From 2fc2c63a438b1511cb48cdbda4d7b116ba2d1c57 Mon Sep 17 00:00:00 2001 From: Mario Pesch Date: Mon, 31 Mar 2025 16:36:13 +0000 Subject: [PATCH 001/517] add-sensebox-eye-esp32s3 --- .../sensebox_mcu_esp32s2/mpconfigboard.h | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.h b/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.h index 9213a260ab86e..565e99d1c3973 100644 --- a/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.h +++ b/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.h @@ -7,26 +7,3 @@ #pragma once // Micropython setup - -#define MICROPY_HW_BOARD_NAME "senseBox MCU-S2 ESP32S2" -#define MICROPY_HW_MCU_NAME "ESP32S2" - -#define MICROPY_HW_NEOPIXEL (&pin_GPIO1) - -#define CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP 1 - -#define CIRCUITPY_BOARD_I2C (2) -#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO40, .sda = &pin_GPIO39}, \ - {.scl = &pin_GPIO42, .sda = &pin_GPIO45}} -#define DEFAULT_SPI_BUS_SCK (&pin_GPIO36) -#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO35) -#define DEFAULT_SPI_BUS_MISO (&pin_GPIO37) - -#define DEFAULT_UART_BUS_RX (&pin_GPIO44) -#define DEFAULT_UART_BUS_TX (&pin_GPIO43) - -#define CIRCUITPY_BOARD_SPI (1) -#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO36, .mosi = &pin_GPIO35, .miso = &pin_GPIO37}} - -#define CIRCUITPY_BOARD_UART (1) -#define CIRCUITPY_BOARD_UART_PIN {{.tx = &pin_GPIO43, .rx = &pin_GPIO44}} From 796ff375d5bfbe097c881c00425c8642b836257f Mon Sep 17 00:00:00 2001 From: Mario Pesch Date: Mon, 31 Mar 2025 16:39:22 +0000 Subject: [PATCH 002/517] add-sensebox-eye-esp32s3 --- .../boards/sensebox_eye_esp32s3/board.c | 9 +++ .../sensebox_eye_esp32s3/mpconfigboard.h | 18 +++++ .../sensebox_eye_esp32s3/mpconfigboard.mk | 21 ++++++ .../boards/sensebox_eye_esp32s3/pins.c | 74 +++++++++++++++++++ .../boards/sensebox_eye_esp32s3/sdkconfig | 19 +++++ 5 files changed, 141 insertions(+) create mode 100644 ports/espressif/boards/sensebox_eye_esp32s3/board.c create mode 100644 ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h create mode 100644 ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk create mode 100644 ports/espressif/boards/sensebox_eye_esp32s3/pins.c create mode 100644 ports/espressif/boards/sensebox_eye_esp32s3/sdkconfig diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/board.c b/ports/espressif/boards/sensebox_eye_esp32s3/board.c new file mode 100644 index 0000000000000..fea7fe2c3002e --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "mpconfigboard.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h new file mode 100644 index 0000000000000..6f340abf66f9c --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "senseBox-eye ESP32S3" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO14) + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO1) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO2) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk new file mode 100644 index 0000000000000..8bfcda99a153b --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk @@ -0,0 +1,21 @@ +USB_VID = 0x303A +USB_PID = 0x82D2 + +USB_PRODUCT = "senseBox-eye ESP32S3" +USB_MANUFACTURER = "Espressif" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_SIZE = 8MB +CIRCUITPY_ESP_FLASH_FREQ = 80m + +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +CIRCUITPY_ESPCAMERA = 1 +CIRCUITPY_AUDIOBUSIO = 1 + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/pins.c b/ports/espressif/boards/sensebox_eye_esp32s3/pins.c new file mode 100644 index 0000000000000..1788f8f1152ea --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/pins.c @@ -0,0 +1,74 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(sscb_i2c, i2c, 2) + +static const mp_rom_obj_tuple_t camera_data_tuple = { + // The order matters. + // They must be ordered from low to high (CAM_D0, CAM_D1...CAM_D7). + + // Do not include any of the control pins in here. + {&mp_type_tuple}, + 8, + { + MP_ROM_PTR(&pin_GPIO11), + MP_ROM_PTR(&pin_GPIO9), + MP_ROM_PTR(&pin_GPIO8), + MP_ROM_PTR(&pin_GPIO10), + MP_ROM_PTR(&pin_GPIO12), + MP_ROM_PTR(&pin_GPIO18), + MP_ROM_PTR(&pin_GPIO17), + MP_ROM_PTR(&pin_GPIO16), + } +}; + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + + { MP_ROM_QSTR(MP_QSTR_CAM_DATA), MP_ROM_PTR(&camera_data_tuple) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D0), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D1), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D2), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D3), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D4), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D5), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D6), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D7), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_CAM_XCLK), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_CAM_HREF), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_CAM_PCLK), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_CAM_VSYNC), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_CAM_SCL), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO4) }, + + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCLK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_SD_POWER), MP_ROM_PTR(&pin_GPIO48) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/sdkconfig b/ports/espressif/boards/sensebox_eye_esp32s3/sdkconfig new file mode 100644 index 0000000000000..919f4d9f345ff --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/sdkconfig @@ -0,0 +1,19 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP +# Camera configuration +# +CONFIG_OV2640_SUPPORT=y +# CONFIG_OV7725_SUPPORT is not set +# CONFIG_OV3660_SUPPORT is not set +# end of Camera configuration +# end of Component config + +# end of Espressif IoT Development Framework Configuration From 4a66244810fb625acec56c5f7aa21354aa95af6f Mon Sep 17 00:00:00 2001 From: Mario Pesch Date: Mon, 31 Mar 2025 16:42:58 +0000 Subject: [PATCH 003/517] Revert "add-sensebox-eye-esp32s3" This reverts commit 2fc2c63a438b1511cb48cdbda4d7b116ba2d1c57. --- .../sensebox_mcu_esp32s2/mpconfigboard.h | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.h b/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.h index 565e99d1c3973..9213a260ab86e 100644 --- a/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.h +++ b/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.h @@ -7,3 +7,26 @@ #pragma once // Micropython setup + +#define MICROPY_HW_BOARD_NAME "senseBox MCU-S2 ESP32S2" +#define MICROPY_HW_MCU_NAME "ESP32S2" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO1) + +#define CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP 1 + +#define CIRCUITPY_BOARD_I2C (2) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO40, .sda = &pin_GPIO39}, \ + {.scl = &pin_GPIO42, .sda = &pin_GPIO45}} +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO36) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO35) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO37) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO36, .mosi = &pin_GPIO35, .miso = &pin_GPIO37}} + +#define CIRCUITPY_BOARD_UART (1) +#define CIRCUITPY_BOARD_UART_PIN {{.tx = &pin_GPIO43, .rx = &pin_GPIO44}} From 8348b8b3b29733fe88c6114fc0c5a47c7df18ac9 Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Mon, 7 Apr 2025 14:52:22 -0700 Subject: [PATCH 004/517] Added board definitions --- .../board.c | 9 ++++ .../mpconfigboard.h | 27 ++++++++++ .../mpconfigboard.mk | 31 +++++++++++ .../pins.c | 53 +++++++++++++++++++ 4 files changed, 120 insertions(+) create mode 100644 ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/board.c create mode 100644 ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h create mode 100644 ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk create mode 100644 ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/board.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/board.c new file mode 100644 index 0000000000000..b44a1ae51e04b --- /dev/null +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h new file mode 100644 index 0000000000000..0188cd9b519ae --- /dev/null +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h @@ -0,0 +1,27 @@ + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Microchip Curiosity CircuitPython Nano" +#define MICROPY_HW_MCU_NAME "same51j20" +#define CIRCUITPY_MCU_FAMILY samd51 + +#define MICROPY_HW_LED_STATUS (&pin_PB22) +#define MICROPY_HW_NEOPIXEL (&pin_PB22) + +#define EXTERNAL_FLASH_QSPI_DUAL + +#define BOARD_HAS_CRYSTAL 1 + +// USB is always used internally so skip the pin objects for it. +#define IGNORE_PIN_PA24 1 +#define IGNORE_PIN_PA25 1 + +#define DEFAULT_I2C_BUS_SCL (&pin_PB31) +#define DEFAULT_I2C_BUS_SDA (&pin_PB30) + +#define DEFAULT_SPI_BUS_SCK (&pin_PBA17) +#define DEFAULT_SPI_BUS_MOSI (&pin_PA16) +#define DEFAULT_SPI_BUS_MISO (&pin_PA18) + +#define DEFAULT_UART_BUS_RX (&pin_PA23) +#define DEFAULT_UART_BUS_TX (&pin_PA22) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk new file mode 100644 index 0000000000000..fcda6bf4b77a7 --- /dev/null +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk @@ -0,0 +1,31 @@ +USB_VID = 0x04D8 +USB_PID = 0xE52B +USB_PRODUCT = "Microchip Curiosity CircuitPython Nano" +USB_MANUFACTURER = "Microchip Technology Inc" + +CHIP_VARIANT = SAME51J20A +CHIP_FAMILY = same51 + +QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICES = "GD25Q16C,W25Q16JVxQ,SST26VF032B" +LONGINT_IMPL = MPZ + +CIRCUITPY__EVE = 1 +CIRCUITPY_BITMAPFILTER = 0 +CIRCUITPY_CANIO = 1 +CIRCUITPY_FLOPPYIO = 0 +CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_GIFIO = 0 +CIRCUITPY_JPEGIO = 0 + +CIRCUITPY_LTO_PARTITION = one + +# We don't have room for the fonts for terminalio for certain languages, +# so turn off terminalio, and if it's off and displayio is on, +# force a clean build. +# Note that we cannot test $(CIRCUITPY_DISPLAYIO) directly with an +# ifeq, because it's not set yet. +ifneq (,$(filter $(TRANSLATION),ja ko ru)) +CIRCUITPY_TERMINALIO = 0 +RELEASE_NEEDS_CLEAN_BUILD = $(CIRCUITPY_DISPLAYIO) +endif diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c new file mode 100644 index 0000000000000..c6a9dd9c416af --- /dev/null +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c @@ -0,0 +1,53 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_PA15) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_PA20) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_PA21) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_PA27) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_PB14) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_PB15) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_PB16) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB22) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIX), MP_ROM_PTR(&pin_PB22) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, + { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_PTR(&pin_PA03) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PB04) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PB05) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_PB06) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_PB07) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_PB08) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_PB09) }, + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&pin_PA02) }, + { MP_ROM_QSTR(MP_QSTR_CAP1), MP_ROM_PTR(&pin_PB09) }, + { MP_ROM_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, + { MP_ROM_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, + { MP_ROM_QSTR(MP_QSTR_CAN_STDBY), MP_ROM_PTR(&pin_PB17) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_PA07) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB30) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB31) }, + { MP_ROM_QSTR(MP_QSTR_BLE_TX), MP_ROM_PTR(&pin_PA12) }, + { MP_ROM_QSTR(MP_QSTR_BLE_RX), MP_ROM_PTR(&pin_PA13) }, + { MP_ROM_QSTR(MP_QSTR_BLE_CLR), MP_ROM_PTR(&pin_PA14) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_PA16) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_PA18) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_PA17) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_0), MP_ROM_PTR(&pin_PB00) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_1), MP_ROM_PTR(&pin_PB01) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_2), MP_ROM_PTR(&pin_PB02) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_3), MP_ROM_PTR(&pin_PB03) }, + { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, + { MP_ROM_QSTR(MP_QSTR_DEBUG_RX), MP_ROM_PTR(&pin_PA23) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 0f9462f35cbbad14d602a9fcae7ead20e8db51c2 Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Wed, 9 Apr 2025 11:11:34 -0700 Subject: [PATCH 005/517] Updated mpconfig.mk to use SST26VF016B. --- .../microchip_curiosity_circuitpython_nano/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk index fcda6bf4b77a7..666cec8ede16b 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk @@ -7,7 +7,7 @@ CHIP_VARIANT = SAME51J20A CHIP_FAMILY = same51 QSPI_FLASH_FILESYSTEM = 1 -EXTERNAL_FLASH_DEVICES = "GD25Q16C,W25Q16JVxQ,SST26VF032B" +EXTERNAL_FLASH_DEVICES = "SST26VF016B" LONGINT_IMPL = MPZ CIRCUITPY__EVE = 1 From b2a11f0d851512508de9adf0c9e2f281c8404525 Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:34:14 -0700 Subject: [PATCH 006/517] Updated pins.c to contain objects for I2C, SPI, UART, CAN --- .../pins.c | 96 +++++++++++-------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c index c6a9dd9c416af..aae64a8a2f86a 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c @@ -9,45 +9,61 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_PA15) }, - { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_PA20) }, - { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_PA21) }, - { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_PA27) }, - { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_PB14) }, - { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_PB15) }, - { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_PB16) }, - { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, - { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB22) }, - { MP_ROM_QSTR(MP_QSTR_NEOPIX), MP_ROM_PTR(&pin_PB22) }, - { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, - { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_PTR(&pin_PA03) }, - { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PB04) }, - { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PB05) }, - { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_PB06) }, - { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_PB07) }, - { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_PB08) }, - { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_PB09) }, - { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&pin_PA02) }, - { MP_ROM_QSTR(MP_QSTR_CAP1), MP_ROM_PTR(&pin_PB09) }, - { MP_ROM_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, - { MP_ROM_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, - { MP_ROM_QSTR(MP_QSTR_CAN_STDBY), MP_ROM_PTR(&pin_PB17) }, - { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_PA07) }, - { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, - { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, - { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB30) }, - { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB31) }, - { MP_ROM_QSTR(MP_QSTR_BLE_TX), MP_ROM_PTR(&pin_PA12) }, - { MP_ROM_QSTR(MP_QSTR_BLE_RX), MP_ROM_PTR(&pin_PA13) }, - { MP_ROM_QSTR(MP_QSTR_BLE_CLR), MP_ROM_PTR(&pin_PA14) }, - { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_PA16) }, - { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_PA18) }, - { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_PA17) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_0), MP_ROM_PTR(&pin_PB00) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_1), MP_ROM_PTR(&pin_PB01) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_2), MP_ROM_PTR(&pin_PB02) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_3), MP_ROM_PTR(&pin_PB03) }, - { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, - { MP_ROM_QSTR(MP_QSTR_DEBUG_RX), MP_ROM_PTR(&pin_PA23) }, + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_PA15) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_PA20) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_PA21) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_PA27) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_PB14) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_PB15) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_PB16) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB22) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIX), MP_ROM_PTR(&pin_PB22) }, + + { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_PTR(&pin_PA03) }, + + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PB04) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PB05) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_PB06) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_PB07) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_PB08) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_PB09) }, + + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&pin_PA02) }, + + { MP_ROM_QSTR(MP_QSTR_CAP1), MP_ROM_PTR(&pin_PB09) }, + + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_PA07) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB30) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB31) }, + + { MP_ROM_QSTR(MP_QSTR_BLE_TX), MP_ROM_PTR(&pin_PA12) }, + { MP_ROM_QSTR(MP_QSTR_BLE_RX), MP_ROM_PTR(&pin_PA13) }, + { MP_ROM_QSTR(MP_QSTR_BLE_CLR), MP_ROM_PTR(&pin_PA14) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_PA16) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_PA18) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_PA17) }, + + { MP_ROM_QSTR(MP_QSTR_SPARE_0), MP_ROM_PTR(&pin_PB00) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_1), MP_ROM_PTR(&pin_PB01) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_2), MP_ROM_PTR(&pin_PB02) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_3), MP_ROM_PTR(&pin_PB03) }, + + { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, + { MP_ROM_QSTR(MP_QSTR_DEBUG_RX), MP_ROM_PTR(&pin_PA23) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 15b1d5f276fd30f8bcd5b3725dc53799c36e8caf Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Wed, 23 Apr 2025 11:38:00 -0700 Subject: [PATCH 007/517] Changes to mpconfigboard.h and pins.c --- mpconfigboard.h | 37 ++++++++++++++++++++++++++ pins.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 mpconfigboard.h create mode 100644 pins.c diff --git a/mpconfigboard.h b/mpconfigboard.h new file mode 100644 index 0000000000000..673a584585148 --- /dev/null +++ b/mpconfigboard.h @@ -0,0 +1,37 @@ + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Microchip Curiosity CircuitPython Nano" +#define MICROPY_HW_MCU_NAME "same51j20" +#define CIRCUITPY_MCU_FAMILY samd51 + +#define MICROPY_HW_LED_STATUS (&pin_PB23) +#define MICROPY_HW_NEOPIXEL (&pin_PB22) + +#define EXTERNAL_FLASH_QSPI_DUAL + +#define BOARD_HAS_CRYSTAL 1 + +// USB is always used internally so skip the pin objects for it. +#define IGNORE_PIN_PA24 1 +#define IGNORE_PIN_PA25 1 + +#define DEFAULT_I2C_BUS_SCL (&pin_PB31) +#define DEFAULT_I2C_BUS_SDA (&pin_PB30) + +#define LCD_SPI_BUS_SCK (&pin_PA05) +#define LCD_SPI_BUS_MOSI (&pin_PA04) +#define LCD_SPI_BUS_CS (&pin_PA07) +#define LCD_BACKLIGHT (&pin_PA06) + +#define SDCARD_SPI_BUS_SCK (&pin_PA17) +#define SDCARD_SPI_BUS_MOSI (&pin_PA16) +#define SDCARD_SPI_BUS_MISO (&pin_PA18) +#define SDCARD_SPI_BUS_CS (&pin_PA19) + +#define DEFAULT_CAN_BUS_TX (&pin_PB12) +#define DEFAULT_CAN_BUS_RX (&pin_PB13) +#define DEFAULT_CAN_BUS_STDBY (&pin_PB17) + +#define DEFAULT_UART_BUS_RX (&pin_PA23) +#define DEFAULT_UART_BUS_TX (&pin_PA22) diff --git a/pins.c b/pins.c new file mode 100644 index 0000000000000..73cd7c64f18df --- /dev/null +++ b/pins.c @@ -0,0 +1,71 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_PA15) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_PA20) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_PA21) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_PA27) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_PB14) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_PB15) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_PB16) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB22) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_PB23) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIX), MP_ROM_PTR(&pin_PB22) }, + + { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_PTR(&pin_PA03) }, + + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PB04) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PB05) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_PB06) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_PB07) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_PB08) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_PB09) }, + + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&pin_PA02) }, + + { MP_ROM_QSTR(MP_QSTR_CAP1), MP_ROM_PTR(&pin_PB09) }, + + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_PA07) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB30) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB31) }, + + { MP_ROM_QSTR(MP_QSTR_BLE_TX), MP_ROM_PTR(&pin_PA12) }, + { MP_ROM_QSTR(MP_QSTR_BLE_RX), MP_ROM_PTR(&pin_PA13) }, + { MP_ROM_QSTR(MP_QSTR_BLE_CLR), MP_ROM_PTR(&pin_PA14) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_PA16) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_PA18) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_PA17) }, + + { MP_ROM_QSTR(MP_QSTR_SPARE_0), MP_ROM_PTR(&pin_PB00) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_1), MP_ROM_PTR(&pin_PB01) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_2), MP_ROM_PTR(&pin_PB02) }, + { MP_ROM_QSTR(MP_QSTR_SPARE_3), MP_ROM_PTR(&pin_PB03) }, + + { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, + { MP_ROM_QSTR(MP_QSTR_DEBUG_RX), MP_ROM_PTR(&pin_PA23) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 4b72af42624dc50b56ecd836015483940ef3f9b1 Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Wed, 23 Apr 2025 12:17:43 -0700 Subject: [PATCH 008/517] Delete pins.c --- pins.c | 71 ---------------------------------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 pins.c diff --git a/pins.c b/pins.c deleted file mode 100644 index 73cd7c64f18df..0000000000000 --- a/pins.c +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "shared-bindings/board/__init__.h" - -static const mp_rom_map_elem_t board_module_globals_table[] = { - CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - - { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_PA15) }, - { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_PA20) }, - { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_PA21) }, - { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_PA27) }, - { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_PB14) }, - { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_PB15) }, - { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_PB16) }, - { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, - { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB22) }, - { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, - - { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_PB23) }, - - { MP_ROM_QSTR(MP_QSTR_NEOPIX), MP_ROM_PTR(&pin_PB22) }, - - { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_PTR(&pin_PA03) }, - - { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PB04) }, - { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PB05) }, - { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_PB06) }, - { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_PB07) }, - { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_PB08) }, - { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_PB09) }, - - { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&pin_PA02) }, - - { MP_ROM_QSTR(MP_QSTR_CAP1), MP_ROM_PTR(&pin_PB09) }, - - { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_PA07) }, - { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, - { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, - - { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB30) }, - { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB31) }, - - { MP_ROM_QSTR(MP_QSTR_BLE_TX), MP_ROM_PTR(&pin_PA12) }, - { MP_ROM_QSTR(MP_QSTR_BLE_RX), MP_ROM_PTR(&pin_PA13) }, - { MP_ROM_QSTR(MP_QSTR_BLE_CLR), MP_ROM_PTR(&pin_PA14) }, - - { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_PA16) }, - { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_PA18) }, - { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_PA17) }, - - { MP_ROM_QSTR(MP_QSTR_SPARE_0), MP_ROM_PTR(&pin_PB00) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_1), MP_ROM_PTR(&pin_PB01) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_2), MP_ROM_PTR(&pin_PB02) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_3), MP_ROM_PTR(&pin_PB03) }, - - { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, - { MP_ROM_QSTR(MP_QSTR_DEBUG_RX), MP_ROM_PTR(&pin_PA23) }, - - { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, - - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, - { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, - { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, -}; -MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From c4285e8472f44b7f1bff796b65efd9200a2522db Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Wed, 23 Apr 2025 12:17:59 -0700 Subject: [PATCH 009/517] Delete mpconfigboard.h --- mpconfigboard.h | 37 ------------------------------------- 1 file changed, 37 deletions(-) delete mode 100644 mpconfigboard.h diff --git a/mpconfigboard.h b/mpconfigboard.h deleted file mode 100644 index 673a584585148..0000000000000 --- a/mpconfigboard.h +++ /dev/null @@ -1,37 +0,0 @@ - -#pragma once - -#define MICROPY_HW_BOARD_NAME "Microchip Curiosity CircuitPython Nano" -#define MICROPY_HW_MCU_NAME "same51j20" -#define CIRCUITPY_MCU_FAMILY samd51 - -#define MICROPY_HW_LED_STATUS (&pin_PB23) -#define MICROPY_HW_NEOPIXEL (&pin_PB22) - -#define EXTERNAL_FLASH_QSPI_DUAL - -#define BOARD_HAS_CRYSTAL 1 - -// USB is always used internally so skip the pin objects for it. -#define IGNORE_PIN_PA24 1 -#define IGNORE_PIN_PA25 1 - -#define DEFAULT_I2C_BUS_SCL (&pin_PB31) -#define DEFAULT_I2C_BUS_SDA (&pin_PB30) - -#define LCD_SPI_BUS_SCK (&pin_PA05) -#define LCD_SPI_BUS_MOSI (&pin_PA04) -#define LCD_SPI_BUS_CS (&pin_PA07) -#define LCD_BACKLIGHT (&pin_PA06) - -#define SDCARD_SPI_BUS_SCK (&pin_PA17) -#define SDCARD_SPI_BUS_MOSI (&pin_PA16) -#define SDCARD_SPI_BUS_MISO (&pin_PA18) -#define SDCARD_SPI_BUS_CS (&pin_PA19) - -#define DEFAULT_CAN_BUS_TX (&pin_PB12) -#define DEFAULT_CAN_BUS_RX (&pin_PB13) -#define DEFAULT_CAN_BUS_STDBY (&pin_PB17) - -#define DEFAULT_UART_BUS_RX (&pin_PA23) -#define DEFAULT_UART_BUS_TX (&pin_PA22) From 6742579e46d8ef7061baf31a41536f6df5a57a3e Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Wed, 23 Apr 2025 12:19:21 -0700 Subject: [PATCH 010/517] Updated mpconfig.h and pins.c --- .../mpconfigboard.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h index 0188cd9b519ae..673a584585148 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h @@ -5,7 +5,7 @@ #define MICROPY_HW_MCU_NAME "same51j20" #define CIRCUITPY_MCU_FAMILY samd51 -#define MICROPY_HW_LED_STATUS (&pin_PB22) +#define MICROPY_HW_LED_STATUS (&pin_PB23) #define MICROPY_HW_NEOPIXEL (&pin_PB22) #define EXTERNAL_FLASH_QSPI_DUAL @@ -19,9 +19,19 @@ #define DEFAULT_I2C_BUS_SCL (&pin_PB31) #define DEFAULT_I2C_BUS_SDA (&pin_PB30) -#define DEFAULT_SPI_BUS_SCK (&pin_PBA17) -#define DEFAULT_SPI_BUS_MOSI (&pin_PA16) -#define DEFAULT_SPI_BUS_MISO (&pin_PA18) +#define LCD_SPI_BUS_SCK (&pin_PA05) +#define LCD_SPI_BUS_MOSI (&pin_PA04) +#define LCD_SPI_BUS_CS (&pin_PA07) +#define LCD_BACKLIGHT (&pin_PA06) + +#define SDCARD_SPI_BUS_SCK (&pin_PA17) +#define SDCARD_SPI_BUS_MOSI (&pin_PA16) +#define SDCARD_SPI_BUS_MISO (&pin_PA18) +#define SDCARD_SPI_BUS_CS (&pin_PA19) + +#define DEFAULT_CAN_BUS_TX (&pin_PB12) +#define DEFAULT_CAN_BUS_RX (&pin_PB13) +#define DEFAULT_CAN_BUS_STDBY (&pin_PB17) #define DEFAULT_UART_BUS_RX (&pin_PA23) #define DEFAULT_UART_BUS_TX (&pin_PA22) From 0a5ce8d453d480d7fcfcc9800616a1bb38df9785 Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Wed, 23 Apr 2025 12:22:27 -0700 Subject: [PATCH 011/517] Switched to SST26VF064B flash --- .../microchip_curiosity_circuitpython_nano/mpconfigboard.mk | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk index 666cec8ede16b..c6fbb75fe802b 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk @@ -6,9 +6,11 @@ USB_MANUFACTURER = "Microchip Technology Inc" CHIP_VARIANT = SAME51J20A CHIP_FAMILY = same51 -QSPI_FLASH_FILESYSTEM = 1 -EXTERNAL_FLASH_DEVICES = "SST26VF016B" +# QSPI_FLASH_FILESYSTEM = 1 +INTERNAL_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICES = "SST26VF064B" LONGINT_IMPL = MPZ +CIRCUITPY_ULAB = 0 CIRCUITPY__EVE = 1 CIRCUITPY_BITMAPFILTER = 0 From 92aa962bdf53cdfe1737c8730ce3283d99074f7a Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Wed, 23 Apr 2025 13:19:36 -0700 Subject: [PATCH 012/517] Added support for flash chips Microchip Quad SPI flash: SST26VF016B, SST26VF016B,SST26VF016B, SST26VF064B --- .../microchip_curiosity_circuitpython_nano/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk index c6fbb75fe802b..1a39d6ac11a4e 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk @@ -8,7 +8,7 @@ CHIP_FAMILY = same51 # QSPI_FLASH_FILESYSTEM = 1 INTERNAL_FLASH_FILESYSTEM = 1 -EXTERNAL_FLASH_DEVICES = "SST26VF064B" +EXTERNAL_FLASH_DEVICES = "SST26VF016B,SST26VF032B,SST26VF064B" LONGINT_IMPL = MPZ CIRCUITPY_ULAB = 0 From 6558a85eb9fe8b64f9c7d5e53db9dad06ed312a9 Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:08:13 -0700 Subject: [PATCH 013/517] Removed dual QSPI for flash chips --- .../microchip_curiosity_circuitpython_nano/mpconfigboard.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h index 673a584585148..71cd8829f665e 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h @@ -8,8 +8,6 @@ #define MICROPY_HW_LED_STATUS (&pin_PB23) #define MICROPY_HW_NEOPIXEL (&pin_PB22) -#define EXTERNAL_FLASH_QSPI_DUAL - #define BOARD_HAS_CRYSTAL 1 // USB is always used internally so skip the pin objects for it. From a9c9aa5c360870e9e4a6373196dd82b3c0a5f295 Mon Sep 17 00:00:00 2001 From: ross-satchell <59891338+ross-satchell@users.noreply.github.com> Date: Mon, 28 Apr 2025 15:38:56 -0700 Subject: [PATCH 014/517] Updated pins.c to add SD card CS pin and obj for lcd sd --- .../boards/microchip_curiosity_circuitpython_nano/pins.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c index aae64a8a2f86a..ef8a44f0874d9 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c @@ -19,6 +19,8 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB22) }, { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_PB23) }, { MP_ROM_QSTR(MP_QSTR_NEOPIX), MP_ROM_PTR(&pin_PB22) }, @@ -49,6 +51,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_PA16) }, { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_PA18) }, { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_PA17) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_PA19) }, { MP_ROM_QSTR(MP_QSTR_SPARE_0), MP_ROM_PTR(&pin_PB00) }, { MP_ROM_QSTR(MP_QSTR_SPARE_1), MP_ROM_PTR(&pin_PB01) }, @@ -62,8 +65,14 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, + { MP_ROM_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, + { MP_ROM_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, + { MP_ROM_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + { MP_ROM_QSTR(MP_QSTR_SDSPI), MP_ROM_PTR(&board_sdspi_obj) }, + { MP_ROM_QSTR(MP_QSTR_LCDSPI), MP_ROM_PTR(&board_lcdspi_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From b0861ec4427135dba8131a7cd5f30f437722fbcb Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Mon, 28 Apr 2025 19:22:32 -0500 Subject: [PATCH 015/517] Initial board declarations for Thumby --- .../boards/tinycircuits_thumby/board.c | 105 ++++++++++++++++++ .../tinycircuits_thumby/mpconfigboard.h | 20 ++++ .../tinycircuits_thumby/mpconfigboard.mk | 18 +++ .../pico-sdk-configboard.h | 12 ++ .../boards/tinycircuits_thumby/pins.c | 48 ++++++++ 5 files changed, 203 insertions(+) create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby/board.c create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby/pico-sdk-configboard.h create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby/pins.c diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/board.c b/ports/raspberrypi/boards/tinycircuits_thumby/board.c new file mode 100644 index 0000000000000..16292dc9e382a --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/board.c @@ -0,0 +1,105 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + + +uint8_t display_init_sequence[] = { + /* + 0, 0xae, // display off + 0xd5, 0, 0x80, // set display clock div + 0, 0xd3, 0, 0x00, // set display offset + 0, 0x40, // set start line + 0, 0xa4, // display all on, resume + 0, 0xa6, // normal display + 0, 0x8d, 0, 0x14, // charge pump + 0, 0x20, 0, 0x00, // memory mode + 0, 0xa0, // segremap + 0, 0xc0, // com scan increment + 0, 0x81, 0, 0xff, // set contrast + 0, 0xd9, 0, 0xf1, // set precharge + 0, 0xd8, 0, 0x20, // set v com detect + 0, 0xa8, 0, 40-1, // set multiplex + 0, 0xda, 0, 0x12, // set com pins + 0, 0xad, 0, 0x30, + 0, 0xaf, // on + */ + + 0xae, 0, // sleep + 0xd5, 1, 0x80, // fOsc divide by 2 + 0xd3, 1, 0x00, // set display offset + 0x40, 1, 0x00, // set start line + 0xa4, 0, // display all on, resume + 0xa6, 0, // normal display + 0x8d, 1, 0x14, // charge pump + 0x20, 1, 0x00, // memory mode + 0xa0, 0, // segremap + 0xc0, 0, // com scan increment + 0x81, 1, 0xff, // set contrast + 0xd9, 1, 0xf1, // set precharge + 0xd8, 1, 0x20, // set v com detect + 0xa8, 1, 40-1, // set multiplex + 0xda, 1, 0x12, // set com pins + 0xad, 1, 0x30, + 0xaf, 0, // on +}; + +void board_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + common_hal_fourwire_fourwire_construct(bus, + spi, + CIRCUITPY_BOARD_OLED_DC, // Command or data + CIRCUITPY_BOARD_OLED_CS, // Chip select + CIRCUITPY_BOARD_OLED_RESET, // Reset + 10000000, // Baudrate + 0, // Polarity + 0); // Phase + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 72, // Width (after rotation) + 40, // Height (after rotation) + 28, // column start + 0, // row start + 0, // rotation + 1, // Color depth + true, // grayscale + false, // pixels in byte share row. only used for depth < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + 0, // Set column command + 0, // Set row command + 0, // Write memory command + display_init_sequence, + sizeof(display_init_sequence), + NULL, // backlight pin + 0x81, + 1.0f, // brightness + true, // single_byte_bounds + true, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + true, // SH1107_addressing + 50000); // backlight pwm frequency +} + +void reset_board(void) { +} diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h new file mode 100644 index 0000000000000..06e46139fbc8e --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h @@ -0,0 +1,20 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "TinyCircuits Thumby" +#define MICROPY_HW_MCU_NAME "rp2040" + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19) + +#define CIRCUITPY_BOARD_OLED_DC (&pin_GPIO17) +#define CIRCUITPY_BOARD_OLED_CS (&pin_GPIO16) +#define CIRCUITPY_BOARD_OLED_RESET (&pin_GPIO20) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL }} diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk new file mode 100644 index 0000000000000..44dc0d671001b --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk @@ -0,0 +1,18 @@ +USB_VID = 0x1D6B +USB_PID = 0x0003 +USB_PRODUCT = "Thumby" +USB_MANUFACTURER = "TinyCircuits" + +CHIP_VARIANT = RP2040 +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" + +CIRCUITPY_STAGE = 1 +CIRCUITPY_AUDIOIO = 1 +CIRCUITPY_AUDIOPWMIO = 1 +CIRCUITPY_KEYPAD = 1 + +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_framebuf +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/pico-sdk-configboard.h b/ports/raspberrypi/boards/tinycircuits_thumby/pico-sdk-configboard.h new file mode 100644 index 0000000000000..ce5a7645b4e22 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/pico-sdk-configboard.h @@ -0,0 +1,12 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. + +// Allow extra time for xosc to start. +#define PICO_XOSC_STARTUP_DELAY_MULTIPLIER 64 diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/pins.c b/ports/raspberrypi/boards/tinycircuits_thumby/pins.c new file mode 100644 index 0000000000000..ec04983719e53 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/pins.c @@ -0,0 +1,48 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // Link Cable (ASR00074) + { MP_ROM_QSTR(MP_QSTR_EXT_TX), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_EXT), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_EXT_PU), MP_ROM_PTR(&pin_GPIO1) }, + + // 0.42 inch OLED AST1042 + { MP_ROM_QSTR(MP_QSTR_OLED_CS), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_OLED_DC), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_OLED_RESET), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, + + // Buttons + { MP_ROM_QSTR(MP_QSTR_BUTTON_LEFT), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_UP), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_RIGHT), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_DOWN), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_1), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_2), MP_ROM_PTR(&pin_GPIO27) }, + + // Mono PWM Speaker + { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_GPIO28) }, + + // Hardware revision ID pins + { MP_OBJ_NEW_QSTR(MP_QSTR_ID3), MP_ROM_PTR(&pin_GPIO12) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ID2), MP_ROM_PTR(&pin_GPIO13) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ID1), MP_ROM_PTR(&pin_GPIO14) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ID0), MP_ROM_PTR(&pin_GPIO15) }, + + // Power pins + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_GPIO26) } +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 992d50a455689aa949f4b5fcc38e29d56c6030de Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 28 Apr 2025 20:53:08 -0400 Subject: [PATCH 016/517] Fix up available SPI buses; swap I2C pins --- .../mpconfigboard.h | 22 +++++++------- .../pins.c | 30 ++++++++++++------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h index 71cd8829f665e..31559f318ada7 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h @@ -14,18 +14,16 @@ #define IGNORE_PIN_PA24 1 #define IGNORE_PIN_PA25 1 -#define DEFAULT_I2C_BUS_SCL (&pin_PB31) -#define DEFAULT_I2C_BUS_SDA (&pin_PB30) - -#define LCD_SPI_BUS_SCK (&pin_PA05) -#define LCD_SPI_BUS_MOSI (&pin_PA04) -#define LCD_SPI_BUS_CS (&pin_PA07) -#define LCD_BACKLIGHT (&pin_PA06) - -#define SDCARD_SPI_BUS_SCK (&pin_PA17) -#define SDCARD_SPI_BUS_MOSI (&pin_PA16) -#define SDCARD_SPI_BUS_MISO (&pin_PA18) -#define SDCARD_SPI_BUS_CS (&pin_PA19) +#define DEFAULT_I2C_BUS_SCL (&pin_PB30) +#define DEFAULT_I2C_BUS_SDA (&pin_PB31) + +#define CIRCUITPY_BOARD_SPI (3) +// These correspond to the CIRCUITPY_BOARD_BUS_SINGLETON definitions in pins.c +#define CIRCUITPY_BOARD_SPI_PIN { \ + {.clock = &pin_PB03, .mosi = &pin_PB02, .miso = &pin_PB00}, /*board.SPI()*/ \ + {.clock = &pin_PA05, .mosi = &pin_PA04, .miso = NULL}, /*board.LCD_SPI()*/ \ + {.clock = &pin_PA17, .mosi = &pin_PA16, .miso = &pin_PA18}, /*board.SD_SPI()*/ \ +} #define DEFAULT_CAN_BUS_TX (&pin_PB12) #define DEFAULT_CAN_BUS_RX (&pin_PB13) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c index ef8a44f0874d9..93e96cb2b1120 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c @@ -6,6 +6,12 @@ #include "shared-bindings/board/__init__.h" +// The singleton for board.SPI() is already defined. +// board.LCD_SPI() +CIRCUITPY_BOARD_BUS_SINGLETON(lcd_spi, spi, 1) +// board.SD_SPI() +CIRCUITPY_BOARD_BUS_SINGLETON(sd_spi, spi, 2) + static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS @@ -19,7 +25,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB22) }, { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, - + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_PB23) }, { MP_ROM_QSTR(MP_QSTR_NEOPIX), MP_ROM_PTR(&pin_PB22) }, @@ -41,8 +47,8 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, - { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB30) }, - { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB31) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB30) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB31) }, { MP_ROM_QSTR(MP_QSTR_BLE_TX), MP_ROM_PTR(&pin_PA12) }, { MP_ROM_QSTR(MP_QSTR_BLE_RX), MP_ROM_PTR(&pin_PA13) }, @@ -53,9 +59,16 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_PA17) }, { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_PA19) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_PB00) }, { MP_ROM_QSTR(MP_QSTR_SPARE_0), MP_ROM_PTR(&pin_PB00) }, + + { MP_ROM_QSTR(MP_QSTR_CS), MP_ROM_PTR(&pin_PB01) }, { MP_ROM_QSTR(MP_QSTR_SPARE_1), MP_ROM_PTR(&pin_PB01) }, + + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB02) }, { MP_ROM_QSTR(MP_QSTR_SPARE_2), MP_ROM_PTR(&pin_PB02) }, + + { MP_ROM_QSTR(MP_QSTR_SCK ), MP_ROM_PTR(&pin_PB03) }, { MP_ROM_QSTR(MP_QSTR_SPARE_3), MP_ROM_PTR(&pin_PB03) }, { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, @@ -65,14 +78,11 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, - { MP_ROM_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, - { MP_ROM_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, - { MP_ROM_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, - { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, - { MP_ROM_QSTR(MP_QSTR_SDSPI), MP_ROM_PTR(&board_sdspi_obj) }, - { MP_ROM_QSTR(MP_QSTR_LCDSPI), MP_ROM_PTR(&board_lcdspi_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SPI), MP_ROM_PTR(&board_lcd_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_SD_SPI), MP_ROM_PTR(&board_sd_spi_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From a4af1be162842300e627b7f078ea88a195b86c96 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 28 Apr 2025 20:55:33 -0400 Subject: [PATCH 017/517] fix pre-commit formatting complaint --- .../boards/microchip_curiosity_circuitpython_nano/pins.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c index 93e96cb2b1120..b4184e3cecd30 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c @@ -68,7 +68,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB02) }, { MP_ROM_QSTR(MP_QSTR_SPARE_2), MP_ROM_PTR(&pin_PB02) }, - { MP_ROM_QSTR(MP_QSTR_SCK ), MP_ROM_PTR(&pin_PB03) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_PB03) }, { MP_ROM_QSTR(MP_QSTR_SPARE_3), MP_ROM_PTR(&pin_PB03) }, { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, From 602a9f61d2a584aa1af53c86e3dac4070217ef46 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 29 Apr 2025 09:26:10 -0500 Subject: [PATCH 018/517] Fix SSD1306 display --- .../boards/tinycircuits_thumby/board.c | 62 ++++++------------- .../tinycircuits_thumby/mpconfigboard.h | 17 ++--- .../boards/tinycircuits_thumby/pins.c | 10 +-- 3 files changed, 34 insertions(+), 55 deletions(-) diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/board.c b/ports/raspberrypi/boards/tinycircuits_thumby/board.c index 16292dc9e382a..15a7eeb93a4c6 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby/board.c +++ b/ports/raspberrypi/boards/tinycircuits_thumby/board.c @@ -16,43 +16,19 @@ uint8_t display_init_sequence[] = { - /* - 0, 0xae, // display off - 0xd5, 0, 0x80, // set display clock div - 0, 0xd3, 0, 0x00, // set display offset - 0, 0x40, // set start line - 0, 0xa4, // display all on, resume - 0, 0xa6, // normal display - 0, 0x8d, 0, 0x14, // charge pump - 0, 0x20, 0, 0x00, // memory mode - 0, 0xa0, // segremap - 0, 0xc0, // com scan increment - 0, 0x81, 0, 0xff, // set contrast - 0, 0xd9, 0, 0xf1, // set precharge - 0, 0xd8, 0, 0x20, // set v com detect - 0, 0xa8, 0, 40-1, // set multiplex - 0, 0xda, 0, 0x12, // set com pins - 0, 0xad, 0, 0x30, - 0, 0xaf, // on - */ - - 0xae, 0, // sleep - 0xd5, 1, 0x80, // fOsc divide by 2 - 0xd3, 1, 0x00, // set display offset - 0x40, 1, 0x00, // set start line - 0xa4, 0, // display all on, resume - 0xa6, 0, // normal display - 0x8d, 1, 0x14, // charge pump - 0x20, 1, 0x00, // memory mode - 0xa0, 0, // segremap - 0xc0, 0, // com scan increment - 0x81, 1, 0xff, // set contrast - 0xd9, 1, 0xf1, // set precharge - 0xd8, 1, 0x20, // set v com detect - 0xa8, 1, 40-1, // set multiplex - 0xda, 1, 0x12, // set com pins - 0xad, 1, 0x30, - 0xaf, 0, // on + 0xAE, 0, // DISPLAY_OFF + 0x20, 1, 0x00, // Set memory addressing to horizontal mode. + 0x81, 1, 0xcf, // set contrast control + 0xA1, 0, // Column 127 is segment 0 + 0xA6, 0, // Normal display + 0xc8, 0, // Normal display + 0xA8, 1, 0x3f, // Mux ratio is 1/64 + 0xd5, 1, 0x80, // Set divide ratio + 0xd9, 1, 0xf1, // Set pre-charge period + 0xda, 1, 0x12, // Set com configuration + 0xdb, 1, 0x40, // Set vcom configuration + 0x8d, 1, 0x14, // Enable charge pump + 0xAF, 0, // DISPLAY_ON }; void board_init(void) { @@ -76,7 +52,7 @@ void board_init(void) { 72, // Width (after rotation) 40, // Height (after rotation) 28, // column start - 0, // row start + 28, // row start 0, // rotation 1, // Color depth true, // grayscale @@ -84,9 +60,9 @@ void board_init(void) { 1, // bytes per cell. Only valid for depths < 8 false, // reverse_pixels_in_byte. Only valid for depths < 8 true, // reverse_pixels_in_word - 0, // Set column command - 0, // Set row command - 0, // Write memory command + 0x21, // Set column command + 0x22, // Set row command + 44, // Write memory command display_init_sequence, sizeof(display_init_sequence), NULL, // backlight pin @@ -97,8 +73,8 @@ void board_init(void) { true, // auto_refresh 60, // native_frames_per_second true, // backlight_on_high - true, // SH1107_addressing - 50000); // backlight pwm frequency + false, // SH1107_addressing + 0); // backlight pwm frequency } void reset_board(void) { diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h index 06e46139fbc8e..6423f545fd4df 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h +++ b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h @@ -6,15 +6,18 @@ #pragma once -#define MICROPY_HW_BOARD_NAME "TinyCircuits Thumby" -#define MICROPY_HW_MCU_NAME "rp2040" +#define MICROPY_HW_BOARD_NAME "TinyCircuits Thumby" +#define MICROPY_HW_MCU_NAME "rp2040" -#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18) -#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19) +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19) -#define CIRCUITPY_BOARD_OLED_DC (&pin_GPIO17) -#define CIRCUITPY_BOARD_OLED_CS (&pin_GPIO16) -#define CIRCUITPY_BOARD_OLED_RESET (&pin_GPIO20) +#define CIRCUITPY_BOARD_OLED_DC (&pin_GPIO17) +#define CIRCUITPY_BOARD_OLED_CS (&pin_GPIO16) +#define CIRCUITPY_BOARD_OLED_RESET (&pin_GPIO20) #define CIRCUITPY_BOARD_SPI (1) #define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL }} + +// For entering safe mode +#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO6) diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/pins.c b/ports/raspberrypi/boards/tinycircuits_thumby/pins.c index ec04983719e53..4f3d81e762708 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby/pins.c +++ b/ports/raspberrypi/boards/tinycircuits_thumby/pins.c @@ -16,11 +16,11 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_EXT_PU), MP_ROM_PTR(&pin_GPIO1) }, // 0.42 inch OLED AST1042 - { MP_ROM_QSTR(MP_QSTR_OLED_CS), MP_ROM_PTR(&pin_GPIO16) }, - { MP_ROM_QSTR(MP_QSTR_OLED_DC), MP_ROM_PTR(&pin_GPIO17) }, - { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO18) }, - { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO19) }, - { MP_ROM_QSTR(MP_QSTR_OLED_RESET), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_OLED_CS), MP_ROM_PTR(CIRCUITPY_BOARD_OLED_CS) }, + { MP_ROM_QSTR(MP_QSTR_OLED_DC), MP_ROM_PTR(CIRCUITPY_BOARD_OLED_DC) }, + { MP_ROM_QSTR(MP_QSTR_OLED_RESET), MP_ROM_PTR(CIRCUITPY_BOARD_OLED_RESET) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(DEFAULT_SPI_BUS_SCK) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(DEFAULT_SPI_BUS_MOSI) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, From 67804080a46c45846c76f5ba2409ebac30bde8ba Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Thu, 1 May 2025 09:37:18 -0500 Subject: [PATCH 019/517] Added Thumby Color --- .../tinycircuits_thumby/mpconfigboard.h | 2 +- .../boards/tinycircuits_thumby/pins.c | 2 +- .../boards/tinycircuits_thumby_color/board.c | 108 ++++++++++++++++++ .../tinycircuits_thumby_color/mpconfigboard.h | 27 +++++ .../mpconfigboard.mk | 15 +++ .../pico-sdk-configboard.h | 9 ++ .../boards/tinycircuits_thumby_color/pins.c | 58 ++++++++++ 7 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby_color/board.c create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby_color/pico-sdk-configboard.h create mode 100644 ports/raspberrypi/boards/tinycircuits_thumby_color/pins.c diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h index 6423f545fd4df..91430966d232e 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h +++ b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h @@ -17,7 +17,7 @@ #define CIRCUITPY_BOARD_OLED_RESET (&pin_GPIO20) #define CIRCUITPY_BOARD_SPI (1) -#define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL }} +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL}} // For entering safe mode #define CIRCUITPY_BOOT_BUTTON (&pin_GPIO6) diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/pins.c b/ports/raspberrypi/boards/tinycircuits_thumby/pins.c index 4f3d81e762708..0a087bb8db8a9 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby/pins.c +++ b/ports/raspberrypi/boards/tinycircuits_thumby/pins.c @@ -14,7 +14,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_EXT_TX), MP_ROM_PTR(&pin_GPIO0) }, { MP_ROM_QSTR(MP_QSTR_EXT), MP_ROM_PTR(&pin_GPIO1) }, { MP_ROM_QSTR(MP_QSTR_EXT_PU), MP_ROM_PTR(&pin_GPIO1) }, - + // 0.42 inch OLED AST1042 { MP_ROM_QSTR(MP_QSTR_OLED_CS), MP_ROM_PTR(CIRCUITPY_BOARD_OLED_CS) }, { MP_ROM_QSTR(MP_QSTR_OLED_DC), MP_ROM_PTR(CIRCUITPY_BOARD_OLED_DC) }, diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c b/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c new file mode 100644 index 0000000000000..abd80dafc62c8 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c @@ -0,0 +1,108 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + + +#define DELAY 0x80 + +// display init sequence according to TinyCircuits-Tiny-Game-Engine +uint8_t display_init_sequence[] = { + 0xFE, 0, // inter register enable 1 + 0xEF, 0, // inter register enable 2 + 0xB0, 1, 0xC0, + 0xB1, 1, 0x80, + 0xB2, 1, 0x2F, + 0xB3, 1, 0x03, + 0xB7, 1, 0x01, + 0xB6, 1, 0x19, + 0xAC, 1, 0xC8, // Complement Principle of RGB 5, 6, 5 + 0xAB, 1, 0x0f, // ? + 0x3A, 1, 0x05, // COLMOD: Pixel Format Set + 0xB4, 1, 0x04, // ? + 0xA8, 1, 0x07, // Frame Rate Set + 0xB8, 1, 0x08, // ? + 0xE7, 1, 0x5A, // VREG_CTL + 0xE8, 1, 0x23, // VGH_SET + 0xE9, 1, 0x47, // VGL_SET + 0xEA, 1, 0x99, // VGH_VGL_CLK + 0xC6, 1, 0x30, // ? + 0xC7, 1, 0x1F, // ? + 0xF0, 14, 0x05, 0x1D, 0x51, 0x2F, 0x85, 0x2A, 0x11, 0x62, 0x00, 0x07, 0x07, 0x0F, 0x08, 0x1F, // SET_GAMMA1 + 0xF1, 14, 0x2E, 0x41, 0x62, 0x56, 0xA5, 0x3A, 0x3f, 0x60, 0x0F, 0x07, 0x0A, 0x18, 0x18, 0x1D, // SET_GAMMA2 + 0x11, 0 | DELAY, 120, + 0x29, 0 | DELAY, 10, // display on +}; + +void board_init(void) { + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct( + spi, + DEFAULT_SPI_BUS_SCK, // CLK + DEFAULT_SPI_BUS_MOSI, // MOSI + NULL, // MISO not connected + false // Not half-duplex + ); + + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + CIRCUITPY_BOARD_LCD_DC, // DC + CIRCUITPY_BOARD_LCD_CS, // CS + CIRCUITPY_BOARD_LCD_RESET, // RST + 80000000, // baudrate + 0, // polarity + 0 // phase + ); + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 128, // width (after rotation) + 128, // height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + CIRCUITPY_BOARD_LCD_BACKLIGHT, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + +void reset_board(void) { +} diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h new file mode 100644 index 0000000000000..ce5d015db5867 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h @@ -0,0 +1,27 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "TinyCircuits Thumby Color" +#define MICROPY_HW_MCU_NAME "rp2350" + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO9) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO8) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = DEFAULT_I2C_BUS_SCL, .sda = DEFAULT_I2C_BUS_SDA}} + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19) + +#define CIRCUITPY_BOARD_LCD_DC (&pin_GPIO16) +#define CIRCUITPY_BOARD_LCD_CS (&pin_GPIO17) +#define CIRCUITPY_BOARD_LCD_RESET (&pin_GPIO11) +#define CIRCUITPY_BOARD_LCD_BACKLIGHT (&pin_GPIO20) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL}} diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk new file mode 100644 index 0000000000000..ed1255e2b96ed --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk @@ -0,0 +1,15 @@ +USB_VID = 0x1D6B +USB_PID = 0x0003 +USB_PRODUCT = "Thumby Color" +USB_MANUFACTURER = "TinyCircuits" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" + +CIRCUITPY_STAGE = 1 +CIRCUITPY_AUDIOIO = 1 +CIRCUITPY_AUDIOPWMIO = 1 +CIRCUITPY_KEYPAD = 1 diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/pico-sdk-configboard.h b/ports/raspberrypi/boards/tinycircuits_thumby_color/pico-sdk-configboard.h new file mode 100644 index 0000000000000..110195b779498 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/pins.c b/ports/raspberrypi/boards/tinycircuits_thumby_color/pins.c new file mode 100644 index 0000000000000..d0e63512d7ab6 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/pins.c @@ -0,0 +1,58 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // Buttons + { MP_ROM_QSTR(MP_QSTR_BUTTON_UP), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_LEFT), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_DOWN), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_RIGHT), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_BUMPER_LEFT), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_BUMPER_RIGHT), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_MENU), MP_ROM_PTR(&pin_GPIO26) }, + + // LED + { MP_ROM_QSTR(MP_QSTR_LED_R), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_LED_G), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_PTR(&pin_GPIO12) }, + + // Rumble + { MP_ROM_QSTR(MP_QSTR_RUMBLE), MP_ROM_PTR(&pin_GPIO5) }, + + // Mono PWM Speaker + { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_SPEAKER_ENABLE), MP_ROM_PTR(&pin_GPIO20) }, + + // 0.85 inch TFT GC9107 + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(CIRCUITPY_BOARD_LCD_CS) }, + { MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(CIRCUITPY_BOARD_LCD_DC) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RESET), MP_ROM_PTR(CIRCUITPY_BOARD_LCD_RESET) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BACKLIGHT), MP_ROM_PTR(CIRCUITPY_BOARD_LCD_BACKLIGHT) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(DEFAULT_SPI_BUS_SCK) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(DEFAULT_SPI_BUS_MOSI) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SPI), MP_ROM_PTR(&board_spi_obj) }, + + // RTC I2C + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(DEFAULT_I2C_BUS_SDA) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(DEFAULT_I2C_BUS_SCL) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + // Power pins + { MP_ROM_QSTR(MP_QSTR_CHARGE_STAT), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, + +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 1ab8176e1dcfb8eccead10927b65f914a237bfa2 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 27 May 2025 17:04:03 +0200 Subject: [PATCH 020/517] mpconfig: Use RUN_BACKGROUND_TASKS as the micropython event hook This is a newer abstraction in micropython; it'll for instance allow background tasks to be checked once per ms in the internal select module modselect. Because `MICROPY_INTERNAL_EVENT_HOOK` is used in a few locations, our own CIRCUITPY-CHANGE call to `RUN_BACKGROUND_TASKS` can be nixed. Specifically, `mp_event_handle_nowait` calls `MICROPY_INTERNAL_EVENT_HOOK` so paths from it don't need `RUN_BACKGROUND_TASKS` anymore. --- py/circuitpy_mpconfig.h | 1 + py/scheduler.c | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index d6658867b7d1d..73d6d682e8c5b 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -452,6 +452,7 @@ void background_callback_run_all(void); #define MICROPY_VM_HOOK_LOOP RUN_BACKGROUND_TASKS; #define MICROPY_VM_HOOK_RETURN RUN_BACKGROUND_TASKS; +#define MICROPY_INTERNAL_EVENT_HOOK (RUN_BACKGROUND_TASKS) // CIRCUITPY_AUTORELOAD_DELAY_MS = 0 will completely disable autoreload. #ifndef CIRCUITPY_AUTORELOAD_DELAY_MS diff --git a/py/scheduler.c b/py/scheduler.c index 91ef7e63b38f1..99952d28aca61 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -271,10 +271,6 @@ void mp_event_wait_indefinite(void) { MICROPY_EVENT_POLL_HOOK #else mp_event_handle_nowait(); - - // CIRCUITPY-CHANGE: don't starve CircuitPython background tasks - RUN_BACKGROUND_TASKS; - MICROPY_INTERNAL_WFE(-1); #endif } @@ -287,10 +283,6 @@ void mp_event_wait_ms(mp_uint_t timeout_ms) { MICROPY_EVENT_POLL_HOOK #else mp_event_handle_nowait(); - - // CIRCUITPY-CHANGE: don't starve CircuitPython background tasks - RUN_BACKGROUND_TASKS; - MICROPY_INTERNAL_WFE(timeout_ms); #endif } From 96636cfd85c1c710d868e8b63d056303fe5e7abf Mon Sep 17 00:00:00 2001 From: eightycc Date: Wed, 4 Jun 2025 13:24:04 -0700 Subject: [PATCH 021/517] Fix typo in https address of cortex toolchain. --- .devcontainer/cortex-m-toolchain.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/cortex-m-toolchain.sh b/.devcontainer/cortex-m-toolchain.sh index 782b472fec2e2..38ecdb72a7b96 100755 --- a/.devcontainer/cortex-m-toolchain.sh +++ b/.devcontainer/cortex-m-toolchain.sh @@ -14,7 +14,7 @@ echo -e "[cortex-m-toolchain.sh] downloading and installing gcc-arm-non-eabi too cd /workspaces wget -qO gcc-arm-none-eabi.tar.xz \ - https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi.tar.xz + https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi.tar.xz tar -xJf gcc-arm-none-eabi.tar.xz ln -s arm-gnu-toolchain-14.2.Rel1-x86_64-arm-none-eabi gcc-arm-none-eabi From 313f826e0cebf4bcaa03bf90b21d2bb6d20d4ffb Mon Sep 17 00:00:00 2001 From: eightycc Date: Wed, 4 Jun 2025 13:25:22 -0700 Subject: [PATCH 022/517] atmel-samd: Release SERCOM with I2C deinit. --- ports/atmel-samd/common-hal/busio/I2C.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/atmel-samd/common-hal/busio/I2C.c b/ports/atmel-samd/common-hal/busio/I2C.c index e13b5410ae32a..da108709feaff 100644 --- a/ports/atmel-samd/common-hal/busio/I2C.c +++ b/ports/atmel-samd/common-hal/busio/I2C.c @@ -135,6 +135,7 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { if (common_hal_busio_i2c_deinited(self)) { return; } + allow_reset_sercom(self->i2c_desc.device.hw); i2c_m_sync_disable(&self->i2c_desc); i2c_m_sync_deinit(&self->i2c_desc); From d78a9c7ba0b481c5d10ff6ce70874876560900a3 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 17 Jun 2025 17:24:03 -0400 Subject: [PATCH 023/517] curiosity: switch back to external flash filesystem --- .../microchip_curiosity_circuitpython_nano/mpconfigboard.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk index 1a39d6ac11a4e..408c4eca9fd60 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk @@ -6,8 +6,7 @@ USB_MANUFACTURER = "Microchip Technology Inc" CHIP_VARIANT = SAME51J20A CHIP_FAMILY = same51 -# QSPI_FLASH_FILESYSTEM = 1 -INTERNAL_FLASH_FILESYSTEM = 1 +QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "SST26VF016B,SST26VF032B,SST26VF064B" LONGINT_IMPL = MPZ CIRCUITPY_ULAB = 0 From 05fae5d00f9c3fb949007d20d4ca3a88cc006530 Mon Sep 17 00:00:00 2001 From: Amaar Ebrahim Date: Fri, 11 Jul 2025 18:30:45 -0500 Subject: [PATCH 024/517] raspberrypi/I2CTarget: Fixed bug where I2C starts were seen as restarts. The Rasperry Pi Pico, based on the RP2040, has a register that maintains the status of I2C interrupt flags called IC_INTR_STAT. The bits of this register are set by hardware and cleared by software. Before this commit, the I2CTarget library did not clear the restart bit (R_RESTART_DET) in this register after an I2C transaction ended, causing the is_restart field of the i2ctarget_i2c_target_request_obj_t struct to always be true after the first I2C transaction. This commit causes the restart and stop bits to get cleared when the I2C transaction ends. Signed-off-by: Amaar Ebrahim --- .../raspberrypi/common-hal/i2ctarget/I2CTarget.c | 15 +++++++++++++++ .../raspberrypi/common-hal/i2ctarget/I2CTarget.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c index d1c92eea9988f..655dfd867ca38 100644 --- a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c +++ b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c @@ -85,6 +85,8 @@ void common_hal_i2ctarget_i2c_target_deinit(i2ctarget_i2c_target_obj_t *self) { } int common_hal_i2ctarget_i2c_target_is_addressed(i2ctarget_i2c_target_obj_t *self, uint8_t *address, bool *is_read, bool *is_restart) { + common_hal_i2ctarget_i2c_target_is_stop(self); + if (!((self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) || (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS))) { return 0; } @@ -123,6 +125,19 @@ int common_hal_i2ctarget_i2c_target_write_byte(i2ctarget_i2c_target_obj_t *self, } } +int common_hal_i2ctarget_i2c_target_is_stop(i2ctarget_i2c_target_obj_t *self) { + // Interrupt bits must be cleared by software. Clear the STOP and + // RESTART interrupt bits after an I2C transaction finishes. + if (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) { + self->peripheral->hw->clr_stop_det; + self->peripheral->hw->clr_restart_det; + return 1; + } else { + return 0; + } + +} + void common_hal_i2ctarget_i2c_target_ack(i2ctarget_i2c_target_obj_t *self, bool ack) { return; } diff --git a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h index 5d4e0690cbffd..67b09c18c2f12 100644 --- a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h +++ b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h @@ -21,3 +21,5 @@ typedef struct { uint8_t scl_pin; uint8_t sda_pin; } i2ctarget_i2c_target_obj_t; + +int common_hal_i2ctarget_i2c_target_is_stop(i2ctarget_i2c_target_obj_t *self); From b16d18e5d0fc7eee0e1ae38c2be44ca2d3055511 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 25 Jul 2025 18:27:01 -0400 Subject: [PATCH 025/517] espressif/common-hal/alarm/PinAlarm.c: return correct wakeup pin for ESP32 --- ports/espressif/common-hal/alarm/pin/PinAlarm.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ports/espressif/common-hal/alarm/pin/PinAlarm.c b/ports/espressif/common-hal/alarm/pin/PinAlarm.c index 45f676fa5993f..8e3e46785f2e7 100644 --- a/ports/espressif/common-hal/alarm/pin/PinAlarm.c +++ b/ports/espressif/common-hal/alarm/pin/PinAlarm.c @@ -108,7 +108,17 @@ mp_obj_t alarm_pin_pinalarm_record_wake_alarm(void) { #ifdef SOC_PM_SUPPORT_EXT0_WAKEUP if (cause == ESP_SLEEP_WAKEUP_EXT0) { - pin_number = REG_GET_FIELD(RTC_IO_EXT_WAKEUP0_REG, RTC_IO_EXT_WAKEUP0_SEL); + int rtc_io_pin_number = REG_GET_FIELD(RTC_IO_EXT_WAKEUP0_REG, RTC_IO_EXT_WAKEUP0_SEL); + // Look up the GPIO equivalent pin for this RTC GPIO pin. On ESP32, the numbering + // is different for RTC_GPIO and regular GPIO, and there's no mapping table. + // The RTC and GPIO pin numbers match for all other current chips, so we could skip this + // for those chips, but it's not expensive, and maybe there will be another mismatch in the future. + for (gpio_num_t gpio_num = 0; gpio_num < SOC_GPIO_PIN_COUNT; gpio_num++) { + if (rtc_io_number_get(gpio_num) == rtc_io_pin_number) { + pin_number = gpio_num; + break; + } + } } else { #endif #ifdef SOC_PM_SUPPORT_EXT1_WAKEUP From 2e183f4c00afcd280dd406cf22a72c6cd32d4e67 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 23 Jul 2025 15:50:53 -0700 Subject: [PATCH 026/517] Add support for quad color epaper highlight_color2 is now available for pixel value 0b11. Black is 0b00, white is 0b01 and highlight_color is 0b10. Also add support for multibit values written with one command when color_command isn't provided. --- shared-bindings/epaperdisplay/EPaperDisplay.c | 8 +++- shared-bindings/epaperdisplay/EPaperDisplay.h | 2 +- shared-module/displayio/ColorConverter.c | 41 ++++++++++++++----- shared-module/displayio/ColorConverter.h | 3 +- shared-module/displayio/Palette.h | 3 +- shared-module/displayio/__init__.c | 1 + shared-module/epaperdisplay/EPaperDisplay.c | 17 ++++++-- 7 files changed, 56 insertions(+), 19 deletions(-) diff --git a/shared-bindings/epaperdisplay/EPaperDisplay.c b/shared-bindings/epaperdisplay/EPaperDisplay.c index 5d72df815d0a7..9f261d599ada9 100644 --- a/shared-bindings/epaperdisplay/EPaperDisplay.c +++ b/shared-bindings/epaperdisplay/EPaperDisplay.c @@ -54,6 +54,7 @@ //| write_color_ram_command: Optional[int] = None, //| color_bits_inverted: bool = False, //| highlight_color: int = 0x000000, +//| highlight_color2: int = 0x000000, //| refresh_display_command: Union[int, circuitpython_typing.ReadableBuffer], //| refresh_time: float = 40, //| busy_pin: Optional[microcontroller.Pin] = None, @@ -97,6 +98,7 @@ //| :param int write_color_ram_command: Command used to write pixels values into the update region //| :param bool color_bits_inverted: True if 0 bits are used to show the color. Otherwise, 1 means to show color. //| :param int highlight_color: RGB888 of source color to highlight with third ePaper color. +//| :param int highlight_color2: RGB888 of source color to highlight with fourth ePaper color. //| :param int refresh_display_command: Command used to start a display refresh. Single int or byte-packed command sequence //| :param float refresh_time: Time it takes to refresh the display before the stop_sequence should be sent. Ignored when busy_pin is provided. //| :param microcontroller.Pin busy_pin: Pin used to signify the display is busy @@ -117,7 +119,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command, ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted, - ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, + ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, ARG_highlight_color2, ARG_refresh_display_command, ARG_refresh_time, ARG_busy_pin, ARG_busy_state, ARG_seconds_per_frame, ARG_always_toggle_chip_select, ARG_grayscale, ARG_advanced_color_epaper, ARG_spectra6, ARG_two_byte_sequence_length, ARG_start_up_time, ARG_address_little_endian }; @@ -141,6 +143,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, { MP_QSTR_write_color_ram_command, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, { MP_QSTR_color_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, { MP_QSTR_highlight_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, + { MP_QSTR_highlight_color2, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, { MP_QSTR_refresh_display_command, MP_ARG_OBJ | MP_ARG_REQUIRED }, { MP_QSTR_refresh_time, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(40)} }, { MP_QSTR_busy_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, @@ -181,6 +184,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, mp_int_t write_color_ram_command = NO_COMMAND; mp_int_t highlight_color = args[ARG_highlight_color].u_int; + mp_int_t highlight_color2 = args[ARG_highlight_color2].u_int; if (args[ARG_write_color_ram_command].u_obj != mp_const_none) { write_color_ram_command = mp_obj_get_int(args[ARG_write_color_ram_command].u_obj); } @@ -216,7 +220,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, args[ARG_set_column_window_command].u_int, args[ARG_set_row_window_command].u_int, args[ARG_set_current_column_command].u_int, args[ARG_set_current_row_command].u_int, args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, - args[ARG_color_bits_inverted].u_bool, highlight_color, refresh_buf, refresh_buf_len, refresh_time, + args[ARG_color_bits_inverted].u_bool, highlight_color, highlight_color2, refresh_buf, refresh_buf_len, refresh_time, busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame, args[ARG_always_toggle_chip_select].u_bool, args[ARG_grayscale].u_bool, args[ARG_advanced_color_epaper].u_bool, args[ARG_spectra6].u_bool, two_byte_sequence_length, args[ARG_address_little_endian].u_bool diff --git a/shared-bindings/epaperdisplay/EPaperDisplay.h b/shared-bindings/epaperdisplay/EPaperDisplay.h index 016a07f1490c5..afa90a514e2a5 100644 --- a/shared-bindings/epaperdisplay/EPaperDisplay.h +++ b/shared-bindings/epaperdisplay/EPaperDisplay.h @@ -23,7 +23,7 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla uint16_t set_column_window_command, uint16_t set_row_window_command, uint16_t set_current_column_command, uint16_t set_current_row_command, uint16_t write_black_ram_command, bool black_bits_inverted, - uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, + uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint32_t highlight_color2, const uint8_t *refresh_sequence, uint16_t refresh_sequence_len, mp_float_t refresh_time, const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select, bool grayscale, bool acep, bool spectra6, bool two_byte_sequence_length, diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index ae7d3f02d5127..d4e64ee286eb8 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -163,8 +163,13 @@ uint8_t displayio_colorconverter_compute_sevencolor(uint32_t color_rgb888) { } } -void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_hue, uint32_t *color) { - +void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color) { + if (pixel_chroma <= 16) { + if (!colorspace->grayscale) { + *color = 0; + } + return; + } int16_t hue_diff = colorspace->tricolor_hue - pixel_hue; if ((-10 <= hue_diff && hue_diff <= 10) || hue_diff <= -220 || hue_diff >= 220) { if (colorspace->grayscale) { @@ -177,6 +182,21 @@ void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *co } } +void displayio_colorconverter_compute_fourcolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color) { + *color >>= 1; + if (pixel_chroma <= 16) { + return; + } + int16_t hue_diff = colorspace->tricolor_hue - pixel_hue; + if ((-10 <= hue_diff && hue_diff <= 10) || hue_diff <= -220 || hue_diff >= 220) { + *color = 2; + } + int16_t hue_diff2 = colorspace->fourcolor_hue - pixel_hue; + if ((-10 <= hue_diff2 && hue_diff2 <= 10) || hue_diff2 <= -220 || hue_diff2 >= 220) { + *color = 3; + } +} + void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t *colorspace, uint32_t input_color, uint32_t *output_color) { displayio_input_pixel_t input_pixel; input_pixel.pixel = input_color; @@ -313,18 +333,17 @@ void displayio_convert_color(const _displayio_colorspace_t *colorspace, bool dit output_color->pixel = packed; output_color->opaque = true; return; - } else if (colorspace->tricolor) { + } else if (colorspace->tricolor || colorspace->fourcolor) { uint8_t luma = displayio_colorconverter_compute_luma(pixel); + uint8_t pixel_chroma = displayio_colorconverter_compute_chroma(pixel); output_color->pixel = luma >> (8 - colorspace->depth); - if (displayio_colorconverter_compute_chroma(pixel) <= 16) { - if (!colorspace->grayscale) { - output_color->pixel = 0; - } - output_color->opaque = true; - return; - } uint8_t pixel_hue = displayio_colorconverter_compute_hue(pixel); - displayio_colorconverter_compute_tricolor(colorspace, pixel_hue, &output_color->pixel); + if (colorspace->tricolor) { + displayio_colorconverter_compute_tricolor(colorspace, pixel_chroma, pixel_hue, &output_color->pixel); + } else if (colorspace->fourcolor) { + displayio_colorconverter_compute_fourcolor(colorspace, pixel_chroma, pixel_hue, &output_color->pixel); + } + output_color->opaque = true; return; } else if (colorspace->grayscale && colorspace->depth <= 8) { uint8_t luma = displayio_colorconverter_compute_luma(pixel); diff --git a/shared-module/displayio/ColorConverter.h b/shared-module/displayio/ColorConverter.h index d3dbedfe160ab..304004d566a95 100644 --- a/shared-module/displayio/ColorConverter.h +++ b/shared-module/displayio/ColorConverter.h @@ -43,4 +43,5 @@ uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_hue(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_sixcolor(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_sevencolor(uint32_t color_rgb888); -void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_hue, uint32_t *color); +void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color); +void displayio_colorconverter_compute_fourcolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color); diff --git a/shared-module/displayio/Palette.h b/shared-module/displayio/Palette.h index bb87a93d98133..24ae5c4b7a525 100644 --- a/shared-module/displayio/Palette.h +++ b/shared-module/displayio/Palette.h @@ -15,10 +15,11 @@ typedef struct { uint8_t depth; uint8_t bytes_per_cell; uint8_t tricolor_hue; - uint8_t tricolor_luma; + uint8_t fourcolor_hue; uint8_t grayscale_bit; // The lowest grayscale bit. Normally 8 - depth. bool grayscale; bool tricolor; + bool fourcolor; bool sixcolor; // Spectra6 e-ink screens. bool sevencolor; // Acep e-ink screens. bool pixels_in_byte_share_row; diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 4b467b75589d1..f3444241b3cca 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -198,6 +198,7 @@ void reset_displays(void) { for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { mp_const_obj_t display_bus_type = display_buses[i].bus_base.type; if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { + display_buses[i].bus_base.type = &mp_type_NoneType; continue; #if CIRCUITPY_FOURWIRE } else if (display_bus_type == &fourwire_fourwire_type) { diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index a5a303d82ae75..e1f0a07f467ee 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -33,7 +33,7 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla uint16_t set_column_window_command, uint16_t set_row_window_command, uint16_t set_current_column_command, uint16_t set_current_row_command, uint16_t write_black_ram_command, bool black_bits_inverted, - uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, + uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint32_t highlight_color2, const uint8_t *refresh_sequence, uint16_t refresh_sequence_len, mp_float_t refresh_time, const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool chip_select, bool grayscale, bool acep, bool spectra6, bool two_byte_sequence_length, bool address_little_endian) { @@ -42,10 +42,16 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla if (highlight_color != 0x000000) { self->core.colorspace.tricolor = true; self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color); - self->core.colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color); } else { self->core.colorspace.tricolor = false; } + if (highlight_color != 0x000000 && highlight_color2 != 0x000000) { + self->core.colorspace.tricolor = false; + self->core.colorspace.fourcolor = true; + self->core.colorspace.fourcolor_hue = displayio_colorconverter_compute_hue(highlight_color2); + } else { + self->core.colorspace.fourcolor = false; + } self->acep = acep || spectra6; self->core.colorspace.sixcolor = spectra6; self->core.colorspace.sevencolor = acep; @@ -54,6 +60,11 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla grayscale = false; core_grayscale = false; } + if ((highlight_color != 0x000000 || highlight_color2 != 0x000000) && write_color_ram_command == NO_COMMAND) { + color_depth = 2; + core_grayscale = false; + grayscale = false; + } displayio_display_core_construct(&self->core, width, height, rotation, color_depth, core_grayscale, true, 1, true, true); displayio_display_bus_construct(&self->bus, bus, ram_width, ram_height, @@ -90,7 +101,7 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla } // Clear the color memory if it isn't in use. - if (highlight_color == 0x00 && write_color_ram_command != NO_COMMAND) { + if (highlight_color == 0x00 && highlight_color2 == 0x00 && write_color_ram_command != NO_COMMAND) { // TODO: Clear } From 69534be70910879209ce462ed0cddc6a709ce602 Mon Sep 17 00:00:00 2001 From: Mario Pesch Date: Tue, 29 Jul 2025 13:59:56 +0000 Subject: [PATCH 027/517] update pins --- .../boards/sensebox_eye_esp32s3/mpconfigboard.h | 2 +- .../sensebox_eye_esp32s3/mpconfigboard.mk | 3 --- .../boards/sensebox_eye_esp32s3/pins.c | 17 ++++++++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h index 6f340abf66f9c..3908c492f1b01 100644 --- a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h +++ b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h @@ -9,7 +9,7 @@ #define MICROPY_HW_BOARD_NAME "senseBox-eye ESP32S3" #define MICROPY_HW_MCU_NAME "ESP32S3" -#define MICROPY_HW_NEOPIXEL (&pin_GPIO14) +#define MICROPY_HW_NEOPIXEL (&pin_GPIO45) #define DEFAULT_I2C_BUS_SCL (&pin_GPIO1) #define DEFAULT_I2C_BUS_SDA (&pin_GPIO2) diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk index 8bfcda99a153b..9f43ff6f392d4 100644 --- a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk +++ b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk @@ -16,6 +16,3 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m CIRCUITPY_ESPCAMERA = 1 CIRCUITPY_AUDIOBUSIO = 1 - -# Include these Python libraries in firmware. -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/pins.c b/ports/espressif/boards/sensebox_eye_esp32s3/pins.c index 1788f8f1152ea..3d5b9f76763f7 100644 --- a/ports/espressif/boards/sensebox_eye_esp32s3/pins.c +++ b/ports/espressif/boards/sensebox_eye_esp32s3/pins.c @@ -33,17 +33,23 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO0) }, { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO0) }, + {MP_ROM_QSTR(MP_QSTR_BUTTON_SW), MP_ROM_PTR(&pin_GPIO47) }, { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, - { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_A14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_A48), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_D48), MP_ROM_PTR(&pin_GPIO48) }, - { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO45) }, { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_UART_ENABLE), MP_ROM_PTR(&pin_GPIO26) }, { MP_ROM_QSTR(MP_QSTR_CAM_DATA), MP_ROM_PTR(&camera_data_tuple) }, { MP_ROM_QSTR(MP_QSTR_CAM_D0), MP_ROM_PTR(&pin_GPIO11) }, @@ -60,12 +66,13 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_CAM_VSYNC), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_CAM_SCL), MP_ROM_PTR(&pin_GPIO5) }, { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_PWDN), MP_ROM_PTR(&pin_GPIO46) }, - { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO41) }, { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO38) }, { MP_ROM_QSTR(MP_QSTR_SD_SCLK), MP_ROM_PTR(&pin_GPIO39) }, { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO40) }, - { MP_ROM_QSTR(MP_QSTR_SD_POWER), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_SD_ENABLE), MP_ROM_PTR(&pin_GPIO3) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, From 7ea1ecf681b134f5227f1283262f8f15a8399201 Mon Sep 17 00:00:00 2001 From: Mario Pesch Date: Tue, 29 Jul 2025 14:09:42 +0000 Subject: [PATCH 028/517] include neopixel library --- ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk | 3 +++ ports/espressif/boards/sensebox_eye_esp32s3/pins.c | 1 + 2 files changed, 4 insertions(+) diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk index 9f43ff6f392d4..485c07f524dbc 100644 --- a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk +++ b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk @@ -16,3 +16,6 @@ CIRCUITPY_ESP_PSRAM_FREQ = 80m CIRCUITPY_ESPCAMERA = 1 CIRCUITPY_AUDIOBUSIO = 1 + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel \ No newline at end of file diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/pins.c b/ports/espressif/boards/sensebox_eye_esp32s3/pins.c index 3d5b9f76763f7..c218fafa918ce 100644 --- a/ports/espressif/boards/sensebox_eye_esp32s3/pins.c +++ b/ports/espressif/boards/sensebox_eye_esp32s3/pins.c @@ -46,6 +46,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_D48), MP_ROM_PTR(&pin_GPIO48) }, { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO45) }, { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, From 704f4d3bcbabbe288487c9cce29509a60b4b94ea Mon Sep 17 00:00:00 2001 From: Mario Pesch Date: Tue, 29 Jul 2025 14:34:40 +0000 Subject: [PATCH 029/517] run pre-commit --- ports/espressif/README.rst | 2 +- ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/espressif/README.rst b/ports/espressif/README.rst index 63174fd167067..5630d4668b563 100644 --- a/ports/espressif/README.rst +++ b/ports/espressif/README.rst @@ -222,4 +222,4 @@ And follow the Espressif GDB tutorial `instructions for connecting Date: Tue, 29 Jul 2025 14:37:25 +0000 Subject: [PATCH 030/517] fix end of file --- ports/espressif/README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/espressif/README.rst b/ports/espressif/README.rst index 5630d4668b563..fe5542aafe46f 100644 --- a/ports/espressif/README.rst +++ b/ports/espressif/README.rst @@ -222,4 +222,3 @@ And follow the Espressif GDB tutorial `instructions for connecting Date: Tue, 29 Jul 2025 13:48:19 -0700 Subject: [PATCH 031/517] Fix other boards by adding new param --- ports/atmel-samd/boards/openbook_m4/board.c | 1 + ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c | 2 ++ ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c | 1 + ports/espressif/boards/heltec_vision_master_e290/board.c | 1 + ports/espressif/boards/heltec_wireless_paper/board.c | 1 + ports/espressif/boards/sqfmi_watchy/board.c | 1 + .../raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c | 2 ++ ports/raspberrypi/boards/pimoroni_badger2040/board.c | 1 + ports/raspberrypi/boards/pimoroni_badger2040w/board.c | 1 + ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c | 1 + ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c | 1 + 11 files changed, 13 insertions(+) diff --git a/ports/atmel-samd/boards/openbook_m4/board.c b/ports/atmel-samd/boards/openbook_m4/board.c index 3ad2e162ecf3c..95027e817e8b8 100644 --- a/ports/atmel-samd/boards/openbook_m4/board.c +++ b/ports/atmel-samd/boards/openbook_m4/board.c @@ -76,6 +76,7 @@ void board_init(void) { NO_COMMAND, // write_color_ram_command (can add this for grayscale eventually) false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, // refresh_display_sequence sizeof(refresh_sequence), 40, // refresh_time diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index 6d4fcbf135e14..c4311474f6061 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -239,6 +239,7 @@ void board_init(void) { 0x26, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 ssd1680_display_refresh_sequence, sizeof(ssd1680_display_refresh_sequence), 1.0, // refresh_time &pin_GPIO5, // busy_pin @@ -273,6 +274,7 @@ void board_init(void) { 0x13, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 il0373_display_refresh_sequence, sizeof(il0373_display_refresh_sequence), 1.0, // refresh_time &pin_GPIO5, // busy_pin diff --git a/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c b/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c index 88150dd401b97..b7f8bc50e714a 100644 --- a/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c +++ b/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c @@ -88,6 +88,7 @@ void board_init(void) { 0x26, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, sizeof(refresh_sequence), // refresh_display_command 1.0, // refresh_time &pin_GPIO48, // busy_pin diff --git a/ports/espressif/boards/heltec_vision_master_e290/board.c b/ports/espressif/boards/heltec_vision_master_e290/board.c index e392f65a76432..5911506ae2c3c 100644 --- a/ports/espressif/boards/heltec_vision_master_e290/board.c +++ b/ports/espressif/boards/heltec_vision_master_e290/board.c @@ -86,6 +86,7 @@ void board_init(void) { 0x26, // write_color_ram_command false, // color_bits_inverted 0xFF0000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, sizeof(refresh_sequence), // refresh_display_command 1.0, // refresh_time &pin_GPIO6, // busy_pin diff --git a/ports/espressif/boards/heltec_wireless_paper/board.c b/ports/espressif/boards/heltec_wireless_paper/board.c index 28fb6ae41b9a1..89e7a054b50c2 100644 --- a/ports/espressif/boards/heltec_wireless_paper/board.c +++ b/ports/espressif/boards/heltec_wireless_paper/board.c @@ -126,6 +126,7 @@ void board_init(void) { 0x10, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, sizeof(refresh_sequence), // refresh_display_command 1.0, // refresh_time &pin_GPIO7, // busy_pin diff --git a/ports/espressif/boards/sqfmi_watchy/board.c b/ports/espressif/boards/sqfmi_watchy/board.c index 393b8b759028a..535a3e11006fe 100644 --- a/ports/espressif/boards/sqfmi_watchy/board.c +++ b/ports/espressif/boards/sqfmi_watchy/board.c @@ -182,6 +182,7 @@ void board_init(void) { 0x26, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, sizeof(refresh_sequence), (double)1.0, // refresh_time &pin_GPIO19, // busy_pin diff --git a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c index 19e18bd544bee..b7e75a475fd09 100644 --- a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c +++ b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c @@ -273,6 +273,7 @@ void board_init(void) { SSD_WRITE_RAM_RED, // write_color_ram_command false, // color_bits_inverted 0xFF0000, // highlight_color (RED for tri-color display) + 0x000000, // highlight_color2 _refresh_sequence_ssd1681, sizeof(_refresh_sequence_ssd1681), // refresh_display_command refresh_time, // refresh_time &pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin @@ -307,6 +308,7 @@ void board_init(void) { NO_COMMAND, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color (RED for tri-color display) + 0x000000, // highlight_color2 _refresh_sequence_ssd1608, sizeof(_refresh_sequence_ssd1608), // refresh_display_command refresh_time, // refresh_time &pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin diff --git a/ports/raspberrypi/boards/pimoroni_badger2040/board.c b/ports/raspberrypi/boards/pimoroni_badger2040/board.c index 5950d11da8a11..392a57cdc0c1b 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2040/board.c @@ -295,6 +295,7 @@ void board_init(void) { DTM1, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, sizeof(refresh_sequence), // refresh_display_command 1.0, // refresh_time &pin_GPIO26, // busy_pin diff --git a/ports/raspberrypi/boards/pimoroni_badger2040w/board.c b/ports/raspberrypi/boards/pimoroni_badger2040w/board.c index 7dc11af01eca3..01c448cd7ad52 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040w/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2040w/board.c @@ -295,6 +295,7 @@ void board_init(void) { DTM1, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, sizeof(refresh_sequence), // refresh_display_command 1.0, // refresh_time &pin_GPIO26, // busy_pin diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c index 7969143ec6621..c7901b8649d78 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c @@ -89,6 +89,7 @@ void board_init(void) { NO_COMMAND, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, sizeof(refresh_sequence), 28.0, // refresh_time NULL, // busy_pin diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c index 7c71e03b4281d..764c85ecb6bcf 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c @@ -148,6 +148,7 @@ void board_init(void) { NO_COMMAND, // write_color_ram_command false, // color_bits_inverted 0x000000, // highlight_color + 0x000000, // highlight_color2 refresh_sequence, sizeof(refresh_sequence), 28.0, // refresh_time NULL, // busy_pin From af67261469a516c4e6231a56ab0a7cbd98f72ca1 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 29 Jul 2025 14:52:22 -0700 Subject: [PATCH 032/517] Tweak computation of new heap area size It now computes slightly more bytes to account for ATB size using truncated division. In other words, any remaining block space that doesn't fill an ATB byte won't be used. So, we round up our next area size to use an exact number of ATB bytes. Fixes #10451 --- py/gc.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/py/gc.c b/py/gc.c index 338803dc245cc..2cf4dbb64a83b 100644 --- a/py/gc.c +++ b/py/gc.c @@ -311,8 +311,9 @@ void gc_add(void *start, void *end) { #if MICROPY_GC_SPLIT_HEAP_AUTO // CIRCUITPY-CHANGE: Added function to compute heap size with selective collect table static size_t compute_heap_size(size_t total_blocks) { - // Add two blocks to account for allocation alignment. - total_blocks += 2; + // Round up to the nearest multiple of BLOCKS_PER_ATB. Partial ATB blocks aren't supported and + // will result in a heap that is too small. + total_blocks = ((total_blocks + BLOCKS_PER_ATB - 1) / BLOCKS_PER_ATB) * BLOCKS_PER_ATB; size_t atb_bytes = (total_blocks + BLOCKS_PER_ATB - 1) / BLOCKS_PER_ATB; size_t ftb_bytes = 0; size_t ctb_bytes = 0; @@ -326,12 +327,13 @@ static size_t compute_heap_size(size_t total_blocks) { // Compute bytes needed to build a heap with total_blocks blocks. size_t total_heap = - atb_bytes + sizeof(mp_state_mem_area_t) + + atb_bytes + + ALLOC_TABLE_GAP_BYTE + ftb_bytes + ctb_bytes + pool_bytes - + ALLOC_TABLE_GAP_BYTE - + sizeof(mp_state_mem_area_t); + + BYTES_PER_BLOCK; // Extra block of bytes to account for end pointer alignment // Round up size to the nearest multiple of BYTES_PER_BLOCK. total_heap = (total_heap + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK; From 6e28ac3d16184c79a260df5e78159aa7db9ec450 Mon Sep 17 00:00:00 2001 From: Olivier Deveault Date: Mon, 28 Jul 2025 23:54:28 +0200 Subject: [PATCH 033/517] Translated using Weblate (French) Currently translated at 100.0% (1014 of 1014 strings) Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/fr/ --- locale/fr.po | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/locale/fr.po b/locale/fr.po index 3a53a97e8fa09..e2846046d93dc 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-06-30 16:01+0000\n" -"Last-Translator: MAE \n" +"PO-Revision-Date: 2025-07-29 22:07+0000\n" +"Last-Translator: Olivier Deveault \n" "Language-Team: \n" "Language: fr\n" "MIME-Version: 1.0\n" @@ -49,7 +49,7 @@ msgid "" "Press reset to exit safe mode.\n" msgstr "" "\n" -"Pour quitter le mode sans échec, appuyez sur reset.\n" +"Appuyer sur reset pour sortir du mode sans échec.\n" #: supervisor/shared/safe_mode.c msgid "" @@ -57,7 +57,7 @@ msgid "" "You are in safe mode because:\n" msgstr "" "\n" -"Le mode sans échec est actif , car:\n" +"Le mode sans échec est actif pour cette raison:\n" #: py/obj.c msgid " File \"%q\"" @@ -2024,7 +2024,7 @@ msgstr "Format correct mais non supporté" #: main.c msgid "Running in safe mode! Not running saved code.\n" -msgstr "Mode sans- échec ! Le code sauvegardé n'est pas éxecuté.\n" +msgstr "Mode sans échec ! Le code sauvegardé n'est pas éxecuté.\n" #: shared-module/sdcardio/SDCard.c msgid "SD card CSD format not supported" From 16f4f65cc97536fd139dfa54a251cac47fb15d5b Mon Sep 17 00:00:00 2001 From: Jacques Supcik Date: Tue, 29 Jul 2025 00:06:36 +0200 Subject: [PATCH 034/517] Translated using Weblate (French) Currently translated at 100.0% (1014 of 1014 strings) Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/fr/ --- locale/fr.po | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/locale/fr.po b/locale/fr.po index e2846046d93dc..b542f8c98fadb 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -9,7 +9,7 @@ msgstr "" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" "PO-Revision-Date: 2025-07-29 22:07+0000\n" -"Last-Translator: Olivier Deveault \n" +"Last-Translator: Jacques Supcik \n" "Language-Team: \n" "Language: fr\n" "MIME-Version: 1.0\n" @@ -1879,15 +1879,15 @@ msgstr "Plus tout autres modules présents sur le système de fichiers\n" #: shared-module/vectorio/Polygon.c msgid "Polygon needs at least 3 points" -msgstr "Polygon a besoin d'au moins 3 points" +msgstr "Un polygone a besoin d'au moins 3 points" #: supervisor/shared/safe_mode.c msgid "Power dipped. Make sure you are providing enough power." -msgstr "Chute de puissance.Apportez plus d'énergie." +msgstr "Chute de puissance. Assurez-vous de fournir assez de puissance." #: shared-bindings/_bleio/Adapter.c msgid "Prefix buffer must be on the heap" -msgstr "Le tampon de préfixe doit être sur la pile" +msgstr "Le tampon de préfixe doit être sur le tas" #: main.c msgid "Press any key to enter the REPL. Use CTRL-D to reload.\n" @@ -1919,11 +1919,12 @@ msgstr "Programme trop long" #: shared-bindings/rclcpy/Publisher.c msgid "Publishers can only be created from a parent node" -msgstr "Les Publishers ne peuvent etre crées que du noeud parent" +msgstr "Les Publishers ne peuvent être créés qu'à partir du nœud parent" #: shared-bindings/digitalio/DigitalInOut.c msgid "Pull not used when direction is output." -msgstr "Le tirage 'pull' n'est pas utilisé quand la direction est 'output'." +msgstr "" +"L'attribut \"pull\" n'est pas utilisé quand la direction est \"output\"." #: ports/raspberrypi/common-hal/countio/Counter.c msgid "RISE_AND_FALL not available on this chip" From 0e26ab4dafd1eac79a1fd8acf96b78ae0e426c0d Mon Sep 17 00:00:00 2001 From: Mario Pesch Date: Wed, 30 Jul 2025 06:41:24 +0000 Subject: [PATCH 035/517] update USB_MANUFACTURER for senseBox boards --- ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk | 2 +- ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk index 8bfcda99a153b..55e822c5dea4a 100644 --- a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk +++ b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk @@ -2,7 +2,7 @@ USB_VID = 0x303A USB_PID = 0x82D2 USB_PRODUCT = "senseBox-eye ESP32S3" -USB_MANUFACTURER = "Espressif" +USB_MANUFACTURER = "senseBox" IDF_TARGET = esp32s3 diff --git a/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk b/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk index 9d3fbecabde71..6b97c870cc032 100644 --- a/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk +++ b/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk @@ -1,7 +1,7 @@ USB_VID = 0x303A USB_PID = 0x81B9 USB_PRODUCT = "senseBox MCU-S2 ESP32S2" -USB_MANUFACTURER = "Espressif" +USB_MANUFACTURER = "senseBox" IDF_TARGET = esp32s2 From fd2627cf950d4a0987b3b79a0ad12f019be8a1dd Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Wed, 30 Jul 2025 12:45:15 -0500 Subject: [PATCH 036/517] Support mono to stereo conversion in `audiomixer.Mixer` --- shared-module/audiocore/__init__.c | 4 +- shared-module/audiocore/__init__.h | 2 +- shared-module/audiodelays/Chorus.c | 2 +- shared-module/audiodelays/Echo.c | 2 +- shared-module/audiodelays/MultiTapDelay.c | 2 +- shared-module/audiodelays/PitchShift.c | 2 +- shared-module/audiofilters/Distortion.c | 2 +- shared-module/audiofilters/Filter.c | 2 +- shared-module/audiofilters/Phaser.c | 2 +- shared-module/audiofreeverb/Freeverb.c | 2 +- shared-module/audiomixer/Mixer.c | 161 +++++++++++++++++----- shared-module/audiomixer/MixerVoice.c | 2 +- 12 files changed, 142 insertions(+), 43 deletions(-) diff --git a/shared-module/audiocore/__init__.c b/shared-module/audiocore/__init__.c index bf80403137996..2ee683d75e1cd 100644 --- a/shared-module/audiocore/__init__.c +++ b/shared-module/audiocore/__init__.c @@ -196,12 +196,12 @@ void audiosample_convert_s16s_u8s(uint8_t *buffer_out, const int16_t *buffer_in, } } -void audiosample_must_match(audiosample_base_t *self, mp_obj_t other_in) { +void audiosample_must_match(audiosample_base_t *self, mp_obj_t other_in, bool allow_mono_to_stereo) { const audiosample_base_t *other = audiosample_check(other_in); if (other->sample_rate != self->sample_rate) { mp_raise_ValueError_varg(MP_ERROR_TEXT("The sample's %q does not match"), MP_QSTR_sample_rate); } - if (other->channel_count != self->channel_count) { + if ((!allow_mono_to_stereo || (allow_mono_to_stereo && self->channel_count != 2)) && other->channel_count != self->channel_count) { mp_raise_ValueError_varg(MP_ERROR_TEXT("The sample's %q does not match"), MP_QSTR_channel_count); } if (other->bits_per_sample != self->bits_per_sample) { diff --git a/shared-module/audiocore/__init__.h b/shared-module/audiocore/__init__.h index 875ac90ecc5c1..920fe752c3822 100644 --- a/shared-module/audiocore/__init__.h +++ b/shared-module/audiocore/__init__.h @@ -89,7 +89,7 @@ static inline void audiosample_get_buffer_structure_checked(mp_obj_t self_in, bo audiosample_get_buffer_structure(audiosample_check(self_in), single_channel_output, single_buffer, samples_signed, max_buffer_length, spacing); } -void audiosample_must_match(audiosample_base_t *self, mp_obj_t other); +void audiosample_must_match(audiosample_base_t *self, mp_obj_t other, bool allow_mono_to_stereo); void audiosample_convert_u8m_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes); void audiosample_convert_u8s_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes); diff --git a/shared-module/audiodelays/Chorus.c b/shared-module/audiodelays/Chorus.c index 1609cc5f94684..9262c6b4de195 100644 --- a/shared-module/audiodelays/Chorus.c +++ b/shared-module/audiodelays/Chorus.c @@ -166,7 +166,7 @@ bool common_hal_audiodelays_chorus_get_playing(audiodelays_chorus_obj_t *self) { } void common_hal_audiodelays_chorus_play(audiodelays_chorus_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiodelays/Echo.c b/shared-module/audiodelays/Echo.c index 78764d9099a1c..0ca5fc8a7502e 100644 --- a/shared-module/audiodelays/Echo.c +++ b/shared-module/audiodelays/Echo.c @@ -204,7 +204,7 @@ bool common_hal_audiodelays_echo_get_playing(audiodelays_echo_obj_t *self) { } void common_hal_audiodelays_echo_play(audiodelays_echo_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiodelays/MultiTapDelay.c b/shared-module/audiodelays/MultiTapDelay.c index 7b702ecf1154a..9c33a5eaee750 100644 --- a/shared-module/audiodelays/MultiTapDelay.c +++ b/shared-module/audiodelays/MultiTapDelay.c @@ -285,7 +285,7 @@ bool common_hal_audiodelays_multi_tap_delay_get_playing(audiodelays_multi_tap_de } void common_hal_audiodelays_multi_tap_delay_play(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiodelays/PitchShift.c b/shared-module/audiodelays/PitchShift.c index ac349da0dd50a..3b8c1a07c7b16 100644 --- a/shared-module/audiodelays/PitchShift.c +++ b/shared-module/audiodelays/PitchShift.c @@ -143,7 +143,7 @@ bool common_hal_audiodelays_pitch_shift_get_playing(audiodelays_pitch_shift_obj_ } void common_hal_audiodelays_pitch_shift_play(audiodelays_pitch_shift_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiofilters/Distortion.c b/shared-module/audiofilters/Distortion.c index ec5fe084e56d3..c4b6b7566ad0d 100644 --- a/shared-module/audiofilters/Distortion.c +++ b/shared-module/audiofilters/Distortion.c @@ -141,7 +141,7 @@ bool common_hal_audiofilters_distortion_get_playing(audiofilters_distortion_obj_ } void common_hal_audiofilters_distortion_play(audiofilters_distortion_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiofilters/Filter.c b/shared-module/audiofilters/Filter.c index 19b567532eb8e..4cedc810abad6 100644 --- a/shared-module/audiofilters/Filter.c +++ b/shared-module/audiofilters/Filter.c @@ -143,7 +143,7 @@ bool common_hal_audiofilters_filter_get_playing(audiofilters_filter_obj_t *self) } void common_hal_audiofilters_filter_play(audiofilters_filter_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiofilters/Phaser.c b/shared-module/audiofilters/Phaser.c index 81d9c0bea3083..01b938c3a0027 100644 --- a/shared-module/audiofilters/Phaser.c +++ b/shared-module/audiofilters/Phaser.c @@ -130,7 +130,7 @@ bool common_hal_audiofilters_phaser_get_playing(audiofilters_phaser_obj_t *self) } void common_hal_audiofilters_phaser_play(audiofilters_phaser_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiofreeverb/Freeverb.c b/shared-module/audiofreeverb/Freeverb.c index d910536013604..28779bdcc84b2 100644 --- a/shared-module/audiofreeverb/Freeverb.c +++ b/shared-module/audiofreeverb/Freeverb.c @@ -195,7 +195,7 @@ bool common_hal_audiofreeverb_freeverb_get_playing(audiofreeverb_freeverb_obj_t } void common_hal_audiofreeverb_freeverb_play(audiofreeverb_freeverb_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index 2c727a5c0cdde..2f510dd1338aa 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -154,9 +154,30 @@ static inline uint32_t pack8(uint32_t val) { return ((val & 0xff000000) >> 16) | ((val & 0xff00) >> 8); } +static inline uint32_t copy16lsb(uint32_t val) { + val &= 0x0000ffff; + return val | (val << 16); +} + +static inline uint32_t copy16msb(uint32_t val) { + val &= 0xffff0000; + return val | (val >> 16); +} + +static inline uint32_t copy8lsb(uint32_t val) { + val &= 0x00ff; + return val | (val << 8); +} + +static inline uint32_t copy8msb(uint32_t val) { + val &= 0xff00; + return val | (val >> 8); +} + static void mix_down_one_voice(audiomixer_mixer_obj_t *self, audiomixer_mixervoice_obj_t *voice, bool voices_active, uint32_t *word_buffer, uint32_t length) { + audiosample_base_t *sample = MP_OBJ_TO_PTR(voice->sample); while (length != 0) { if (voice->buffer_length == 0) { if (!voice->more_data) { @@ -179,13 +200,23 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, uint32_t *src = voice->remaining_buffer; #if CIRCUITPY_SYNTHIO - uint32_t n = MIN(MIN(voice->buffer_length, length), SYNTHIO_MAX_DUR * self->base.channel_count); + uint32_t n; + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + n = MIN(MIN(voice->buffer_length, length), SYNTHIO_MAX_DUR * self->base.channel_count); + } else { + n = MIN(MIN(voice->buffer_length << 1, length), SYNTHIO_MAX_DUR * self->base.channel_count); + } // Get the current level from the BlockInput. These may change at run time so you need to do bounds checking if required. shared_bindings_synthio_lfo_tick(self->base.sample_rate, n / self->base.channel_count); uint16_t level = (uint16_t)(synthio_block_slot_get_limited(&voice->level, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * (1 << 15)); #else - uint32_t n = MIN(voice->buffer_length, length); + uint32_t n; + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + n = MIN(voice->buffer_length, length); + } else { + n = MIN(voice->buffer_length << 1, length); + } uint16_t level = voice->level; #endif @@ -193,61 +224,129 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (!voices_active) { if (MP_LIKELY(self->base.bits_per_sample == 16)) { if (MP_LIKELY(self->base.samples_signed)) { - for (uint32_t i = 0; i < n; i++) { - uint32_t v = src[i]; - word_buffer[i] = mult16signed(v, level); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n; i++) { + uint32_t v = src[i]; + word_buffer[i] = mult16signed(v, level); + } + } else { + for (uint32_t i = 0; i < n; i += 2) { + uint32_t v = src[i >> 1]; + v = mult16signed(v, level); + word_buffer[i] = copy16lsb(v); + word_buffer[i + 1] = copy16msb(v); + } } } else { - for (uint32_t i = 0; i < n; i++) { - uint32_t v = src[i]; - v = tosigned16(v); - word_buffer[i] = mult16signed(v, level); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n; i++) { + uint32_t v = src[i]; + v = tosigned16(v); + word_buffer[i] = mult16signed(v, level); + } + } else { + for (uint32_t i = 0; i + 1 < n; i += 2) { + uint32_t v = src[i >> 1]; + v = tosigned16(v); + v = mult16signed(v, level); + word_buffer[i] = copy16lsb(v); + word_buffer[i + 1] = copy16msb(v); + } } } } else { uint16_t *hword_buffer = (uint16_t *)word_buffer; uint16_t *hsrc = (uint16_t *)src; - for (uint32_t i = 0; i < n * 2; i++) { - uint32_t word = unpack8(hsrc[i]); - if (MP_LIKELY(!self->base.samples_signed)) { - word = tosigned16(word); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n * 2; i++) { + uint32_t word = unpack8(hsrc[i]); + if (MP_LIKELY(!self->base.samples_signed)) { + word = tosigned16(word); + } + word = mult16signed(word, level); + hword_buffer[i] = pack8(word); + } + } else { + for (uint32_t i = 0; i + 1 < n * 2; i += 2) { + uint32_t word = unpack8(hsrc[i >> 1]); + if (MP_LIKELY(!self->base.samples_signed)) { + word = tosigned16(word); + } + word = mult16signed(word, level); + word = pack8(word); + hword_buffer[i] = copy8lsb(word); + hword_buffer[i + 1] = copy8msb(word); } - word = mult16signed(word, level); - hword_buffer[i] = pack8(word); } } } else { if (MP_LIKELY(self->base.bits_per_sample == 16)) { if (MP_LIKELY(self->base.samples_signed)) { - for (uint32_t i = 0; i < n; i++) { - uint32_t word = src[i]; - word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n; i++) { + uint32_t word = src[i]; + word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]); + } + } else { + for (uint32_t i = 0; i + 1 < n; i += 2) { + uint32_t word = src[i >> 1]; + word = mult16signed(word, level); + word_buffer[i] = add16signed(copy16lsb(word), word_buffer[i]); + word_buffer[i + 1] = add16signed(copy16msb(word), word_buffer[i + 1]); + } } } else { - for (uint32_t i = 0; i < n; i++) { - uint32_t word = src[i]; - word = tosigned16(word); - word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n; i++) { + uint32_t word = src[i]; + word = tosigned16(word); + word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]); + } + } else { + for (uint32_t i = 0; i + 1 < n; i += 2) { + uint32_t word = src[i >> 1]; + word = tosigned16(word); + word = mult16signed(word, level); + word_buffer[i] = add16signed(copy16lsb(word), word_buffer[i]); + word_buffer[i + 1] = add16signed(copy16msb(word), word_buffer[i + 1]); + } } } } else { uint16_t *hword_buffer = (uint16_t *)word_buffer; uint16_t *hsrc = (uint16_t *)src; - for (uint32_t i = 0; i < n * 2; i++) { - uint32_t word = unpack8(hsrc[i]); - if (MP_LIKELY(!self->base.samples_signed)) { - word = tosigned16(word); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n * 2; i++) { + uint32_t word = unpack8(hsrc[i]); + if (MP_LIKELY(!self->base.samples_signed)) { + word = tosigned16(word); + } + word = mult16signed(word, level); + word = add16signed(word, unpack8(hword_buffer[i])); + hword_buffer[i] = pack8(word); + } + } else { + for (uint32_t i = 0; i + 1 < n * 2; i += 2) { + uint32_t word = unpack8(hsrc[i >> 1]); + if (MP_LIKELY(!self->base.samples_signed)) { + word = tosigned16(word); + } + word = mult16signed(word, level); + hword_buffer[i] = pack8(add16signed(copy16lsb(word), unpack8(hword_buffer[i]))); + hword_buffer[i + 1] = pack8(add16signed(copy16msb(word), unpack8(hword_buffer[i + 1]))); } - word = mult16signed(word, level); - word = add16signed(word, unpack8(hword_buffer[i])); - hword_buffer[i] = pack8(word); } } } length -= n; word_buffer += n; - voice->remaining_buffer += n; - voice->buffer_length -= n; + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + voice->remaining_buffer += n; + voice->buffer_length -= n; + } else { + voice->remaining_buffer += n >> 1; + voice->buffer_length -= n >> 1; + } } if (length && !voices_active) { diff --git a/shared-module/audiomixer/MixerVoice.c b/shared-module/audiomixer/MixerVoice.c index e0be869aa8e44..96f3baf406880 100644 --- a/shared-module/audiomixer/MixerVoice.c +++ b/shared-module/audiomixer/MixerVoice.c @@ -47,7 +47,7 @@ void common_hal_audiomixer_mixervoice_set_loop(audiomixer_mixervoice_obj_t *self } void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t *self, mp_obj_t sample_in, bool loop) { - audiosample_must_match(&self->parent->base, sample_in); + audiosample_must_match(&self->parent->base, sample_in, true); // cast is safe, checked by must_match audiosample_base_t *sample = MP_OBJ_TO_PTR(sample_in); self->sample = sample; From c92efd66db3b9c66679c3b6752ecbb38612ab4f8 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Wed, 30 Jul 2025 13:26:04 -0500 Subject: [PATCH 037/517] Add `MixerVoice.panning` --- shared-bindings/audiomixer/MixerVoice.c | 26 +++++++++ shared-bindings/audiomixer/MixerVoice.h | 2 + shared-module/audiomixer/Mixer.c | 70 +++++++++++++++---------- shared-module/audiomixer/MixerVoice.c | 17 ++++++ shared-module/audiomixer/MixerVoice.h | 2 + 5 files changed, 88 insertions(+), 29 deletions(-) diff --git a/shared-bindings/audiomixer/MixerVoice.c b/shared-bindings/audiomixer/MixerVoice.c index fc292d4ea2f68..b87bd4d6525cb 100644 --- a/shared-bindings/audiomixer/MixerVoice.c +++ b/shared-bindings/audiomixer/MixerVoice.c @@ -114,6 +114,31 @@ MP_PROPERTY_GETSET(audiomixer_mixervoice_level_obj, (mp_obj_t)&audiomixer_mixervoice_get_level_obj, (mp_obj_t)&audiomixer_mixervoice_set_level_obj); +//| panning: synthio.BlockInput +//| """Defines the channel(s) in which the voice appears, as a floating point number between +//| -1 and 1. If your board does not support synthio, this property will only accept a float +//| value. This property is ignored if `audiomixer.Mixer.channel_count=1`. +//| +//| -1 is left channel only, 0 is both channels, and 1 is right channel. For fractional values, +//| the note plays at full amplitude in one channel and partial amplitude in the other channel. +//| For instance -.5 plays at full amplitude in the left channel and 1/2 amplitude in the right +//| channel.""" +static mp_obj_t audiomixer_mixervoice_obj_get_panning(mp_obj_t self_in) { + return common_hal_audiomixer_mixervoice_get_panning(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixervoice_get_panning_obj, audiomixer_mixervoice_obj_get_panning); + +static mp_obj_t audiomixer_mixervoice_obj_set_panning(mp_obj_t self_in, mp_obj_t panning_in) { + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiomixer_mixervoice_set_panning(self, panning_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiomixer_mixervoice_set_panning_obj, audiomixer_mixervoice_obj_set_panning); + +MP_PROPERTY_GETSET(audiomixer_mixervoice_panning_obj, + (mp_obj_t)&audiomixer_mixervoice_get_panning_obj, + (mp_obj_t)&audiomixer_mixervoice_set_panning_obj); + //| loop: bool //| """Get or set the loop status of the currently playing sample.""" static mp_obj_t audiomixer_mixervoice_obj_get_loop(mp_obj_t self_in) { @@ -158,6 +183,7 @@ static const mp_rom_map_elem_t audiomixer_mixervoice_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiomixer_mixervoice_playing_obj) }, { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audiomixer_mixervoice_level_obj) }, + { MP_ROM_QSTR(MP_QSTR_panning), MP_ROM_PTR(&audiomixer_mixervoice_panning_obj) }, { MP_ROM_QSTR(MP_QSTR_loop), MP_ROM_PTR(&audiomixer_mixervoice_loop_obj) }, }; static MP_DEFINE_CONST_DICT(audiomixer_mixervoice_locals_dict, audiomixer_mixervoice_locals_dict_table); diff --git a/shared-bindings/audiomixer/MixerVoice.h b/shared-bindings/audiomixer/MixerVoice.h index fe350eb1a7592..d60820f7f5734 100644 --- a/shared-bindings/audiomixer/MixerVoice.h +++ b/shared-bindings/audiomixer/MixerVoice.h @@ -18,6 +18,8 @@ void common_hal_audiomixer_mixervoice_stop(audiomixer_mixervoice_obj_t *self); void common_hal_audiomixer_mixervoice_end(audiomixer_mixervoice_obj_t *self); mp_obj_t common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self); void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *self, mp_obj_t gain); +mp_obj_t common_hal_audiomixer_mixervoice_get_panning(audiomixer_mixervoice_obj_t *self); +void common_hal_audiomixer_mixervoice_set_panning(audiomixer_mixervoice_obj_t *self, mp_obj_t value); bool common_hal_audiomixer_mixervoice_get_playing(audiomixer_mixervoice_obj_t *self); diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index 2f510dd1338aa..ebbb410388f23 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -92,22 +92,22 @@ static inline uint32_t add16signed(uint32_t a, uint32_t b) { } __attribute__((always_inline)) -static inline uint32_t mult16signed(uint32_t val, int32_t mul) { +static inline uint32_t mult16signed(uint32_t val, int32_t mul[2]) { #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) mul <<= 16; int32_t hi, lo; enum { bits = 16 }; // saturate to 16 bits enum { shift = 15 }; // shift is done automatically - asm volatile ("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); - asm volatile ("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); + asm volatile ("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul[0]), "r" (val)); + asm volatile ("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul[1]), "r" (val)); asm volatile ("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); asm volatile ("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); asm volatile ("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack return val; #else uint32_t result = 0; - float mod_mul = (float)mul / (float)((1 << 15) - 1); for (int8_t i = 0; i < 2; i++) { + float mod_mul = (float)mul[i] / (float)((1 << 15) - 1); int16_t ai = (val >> (sizeof(uint16_t) * 8 * i)); int32_t intermediate = (int32_t)(ai * mod_mul); if (intermediate > SHRT_MAX) { @@ -174,6 +174,8 @@ static inline uint32_t copy8msb(uint32_t val) { return val | (val >> 8); } +#define ALMOST_ONE (MICROPY_FLOAT_CONST(32767.) / 32768) + static void mix_down_one_voice(audiomixer_mixer_obj_t *self, audiomixer_mixervoice_obj_t *voice, bool voices_active, uint32_t *word_buffer, uint32_t length) { @@ -210,6 +212,7 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, // Get the current level from the BlockInput. These may change at run time so you need to do bounds checking if required. shared_bindings_synthio_lfo_tick(self->base.sample_rate, n / self->base.channel_count); uint16_t level = (uint16_t)(synthio_block_slot_get_limited(&voice->level, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * (1 << 15)); + int16_t panning = synthio_block_slot_get_scaled(&voice->panning, -ALMOST_ONE, ALMOST_ONE); #else uint32_t n; if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { @@ -218,8 +221,24 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, n = MIN(voice->buffer_length << 1, length); } uint16_t level = voice->level; + int16_t panning = voice->panning; #endif + uint16_t left_panning_scaled = 32768, right_panning_scaled = 32768; + if (MP_LIKELY(self->base.channel_count == 2)) { + if (panning >= 0) { + right_panning_scaled = 32767 - panning; + } else { + left_panning_scaled = 32767 + panning; + } + } + + int32_t loudness[2] = { level, level }; + if (MP_LIKELY(self->base.channel_count == 2)) { + loudness[0] = (left_panning_scaled * loudness[0]) >> 15; + loudness[1] = (right_panning_scaled * loudness[1]) >> 15; + } + // First active voice gets copied over verbatim. if (!voices_active) { if (MP_LIKELY(self->base.bits_per_sample == 16)) { @@ -227,14 +246,13 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { for (uint32_t i = 0; i < n; i++) { uint32_t v = src[i]; - word_buffer[i] = mult16signed(v, level); + word_buffer[i] = mult16signed(v, loudness); } } else { for (uint32_t i = 0; i < n; i += 2) { uint32_t v = src[i >> 1]; - v = mult16signed(v, level); - word_buffer[i] = copy16lsb(v); - word_buffer[i + 1] = copy16msb(v); + word_buffer[i] = mult16signed(copy16lsb(v), loudness); + word_buffer[i + 1] = mult16signed(copy16msb(v), loudness); } } } else { @@ -242,15 +260,14 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, for (uint32_t i = 0; i < n; i++) { uint32_t v = src[i]; v = tosigned16(v); - word_buffer[i] = mult16signed(v, level); + word_buffer[i] = mult16signed(v, loudness); } } else { for (uint32_t i = 0; i + 1 < n; i += 2) { uint32_t v = src[i >> 1]; v = tosigned16(v); - v = mult16signed(v, level); - word_buffer[i] = copy16lsb(v); - word_buffer[i + 1] = copy16msb(v); + word_buffer[i] = mult16signed(copy16lsb(v), loudness); + word_buffer[i + 1] = mult16signed(copy16msb(v), loudness); } } } @@ -263,7 +280,7 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(!self->base.samples_signed)) { word = tosigned16(word); } - word = mult16signed(word, level); + word = mult16signed(word, loudness); hword_buffer[i] = pack8(word); } } else { @@ -272,10 +289,8 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(!self->base.samples_signed)) { word = tosigned16(word); } - word = mult16signed(word, level); - word = pack8(word); - hword_buffer[i] = copy8lsb(word); - hword_buffer[i + 1] = copy8msb(word); + hword_buffer[i] = pack8(mult16signed(copy16lsb(word), loudness)); + hword_buffer[i + 1] = pack8(mult16signed(copy16msb(word), loudness)); } } } @@ -285,14 +300,13 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { for (uint32_t i = 0; i < n; i++) { uint32_t word = src[i]; - word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]); + word_buffer[i] = add16signed(mult16signed(word, loudness), word_buffer[i]); } } else { for (uint32_t i = 0; i + 1 < n; i += 2) { uint32_t word = src[i >> 1]; - word = mult16signed(word, level); - word_buffer[i] = add16signed(copy16lsb(word), word_buffer[i]); - word_buffer[i + 1] = add16signed(copy16msb(word), word_buffer[i + 1]); + word_buffer[i] = add16signed(mult16signed(copy16lsb(word), loudness), word_buffer[i]); + word_buffer[i + 1] = add16signed(mult16signed(copy16msb(word), loudness), word_buffer[i + 1]); } } } else { @@ -300,15 +314,14 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, for (uint32_t i = 0; i < n; i++) { uint32_t word = src[i]; word = tosigned16(word); - word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]); + word_buffer[i] = add16signed(mult16signed(word, loudness), word_buffer[i]); } } else { for (uint32_t i = 0; i + 1 < n; i += 2) { uint32_t word = src[i >> 1]; word = tosigned16(word); - word = mult16signed(word, level); - word_buffer[i] = add16signed(copy16lsb(word), word_buffer[i]); - word_buffer[i + 1] = add16signed(copy16msb(word), word_buffer[i + 1]); + word_buffer[i] = add16signed(mult16signed(copy16lsb(word), loudness), word_buffer[i]); + word_buffer[i + 1] = add16signed(mult16signed(copy16msb(word), loudness), word_buffer[i + 1]); } } } @@ -321,7 +334,7 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(!self->base.samples_signed)) { word = tosigned16(word); } - word = mult16signed(word, level); + word = mult16signed(word, loudness); word = add16signed(word, unpack8(hword_buffer[i])); hword_buffer[i] = pack8(word); } @@ -331,9 +344,8 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, if (MP_LIKELY(!self->base.samples_signed)) { word = tosigned16(word); } - word = mult16signed(word, level); - hword_buffer[i] = pack8(add16signed(copy16lsb(word), unpack8(hword_buffer[i]))); - hword_buffer[i + 1] = pack8(add16signed(copy16msb(word), unpack8(hword_buffer[i + 1]))); + hword_buffer[i] = pack8(add16signed(mult16signed(copy16lsb(word), loudness), unpack8(hword_buffer[i]))); + hword_buffer[i + 1] = pack8(add16signed(mult16signed(copy16msb(word), loudness), unpack8(hword_buffer[i + 1]))); } } } diff --git a/shared-module/audiomixer/MixerVoice.c b/shared-module/audiomixer/MixerVoice.c index 96f3baf406880..6c23a39215cbc 100644 --- a/shared-module/audiomixer/MixerVoice.c +++ b/shared-module/audiomixer/MixerVoice.c @@ -16,6 +16,7 @@ void common_hal_audiomixer_mixervoice_construct(audiomixer_mixervoice_obj_t *self) { self->sample = NULL; common_hal_audiomixer_mixervoice_set_level(self, mp_obj_new_float(1.0)); + common_hal_audiomixer_mixervoice_set_panning(self, mp_obj_new_float(0.0)); } void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t *self, audiomixer_mixer_obj_t *parent) { @@ -38,6 +39,22 @@ void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *sel #endif } +mp_obj_t common_hal_audiomixer_mixervoice_get_panning(audiomixer_mixervoice_obj_t *self) { + #if CIRCUITPY_SYNTHIO + return self->panning.obj; + #else + return mp_obj_new_float((mp_float_t)self->panning / ((1 << 15) - 1)); + #endif +} + +void common_hal_audiomixer_mixervoice_set_panning(audiomixer_mixervoice_obj_t *self, mp_obj_t arg) { + #if CIRCUITPY_SYNTHIO + synthio_block_assign_slot(arg, &self->panning, MP_QSTR_panning); + #else + self->panning = (uint16_t)(mp_arg_validate_obj_float_range(arg, -1, 1, MP_QSTR_panning) * ((1 << 15) - 1)); + #endif +} + bool common_hal_audiomixer_mixervoice_get_loop(audiomixer_mixervoice_obj_t *self) { return self->loop; } diff --git a/shared-module/audiomixer/MixerVoice.h b/shared-module/audiomixer/MixerVoice.h index dd9095515459a..75e7d47faa33d 100644 --- a/shared-module/audiomixer/MixerVoice.h +++ b/shared-module/audiomixer/MixerVoice.h @@ -23,7 +23,9 @@ typedef struct { uint32_t buffer_length; #if CIRCUITPY_SYNTHIO synthio_block_slot_t level; + synthio_block_slot_t panning; #else uint16_t level; + int16_t panning; #endif } audiomixer_mixervoice_obj_t; From fb84062800e85a826ab08a9b4fc8f5626599647c Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Wed, 30 Jul 2025 14:05:24 -0500 Subject: [PATCH 038/517] Fix bitshift operation on atmel-samd --- shared-module/audiomixer/Mixer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index ebbb410388f23..b27eafb71a98c 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -94,7 +94,8 @@ static inline uint32_t add16signed(uint32_t a, uint32_t b) { __attribute__((always_inline)) static inline uint32_t mult16signed(uint32_t val, int32_t mul[2]) { #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) - mul <<= 16; + mul[0] <<= 16; + mul[1] <<= 16; int32_t hi, lo; enum { bits = 16 }; // saturate to 16 bits enum { shift = 15 }; // shift is done automatically From f94f564a41e7821f1c34354e19c1bbc53394cc84 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Wed, 30 Jul 2025 14:15:05 -0500 Subject: [PATCH 039/517] Fix docstring reference --- shared-bindings/audiomixer/MixerVoice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/audiomixer/MixerVoice.c b/shared-bindings/audiomixer/MixerVoice.c index b87bd4d6525cb..128c214d447f8 100644 --- a/shared-bindings/audiomixer/MixerVoice.c +++ b/shared-bindings/audiomixer/MixerVoice.c @@ -117,7 +117,7 @@ MP_PROPERTY_GETSET(audiomixer_mixervoice_level_obj, //| panning: synthio.BlockInput //| """Defines the channel(s) in which the voice appears, as a floating point number between //| -1 and 1. If your board does not support synthio, this property will only accept a float -//| value. This property is ignored if `audiomixer.Mixer.channel_count=1`. +//| value. This property is ignored if ``audiomixer.Mixer.channel_count=1``. //| //| -1 is left channel only, 0 is both channels, and 1 is right channel. For fractional values, //| the note plays at full amplitude in one channel and partial amplitude in the other channel. From f1d20b277181483a55869c2600a4ef159af506e5 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 30 Jul 2025 17:33:21 -0400 Subject: [PATCH 040/517] .github/actions/deps/ports/nordic/action.yml: Change URL for nrfutil --- .github/actions/deps/ports/nordic/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/deps/ports/nordic/action.yml b/.github/actions/deps/ports/nordic/action.yml index 5f08b17ca7a68..96754be9507c5 100644 --- a/.github/actions/deps/ports/nordic/action.yml +++ b/.github/actions/deps/ports/nordic/action.yml @@ -5,7 +5,7 @@ runs: steps: - name: Get nrfutil 7+ run: | - wget https://developer.nordicsemi.com/.pc-tools/nrfutil/x64-linux/nrfutil + wget https://files.nordicsemi.com/artifactory/swtools/external/nrfutil/executables/x86_64-unknown-linux-gnu/nrfutil chmod +x nrfutil ./nrfutil install nrf5sdk-tools mkdir -p $HOME/.local/bin From 58b13f731d0dd8bfc0ed329486a01afec9f72a90 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:45:33 +0000 Subject: [PATCH 041/517] fix picodvi refresh rates (use 60Hz) This implements video mode timing changes suggested by @WolfWings in circuitpython issue #10242. Previously, picodvi was set so that 640x480 and 720x400 used refresh rates faster than 60Hz. These changes lower the refresh rate to 60Hz which should hopefully be compatible with a wider range of HDMI devices. --- .../common-hal/picodvi/Framebuffer_RP2350.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c index 79ec315d497e4..2dc19558314cb 100644 --- a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +++ b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c @@ -54,15 +54,15 @@ #define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20)) #define MODE_720_H_SYNC_POLARITY 0 -#define MODE_720_H_FRONT_PORCH 24 -#define MODE_720_H_SYNC_WIDTH 64 -#define MODE_720_H_BACK_PORCH 88 +#define MODE_720_H_FRONT_PORCH 8 +#define MODE_720_H_SYNC_WIDTH 32 +#define MODE_720_H_BACK_PORCH 40 #define MODE_720_H_ACTIVE_PIXELS 720 #define MODE_720_V_SYNC_POLARITY 0 #define MODE_720_V_FRONT_PORCH 3 #define MODE_720_V_SYNC_WIDTH 4 -#define MODE_720_V_BACK_PORCH 13 +#define MODE_720_V_BACK_PORCH 218 #define MODE_720_V_ACTIVE_LINES 400 #define MODE_640_H_SYNC_POLARITY 0 @@ -74,7 +74,7 @@ #define MODE_640_V_SYNC_POLARITY 0 #define MODE_640_V_FRONT_PORCH 10 #define MODE_640_V_SYNC_WIDTH 2 -#define MODE_640_V_BACK_PORCH 33 +#define MODE_640_V_BACK_PORCH 133 #define MODE_640_V_ACTIVE_LINES 480 #define MODE_720_V_TOTAL_LINES ( \ From 173a94661bc8371ee5639ba47db4d82af78951ad Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 1 Aug 2025 13:51:35 -0400 Subject: [PATCH 042/517] correct board.UART() pins on Feather ESP32-S2/S3 also touch up some other files to be identical --- .../mpconfigboard.h | 4 ++-- .../mpconfigboard.mk | 3 +++ .../adafruit_feather_esp32s2_reverse_tft/sdkconfig | 14 ++++++++++++++ .../mpconfigboard.h | 4 ++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.h index 42b51434edfd3..f2ec187abde63 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.h @@ -23,7 +23,7 @@ #define DEFAULT_SPI_BUS_MOSI (&pin_GPIO35) #define DEFAULT_SPI_BUS_MISO (&pin_GPIO37) -#define DEFAULT_UART_BUS_RX (&pin_GPIO34) -#define DEFAULT_UART_BUS_TX (&pin_GPIO35) +#define DEFAULT_UART_BUS_RX (&pin_GPIO38) +#define DEFAULT_UART_BUS_TX (&pin_GPIO39) #define DOUBLE_TAP_PIN (&pin_GPIO34) diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.mk index 06a1bf706d345..a0be8c7a5ca0a 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.mk @@ -13,3 +13,6 @@ CIRCUITPY_ESP_FLASH_SIZE = 4MB CIRCUITPY_ESP_PSRAM_SIZE = 2MB CIRCUITPY_ESP_PSRAM_MODE = qio CIRCUITPY_ESP_PSRAM_FREQ = 80m + +# Already have a display. +CIRCUITPY_PARALLELDISPLAYBUS = 0 diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/sdkconfig index e69de29bb2d1d..e962866216039 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h index c11780faf41ea..5053a1b2b589b 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h @@ -23,7 +23,7 @@ #define DEFAULT_SPI_BUS_MOSI (&pin_GPIO35) #define DEFAULT_SPI_BUS_MISO (&pin_GPIO37) -#define DEFAULT_UART_BUS_RX (&pin_GPIO34) -#define DEFAULT_UART_BUS_TX (&pin_GPIO35) +#define DEFAULT_UART_BUS_RX (&pin_GPIO38) +#define DEFAULT_UART_BUS_TX (&pin_GPIO39) #define DOUBLE_TAP_PIN (&pin_GPIO34) From efc45dea15e43c8b91f15b8f12573fff246e88c9 Mon Sep 17 00:00:00 2001 From: MAE Date: Sat, 2 Aug 2025 09:45:08 +0200 Subject: [PATCH 043/517] Translated using Weblate (Spanish) Currently translated at 99.6% (1010 of 1014 strings) Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/es/ --- locale/es.po | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/locale/es.po b/locale/es.po index fa7377b5b53d5..85a03408b7517 100644 --- a/locale/es.po +++ b/locale/es.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-07-17 04:01+0000\n" -"Last-Translator: Jean Serrano \n" +"PO-Revision-Date: 2025-08-03 08:01+0000\n" +"Last-Translator: MAE \n" "Language-Team: \n" "Language: es\n" "MIME-Version: 1.0\n" @@ -1803,10 +1803,9 @@ msgid "PWM slice channel A already in use" msgstr "Segmento del PWM canal A ya esta en uso" #: shared-bindings/spitarget/SPITarget.c -#, fuzzy msgid "Packet buffers for an SPI transfer must have the same length." msgstr "" -"Búferes de paquetes para transferencia SPI deben de ser de igual longitud" +"Búferes de paquetes para transferencia SPI deben de ser de igual longitud." #: shared-module/jpegio/JpegDecoder.c msgid "Parameter error" From e3f61544f563de33d4c991c48b982185a932bd2d Mon Sep 17 00:00:00 2001 From: MAE Date: Sat, 2 Aug 2025 09:57:57 +0200 Subject: [PATCH 044/517] Translated using Weblate (French) Currently translated at 99.9% (1013 of 1014 strings) Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/fr/ --- locale/fr.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locale/fr.po b/locale/fr.po index b542f8c98fadb..f871c669f93ed 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-07-29 22:07+0000\n" -"Last-Translator: Jacques Supcik \n" +"PO-Revision-Date: 2025-08-03 08:01+0000\n" +"Last-Translator: MAE \n" "Language-Team: \n" "Language: fr\n" "MIME-Version: 1.0\n" @@ -3972,7 +3972,7 @@ msgstr "seules les tranches avec 'step=1' (cad None) sont supportées" #: py/vm.c msgid "opcode" -msgstr "opcode" +msgstr "" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/bitwise.c #: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/vector.c From 23bf5a52e1863ec8e2873f141b723958bdf9ccca Mon Sep 17 00:00:00 2001 From: MAE Date: Sat, 2 Aug 2025 09:46:40 +0200 Subject: [PATCH 045/517] Translated using Weblate (Polish) Currently translated at 52.3% (531 of 1014 strings) Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/pl/ --- locale/pl.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/locale/pl.po b/locale/pl.po index 9b20c513e7a09..66bf686097992 100644 --- a/locale/pl.po +++ b/locale/pl.po @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-07-23 09:07+0000\n" -"Last-Translator: MuskoM \n" +"PO-Revision-Date: 2025-08-03 08:01+0000\n" +"Last-Translator: MAE \n" "Language-Team: pl\n" "Language: pl\n" "MIME-Version: 1.0\n" @@ -90,8 +90,8 @@ msgstr "%%c wymaga int lub char" msgid "" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" msgstr "" -"%d pinów adresowalnych, %d pinów rgb oraz %d płytek wskazują na wysokość " -"%d, nie %d" +"%d pinów adresowalnych, %d pinów rgb oraz %d płytek wskazują na wysokość %d, " +"nie %d" #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" From 3aa680cf9c47f3399fa62a902b678a747c575fd2 Mon Sep 17 00:00:00 2001 From: Peggy Date: Mon, 4 Aug 2025 16:19:06 -0700 Subject: [PATCH 046/517] Add os.urandom module to ports/analog for MAX32690_apard I added a port-specific module, os.urandom() to ports/analog. Now users can generate random numbers from the TRNG hardware. The following code generates 3 random numbers and prints them in hex: import os print(os.urandom(3).hex()) Files I modiied: * ports/analog/Makefile: added INC and SRC for TRNG * ports/mpconfigport.mk: enabled CIRCUITPY_RANDOM * ports/analog/common-hal/os/__init__.c: implemented common_hal_os_urandom() * ports/analog/supervisor/port.c: initialized the TRNG object --- ports/analog/Makefile | 7 +++++-- ports/analog/common-hal/os/__init__.c | 7 ++++++- ports/analog/mpconfigport.mk | 2 +- ports/analog/supervisor/port.c | 12 ++++++++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ports/analog/Makefile b/ports/analog/Makefile index 08b245c5897e3..0d4d05f136804 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -83,7 +83,8 @@ INC += \ -I$(PERIPH_SRC)/ICC \ -I$(PERIPH_SRC)/TMR \ -I$(PERIPH_SRC)/RTC \ - -I$(PERIPH_SRC)/UART + -I$(PERIPH_SRC)/UART \ + -I$(PERIPH_SRC)/TRNG INC += -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/GCC @@ -116,7 +117,9 @@ SRC_MAX32 += \ $(PERIPH_SRC)/TMR/tmr_$(DIE_TYPE).c \ $(PERIPH_SRC)/UART/uart_common.c \ $(PERIPH_SRC)/UART/uart_$(DIE_TYPE).c \ - $(PERIPH_SRC)/UART/uart_revb.c + $(PERIPH_SRC)/UART/uart_revb.c \ + $(PERIPH_SRC)/TRNG/trng_revb.c \ + $(PERIPH_SRC)/TRNG/trng_$(DIE_TYPE).c SRC_C += $(SRC_MAX32) \ boards/$(BOARD)/board.c \ diff --git a/ports/analog/common-hal/os/__init__.c b/ports/analog/common-hal/os/__init__.c index 7b607cf6b3c4b..04667db8b5896 100644 --- a/ports/analog/common-hal/os/__init__.c +++ b/ports/analog/common-hal/os/__init__.c @@ -15,9 +15,14 @@ // #include "peripherals/periph.h" +// true random number generator, TRNG +#include "trng.h" + bool common_hal_os_urandom(uint8_t *buffer, uint32_t length) { #if (HAS_TRNG) - // todo (low prior): implement + // get a random number of "length" number of bytes + MXC_TRNG_Random(buffer, length); + return true; #else #endif return false; diff --git a/ports/analog/mpconfigport.mk b/ports/analog/mpconfigport.mk index 1729a284a3f47..221730106d269 100644 --- a/ports/analog/mpconfigport.mk +++ b/ports/analog/mpconfigport.mk @@ -51,7 +51,7 @@ CIRCUITPY_BITBANGIO ?= 1 # Requires Microcontroller CIRCUITPY_TOUCHIO ?= 1 # Requires OS -CIRCUITPY_RANDOM ?= 0 +CIRCUITPY_RANDOM ?= 1 # Requires busio.UART CIRCUITPY_CONSOLE_UART ?= 0 # Does nothing without I2C diff --git a/ports/analog/supervisor/port.c b/ports/analog/supervisor/port.c index ad003c12ed9e0..ff05ff2d28709 100644 --- a/ports/analog/supervisor/port.c +++ b/ports/analog/supervisor/port.c @@ -44,6 +44,9 @@ #include "mxc_delay.h" #include "rtc.h" +// true random number generator, TRNG +#include "trng.h" + // msec to RTC subsec ticks (4 kHz) /* Converts a time in milleseconds to equivalent RSSA register value */ #define MSEC_TO_SS_ALARM(x) (0 - ((x * 4096) / 1000)) @@ -112,9 +115,18 @@ safe_mode_t port_init(void) { } ; + // enable TRNG (true random number generator) + #ifdef CIRCUITPY_RANDOM + MXC_TRNG_Init(); + #endif + return SAFE_MODE_NONE; } +void TRNG_IRQHandler(void) { + MXC_TRNG_Handler(); +} + void RTC_IRQHandler(void) { // Read flags to clear int flags = MXC_RTC_GetFlags(); From 06f1ac33e02657a9fd33ad1b18a47b7be06615fc Mon Sep 17 00:00:00 2001 From: Peggy Date: Tue, 5 Aug 2025 11:34:20 -0700 Subject: [PATCH 047/517] update licenses --- ports/analog/Makefile | 1 + ports/analog/common-hal/os/__init__.c | 1 + ports/analog/mpconfigport.mk | 1 + 3 files changed, 3 insertions(+) diff --git a/ports/analog/Makefile b/ports/analog/Makefile index 0d4d05f136804..ce9082c5e255a 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -2,6 +2,7 @@ # # SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries # SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +# SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. # # SPDX-License-Identifier: MIT diff --git a/ports/analog/common-hal/os/__init__.c b/ports/analog/common-hal/os/__init__.c index 04667db8b5896..508b40798e22b 100644 --- a/ports/analog/common-hal/os/__init__.c +++ b/ports/analog/common-hal/os/__init__.c @@ -2,6 +2,7 @@ // // SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries // SPDX-FileCopyrightText: Copyright (c) 2019 Lucian Copeland for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. // // SPDX-License-Identifier: MIT diff --git a/ports/analog/mpconfigport.mk b/ports/analog/mpconfigport.mk index 221730106d269..67f1f79fb8121 100644 --- a/ports/analog/mpconfigport.mk +++ b/ports/analog/mpconfigport.mk @@ -2,6 +2,7 @@ # # SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries # SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +# SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. # # SPDX-License-Identifier: MIT From 2c83897670fdb0208869f09391ce16f351eb39bd Mon Sep 17 00:00:00 2001 From: MAE Date: Tue, 5 Aug 2025 19:39:51 +0200 Subject: [PATCH 048/517] Translated using Weblate (Polish) Currently translated at 52.4% (532 of 1014 strings) Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/pl/ --- locale/pl.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locale/pl.po b/locale/pl.po index 66bf686097992..2e55f983e0027 100644 --- a/locale/pl.po +++ b/locale/pl.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-08-03 08:01+0000\n" +"PO-Revision-Date: 2025-08-06 18:01+0000\n" "Last-Translator: MAE \n" "Language-Team: pl\n" "Language: pl\n" @@ -204,7 +204,7 @@ msgstr "" #: py/argcheck.c msgid "%q must be >= %d" -msgstr "" +msgstr "%q musi być >= %d" #: shared-bindings/analogbufio/BufferedIn.c msgid "%q must be a bytearray or array of type 'H' or 'B'" From c097441cb8fce14a9b44c1b00525d36d02b0bedd Mon Sep 17 00:00:00 2001 From: CDarius Date: Fri, 8 Aug 2025 16:59:49 +0200 Subject: [PATCH 049/517] Refactor/fix M5Bus pins definitions --- ports/espressif/boards/m5stack_cores3/pins.c | 24 ++++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/ports/espressif/boards/m5stack_cores3/pins.c b/ports/espressif/boards/m5stack_cores3/pins.c index 2ebb225277ded..223a6932c4c05 100644 --- a/ports/espressif/boards/m5stack_cores3/pins.c +++ b/ports/espressif/boards/m5stack_cores3/pins.c @@ -28,7 +28,7 @@ CIRCUITPY_BOARD_BUS_SINGLETON(porta_i2c, i2c, 1) static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - // M5 Bus (except I2S & PORT B) + // M5 Bus (except I2S) { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO35) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) }, @@ -36,33 +36,37 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_D44), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_PORTC_RX), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_A18), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) }, { MP_ROM_QSTR(MP_QSTR_PORTA_SDA), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO6) }, - { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_A8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_A9), MP_ROM_PTR(&pin_GPIO9) }, { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_D43), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_PORTC_TX), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_A17), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO11) }, { MP_ROM_QSTR(MP_QSTR_PORTA_SCL), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO7) }, - // Port B - { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, - // I2S { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_ROM_PTR(&pin_GPIO34) }, { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_ROM_PTR(&pin_GPIO33) }, - { MP_ROM_QSTR(MP_QSTR_IS2_DATA), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_IS2_DATA_OUT), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_IS2_DATA_IN), MP_ROM_PTR(&pin_GPIO14) }, { MP_ROM_QSTR(MP_QSTR_IS2_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, // Camera From 7dc07f5830cd2d52824b11adf8648e1de983f4e1 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Fri, 8 Aug 2025 20:50:45 +0000 Subject: [PATCH 050/517] update tinyusb submodule commit This is the latest upstream commit. There were many changes in the past the 3 months since the last submodule update. In particular, there were some fixes for descriptor parsing and better stability when devices are unplugged. See issue #10556. --- lib/tinyusb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tinyusb b/lib/tinyusb index 542e5b4550a01..5fb3c09963bca 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 542e5b4550a01d034b78308d77c408ed89427513 +Subproject commit 5fb3c09963bca362e535788e289d4b3518da5973 From b739a86fd5bcdddaf873470ffa86eab890ac82ca Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 8 Aug 2025 12:02:45 -0700 Subject: [PATCH 051/517] Refactor EPaperDisplay args into struct Claude helped with initial prompt: > Refactor common_hal_epaperdisplay_epaperdisplay_construct so that the current arguments are struct fields and then the function only takes the struct. Also add a macro that initializes it to default values. And one follow up prompt before minor manual cleanup and testing: > Clean up board files by removing settings that match the default. In particular bools that are false should be default, commands with NO_COMMAND and highlight color as 0. For issue #10528 --- ports/atmel-samd/boards/openbook_m4/board.c | 54 +++----- .../adafruit_magtag_2.9_grayscale/board.c | 115 +++++++--------- .../elecrow_crowpanel_4_2_epaper/board.c | 57 ++++---- .../boards/heltec_vision_master_e290/board.c | 64 +++++---- .../boards/heltec_wireless_paper/board.c | 56 +++----- ports/espressif/boards/sqfmi_watchy/board.c | 62 ++++----- .../bradanlanestudio_explorer_rp2040/board.c | 123 ++++++++---------- .../boards/pimoroni_badger2040/board.c | 55 +++----- .../boards/pimoroni_badger2040w/board.c | 55 +++----- .../boards/pimoroni_inky_frame_5_7/board.c | 54 +++----- .../boards/pimoroni_inky_frame_7_3/board.c | 55 +++----- shared-bindings/epaperdisplay/EPaperDisplay.c | 51 ++++++-- shared-bindings/epaperdisplay/EPaperDisplay.h | 89 +++++++++++-- shared-module/epaperdisplay/EPaperDisplay.c | 81 +++++------- 14 files changed, 452 insertions(+), 519 deletions(-) diff --git a/ports/atmel-samd/boards/openbook_m4/board.c b/ports/atmel-samd/boards/openbook_m4/board.c index 95027e817e8b8..fb75cac3a4ee7 100644 --- a/ports/atmel-samd/boards/openbook_m4/board.c +++ b/ports/atmel-samd/boards/openbook_m4/board.c @@ -53,42 +53,24 @@ void board_init(void) { epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct(display, - bus, - start_sequence, - sizeof(start_sequence), - 0, // start up time - stop_sequence, - sizeof(stop_sequence), - 300, // width - 400, // height - 300, // RAM width - 400, // RAM height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x13, // write_black_ram_command - false, // black_bits_inverted - NO_COMMAND, // write_color_ram_command (can add this for grayscale eventually) - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, // refresh_display_sequence - sizeof(refresh_sequence), - 40, // refresh_time - &pin_PA01, // busy_pin - false, // busy_state - 5, // seconds_per_frame - false, // chip_select (don't always toggle chip select) - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = start_sequence; + args.start_sequence_len = sizeof(start_sequence); + args.stop_sequence = stop_sequence; + args.stop_sequence_len = sizeof(stop_sequence); + args.width = 300; + args.height = 400; + args.ram_width = 300; + args.ram_height = 400; + args.rotation = 270; + args.write_black_ram_command = 0x13; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 40.0; + args.busy_pin = &pin_PA01; + args.seconds_per_frame = 5.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index c4311474f6061..d37233b7f2420 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -217,75 +217,54 @@ void board_init(void) { display->base.type = &epaperdisplay_epaperdisplay_type; if (is_ssd1680) { - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - ssd1680_display_start_sequence, sizeof(ssd1680_display_start_sequence), - 0, // start up time - ssd1680_display_stop_sequence, sizeof(ssd1680_display_stop_sequence), - 296, // width - 128, // height - 250, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - 0x44, // set_column_window_command - 0x45, // set_row_window_command - 0x4e, // set_current_column_command - 0x4f, // set_current_row_command - 0x24, // write_black_ram_command - false, // black_bits_inverted - 0x26, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - ssd1680_display_refresh_sequence, sizeof(ssd1680_display_refresh_sequence), - 1.0, // refresh_time - &pin_GPIO5, // busy_pin - true, // busy_state - 5.0, // seconds_per_frame - false, // always_toggle_chip_select - true, // grayscale - false, // acep - false, // spectra6 - true, // two_byte_sequence_length - true); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = ssd1680_display_start_sequence; + args.start_sequence_len = sizeof(ssd1680_display_start_sequence); + args.stop_sequence = ssd1680_display_stop_sequence; + args.stop_sequence_len = sizeof(ssd1680_display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 250; + args.ram_height = 296; + args.rotation = 270; + args.set_column_window_command = 0x44; + args.set_row_window_command = 0x45; + args.set_current_column_command = 0x4e; + args.set_current_row_command = 0x4f; + args.write_black_ram_command = 0x24; + args.write_color_ram_command = 0x26; + args.refresh_sequence = ssd1680_display_refresh_sequence; + args.refresh_sequence_len = sizeof(ssd1680_display_refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO5; + args.busy_state = true; + args.seconds_per_frame = 5.0; + args.grayscale = true; + args.two_byte_sequence_length = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } else { - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - il0373_display_start_sequence, sizeof(il0373_display_start_sequence), - 0, // start up time - il0373_display_stop_sequence, sizeof(il0373_display_stop_sequence), - 296, // width - 128, // height - 160, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x10, // write_black_ram_command - false, // black_bits_inverted - 0x13, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - il0373_display_refresh_sequence, sizeof(il0373_display_refresh_sequence), - 1.0, // refresh_time - &pin_GPIO5, // busy_pin - false, // busy_state - 5.0, // seconds_per_frame - false, // always_toggle_chip_select - true, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = il0373_display_start_sequence; + args.start_sequence_len = sizeof(il0373_display_start_sequence); + args.stop_sequence = il0373_display_stop_sequence; + args.stop_sequence_len = sizeof(il0373_display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 160; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = 0x10; + args.write_color_ram_command = 0x13; + args.refresh_sequence = il0373_display_refresh_sequence; + args.refresh_sequence_len = sizeof(il0373_display_refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO5; + args.seconds_per_frame = 5.0; + args.grayscale = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } } diff --git a/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c b/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c index b7f8bc50e714a..04f5f92f85ba9 100644 --- a/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c +++ b/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c @@ -63,43 +63,30 @@ void board_init(void) { 0, // Polarity 0); // Phase -// Set up the DisplayIO epaper object + // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 1, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 400, // width - 300, // height - 400, // ram_width - 300, // ram_height - 0, // colstart - 0, // rowstart - 0, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x24, // write_black_ram_command - false, // black_bits_inverted - 0x26, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO48, // busy_pin - true, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.start_up_time = 1.0; + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 400; + args.height = 300; + args.ram_width = 400; + args.ram_height = 300; + args.write_black_ram_command = 0x24; + args.write_color_ram_command = 0x26; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO48; + args.busy_state = true; + args.seconds_per_frame = 2.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/espressif/boards/heltec_vision_master_e290/board.c b/ports/espressif/boards/heltec_vision_master_e290/board.c index 5911506ae2c3c..9749f5c704881 100644 --- a/ports/espressif/boards/heltec_vision_master_e290/board.c +++ b/ports/espressif/boards/heltec_vision_master_e290/board.c @@ -61,43 +61,37 @@ void board_init(void) { 0, // Polarity 0); // Phase -// Set up the DisplayIO epaper object + // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 296, // width - 128, // height - 250, // ram_width - 296, // ram_height - 8, // colstart - 0, // rowstart - 90, // rotation - 0x44, // set_column_window_command - 0x45, // set_row_window_command - 0x4E, // set_current_column_command - 0x4F, // set_current_row_command - 0x24, // write_black_ram_command - false, // black_bits_inverted - 0x26, // write_color_ram_command - false, // color_bits_inverted - 0xFF0000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO6, // busy_pin - true, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - true); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 250; + args.ram_height = 296; + args.colstart = 8; + args.rotation = 90; + args.set_column_window_command = 0x44; + args.set_row_window_command = 0x45; + args.set_current_column_command = 0x4E; + args.set_current_row_command = 0x4F; + args.write_black_ram_command = 0x24; + args.write_color_ram_command = 0x26; + args.highlight_color = 0xFF0000; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO6; + args.busy_state = true; + args.seconds_per_frame = 2.0; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/espressif/boards/heltec_wireless_paper/board.c b/ports/espressif/boards/heltec_wireless_paper/board.c index 89e7a054b50c2..c796a9be8843d 100644 --- a/ports/espressif/boards/heltec_wireless_paper/board.c +++ b/ports/espressif/boards/heltec_wireless_paper/board.c @@ -101,43 +101,29 @@ void board_init(void) { 0, // Polarity 0); // Phase -// Set up the DisplayIO epaper object + // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 250, // width - 122, // height - 128, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x13, // write_black_ram_command - false, // black_bits_inverted - 0x10, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO7, // busy_pin - false, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 250; + args.height = 122; + args.ram_width = 128; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = 0x13; + args.write_color_ram_command = 0x10; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO7; + args.seconds_per_frame = 2.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/espressif/boards/sqfmi_watchy/board.c b/ports/espressif/boards/sqfmi_watchy/board.c index 535a3e11006fe..297190b53e200 100644 --- a/ports/espressif/boards/sqfmi_watchy/board.c +++ b/ports/espressif/boards/sqfmi_watchy/board.c @@ -160,41 +160,33 @@ void board_init(void) { epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - (double)0.01, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 200, // width - 200, // height - 200, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 0, // rotation - 0x44, // set_column_window_command - 0x45, // set_row_window_command - 0x4E, // set_current_column_command - 0x4F, // set_current_row_command - 0x24, // write_black_ram_command - true, // black_bits_inverted - 0x26, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, sizeof(refresh_sequence), - (double)1.0, // refresh_time - &pin_GPIO19, // busy_pin - true, // busy_state - (double)5.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - true // address_little_endian - ); + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.start_up_time = 0.01; + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 200; + args.height = 200; + args.ram_width = 200; + args.ram_height = 296; + args.set_column_window_command = 0x44; + args.set_row_window_command = 0x45; + args.set_current_column_command = 0x4E; + args.set_current_row_command = 0x4F; + args.write_black_ram_command = 0x24; + args.black_bits_inverted = true; + args.write_color_ram_command = 0x26; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO19; + args.busy_state = true; + args.seconds_per_frame = 5.0; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } bool board_requests_safe_mode(void) { diff --git a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c index b7e75a475fd09..35e128627bc0b 100644 --- a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c +++ b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c @@ -251,75 +251,62 @@ void board_init(void) { if ((vid_setting == 1) || // DCNextGen SSD1681 BWR rotated 270 (vid_setting == 3) || // Explorer SSD1681 BW rotated 0 (vid_setting == 4)) { // Explorer SSD1681 BWR rotated 0 - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - _start_sequence_ssd1681, sizeof(_start_sequence_ssd1681), - 1.0, // start up time - _stop_sequence_ssd1681, sizeof(_stop_sequence_ssd1681), - WIDTH, // width - HEIGHT, // height - WIDTH, // ram_width - HEIGHT + 0x60, // ram_height RAM is actually only 200 bits high but we use 296 to match the 9 bits - 0, // colstart - 0, // rowstart - rotation, // rotation - SSD_SET_RAMXPOS, // set_column_window_command - SSD_SET_RAMYPOS, // set_row_window_command - SSD_SET_RAMXCOUNT, // set_current_column_command - SSD_SET_RAMYCOUNT, // set_current_row_command - SSD_WRITE_RAM_BLK, // write_black_ram_command - false, // black_bits_inverted - SSD_WRITE_RAM_RED, // write_color_ram_command - false, // color_bits_inverted - 0xFF0000, // highlight_color (RED for tri-color display) - 0x000000, // highlight_color2 - _refresh_sequence_ssd1681, sizeof(_refresh_sequence_ssd1681), // refresh_display_command - refresh_time, // refresh_time - &pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin - true, // busy_state - seconds_per_frame, // seconds_per_frame (does not seem the user can change this) - true, // always_toggle_chip_select - false, // not grayscale - false, // not acep - false, // not spectra6 - false, // not two_byte_sequence_length - true); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = _start_sequence_ssd1681; + args.start_sequence_len = sizeof(_start_sequence_ssd1681); + args.start_up_time = 1.0; + args.stop_sequence = _stop_sequence_ssd1681; + args.stop_sequence_len = sizeof(_stop_sequence_ssd1681); + args.width = WIDTH; + args.height = HEIGHT; + args.ram_width = WIDTH; + args.ram_height = HEIGHT + 0x60; + args.rotation = rotation; + args.set_column_window_command = SSD_SET_RAMXPOS; + args.set_row_window_command = SSD_SET_RAMYPOS; + args.set_current_column_command = SSD_SET_RAMXCOUNT; + args.set_current_row_command = SSD_SET_RAMYCOUNT; + args.write_black_ram_command = SSD_WRITE_RAM_BLK; + args.write_color_ram_command = SSD_WRITE_RAM_RED; + args.highlight_color = 0xFF0000; + args.refresh_sequence = _refresh_sequence_ssd1681; + args.refresh_sequence_len = sizeof(_refresh_sequence_ssd1681); + args.refresh_time = refresh_time; + args.busy_pin = &pin_GPIO9; + args.busy_state = true; + args.seconds_per_frame = seconds_per_frame; + args.always_toggle_chip_select = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } else if (vid_setting == 2) { // Explorer SSD1608 BW - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - _start_sequence_ssd1608, sizeof(_start_sequence_ssd1608), - 1.0, // start up time - _stop_sequence_ssd1608, sizeof(_stop_sequence_ssd1608), - WIDTH, // width - HEIGHT, // height - WIDTH, // ram_width - HEIGHT /* + 0x60 */, // ram_height RAM is actually only 200 bits high but we use 296 to match the 9 bits - 0, // colstart - 0, // rowstart - rotation, // rotation - SSD_SET_RAMXPOS, // set_column_window_command - SSD_SET_RAMYPOS, // set_row_window_command - SSD_SET_RAMXCOUNT, // set_current_column_command - SSD_SET_RAMYCOUNT, // set_current_row_command - SSD_WRITE_RAM_BLK, // write_black_ram_command - false, // black_bits_inverted - NO_COMMAND, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color (RED for tri-color display) - 0x000000, // highlight_color2 - _refresh_sequence_ssd1608, sizeof(_refresh_sequence_ssd1608), // refresh_display_command - refresh_time, // refresh_time - &pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin - true, // busy_state - seconds_per_frame, // seconds_per_frame (does not seem the user can change this) - true, // always_toggle_chip_select - false, // not grayscale - false, // not acep - false, // not spectra6 - false, // not two_byte_sequence_length - true); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = _start_sequence_ssd1608; + args.start_sequence_len = sizeof(_start_sequence_ssd1608); + args.start_up_time = 1.0; + args.stop_sequence = _stop_sequence_ssd1608; + args.stop_sequence_len = sizeof(_stop_sequence_ssd1608); + args.width = WIDTH; + args.height = HEIGHT; + args.ram_width = WIDTH; + args.ram_height = HEIGHT; + args.rotation = rotation; + args.set_column_window_command = SSD_SET_RAMXPOS; + args.set_row_window_command = SSD_SET_RAMYPOS; + args.set_current_column_command = SSD_SET_RAMXCOUNT; + args.set_current_row_command = SSD_SET_RAMYCOUNT; + args.write_black_ram_command = SSD_WRITE_RAM_BLK; + args.write_color_ram_command = NO_COMMAND; + args.refresh_sequence = _refresh_sequence_ssd1608; + args.refresh_sequence_len = sizeof(_refresh_sequence_ssd1608); + args.refresh_time = refresh_time; + args.busy_pin = &pin_GPIO9; + args.busy_state = true; + args.seconds_per_frame = seconds_per_frame; + args.always_toggle_chip_select = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } else { // what should happen if this firmware is installed on some other board? // currently, we mark the display as None diff --git a/ports/raspberrypi/boards/pimoroni_badger2040/board.c b/ports/raspberrypi/boards/pimoroni_badger2040/board.c index 392a57cdc0c1b..07cc512b9a58e 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2040/board.c @@ -273,40 +273,27 @@ void board_init(void) { // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 296, // width - 128, // height - 160, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - DTM2, // write_black_ram_command - true, // black_bits_inverted - DTM1, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO26, // busy_pin - false, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 160; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = DTM2; + args.black_bits_inverted = true; + args.write_color_ram_command = DTM1; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO26; + args.seconds_per_frame = 2.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/raspberrypi/boards/pimoroni_badger2040w/board.c b/ports/raspberrypi/boards/pimoroni_badger2040w/board.c index 01c448cd7ad52..b992bcd6a1e37 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040w/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2040w/board.c @@ -273,40 +273,27 @@ void board_init(void) { // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 296, // width - 128, // height - 160, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - DTM2, // write_black_ram_command - true, // black_bits_inverted - DTM1, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO26, // busy_pin - false, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 160; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = DTM2; + args.black_bits_inverted = true; + args.write_color_ram_command = DTM1; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO26; + args.seconds_per_frame = 2.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c index c7901b8649d78..4bf7518c6a8df 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c @@ -67,40 +67,26 @@ void board_init(void) { epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 1.0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 600, // width - 448, // height - 640, // ram_width - 480, // ram_height - 0, // colstart - 0, // rowstart - 0, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x10, // write_black_ram_command - false, // black_bits_inverted - NO_COMMAND, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, sizeof(refresh_sequence), - 28.0, // refresh_time - NULL, // busy_pin - false, // busy_state - 40.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - true, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.start_up_time = 1.0; + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 600; + args.height = 448; + args.ram_width = 640; + args.ram_height = 480; + args.write_black_ram_command = 0x10; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 28.0; + args.busy_pin = NULL; + args.seconds_per_frame = 40.0; + args.acep = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c index 764c85ecb6bcf..763a91a255569 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c @@ -126,40 +126,27 @@ void board_init(void) { epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 1.0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 800, // width - 480, // height - 800, // ram_width - 480, // ram_height - 0, // colstart - 0, // rowstart - 180, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x10, // write_black_ram_command - false, // black_bits_inverted - NO_COMMAND, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - 0x000000, // highlight_color2 - refresh_sequence, sizeof(refresh_sequence), - 28.0, // refresh_time - NULL, // busy_pin - false, // busy_state - 30.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - true, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.start_up_time = 1.0; + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 800; + args.height = 480; + args.ram_width = 800; + args.ram_height = 480; + args.rotation = 180; + args.write_black_ram_command = 0x10; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 28.0; + args.busy_pin = NULL; + args.seconds_per_frame = 30.0; + args.acep = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/shared-bindings/epaperdisplay/EPaperDisplay.c b/shared-bindings/epaperdisplay/EPaperDisplay.c index 9f261d599ada9..cd9c18e27d526 100644 --- a/shared-bindings/epaperdisplay/EPaperDisplay.c +++ b/shared-bindings/epaperdisplay/EPaperDisplay.c @@ -211,20 +211,43 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, } self->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - self, - display_bus, - start_bufinfo.buf, start_bufinfo.len, start_up_time, stop_bufinfo.buf, stop_bufinfo.len, - args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_ram_width].u_int, args[ARG_ram_height].u_int, - args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation, - args[ARG_set_column_window_command].u_int, args[ARG_set_row_window_command].u_int, - args[ARG_set_current_column_command].u_int, args[ARG_set_current_row_command].u_int, - args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, - args[ARG_color_bits_inverted].u_bool, highlight_color, highlight_color2, refresh_buf, refresh_buf_len, refresh_time, - busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame, - args[ARG_always_toggle_chip_select].u_bool, args[ARG_grayscale].u_bool, args[ARG_advanced_color_epaper].u_bool, args[ARG_spectra6].u_bool, - two_byte_sequence_length, args[ARG_address_little_endian].u_bool - ); + epaperdisplay_construct_args_t construct_args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + construct_args.bus = display_bus; + construct_args.start_sequence = start_bufinfo.buf; + construct_args.start_sequence_len = start_bufinfo.len; + construct_args.start_up_time = start_up_time; + construct_args.stop_sequence = stop_bufinfo.buf; + construct_args.stop_sequence_len = stop_bufinfo.len; + construct_args.width = args[ARG_width].u_int; + construct_args.height = args[ARG_height].u_int; + construct_args.ram_width = args[ARG_ram_width].u_int; + construct_args.ram_height = args[ARG_ram_height].u_int; + construct_args.colstart = args[ARG_colstart].u_int; + construct_args.rowstart = args[ARG_rowstart].u_int; + construct_args.rotation = rotation; + construct_args.set_column_window_command = args[ARG_set_column_window_command].u_int; + construct_args.set_row_window_command = args[ARG_set_row_window_command].u_int; + construct_args.set_current_column_command = args[ARG_set_current_column_command].u_int; + construct_args.set_current_row_command = args[ARG_set_current_row_command].u_int; + construct_args.write_black_ram_command = args[ARG_write_black_ram_command].u_int; + construct_args.black_bits_inverted = args[ARG_black_bits_inverted].u_bool; + construct_args.write_color_ram_command = write_color_ram_command; + construct_args.color_bits_inverted = args[ARG_color_bits_inverted].u_bool; + construct_args.highlight_color = highlight_color; + construct_args.highlight_color2 = highlight_color2; + construct_args.refresh_sequence = refresh_buf; + construct_args.refresh_sequence_len = refresh_buf_len; + construct_args.refresh_time = refresh_time; + construct_args.busy_pin = busy_pin; + construct_args.busy_state = args[ARG_busy_state].u_bool; + construct_args.seconds_per_frame = seconds_per_frame; + construct_args.always_toggle_chip_select = args[ARG_always_toggle_chip_select].u_bool; + construct_args.grayscale = args[ARG_grayscale].u_bool; + construct_args.acep = args[ARG_advanced_color_epaper].u_bool; + construct_args.spectra6 = args[ARG_spectra6].u_bool; + construct_args.two_byte_sequence_length = two_byte_sequence_length; + construct_args.address_little_endian = args[ARG_address_little_endian].u_bool; + common_hal_epaperdisplay_epaperdisplay_construct(self, &construct_args); return self; } diff --git a/shared-bindings/epaperdisplay/EPaperDisplay.h b/shared-bindings/epaperdisplay/EPaperDisplay.h index afa90a514e2a5..83f0b0377fe6c 100644 --- a/shared-bindings/epaperdisplay/EPaperDisplay.h +++ b/shared-bindings/epaperdisplay/EPaperDisplay.h @@ -15,19 +15,84 @@ extern const mp_obj_type_t epaperdisplay_epaperdisplay_type; #define NO_COMMAND 0x100 +typedef struct { + mp_obj_t bus; + const uint8_t *start_sequence; + uint16_t start_sequence_len; + mp_float_t start_up_time; + const uint8_t *stop_sequence; + uint16_t stop_sequence_len; + uint16_t width; + uint16_t height; + uint16_t ram_width; + uint16_t ram_height; + int16_t colstart; + int16_t rowstart; + uint16_t rotation; + uint16_t set_column_window_command; + uint16_t set_row_window_command; + uint16_t set_current_column_command; + uint16_t set_current_row_command; + uint16_t write_black_ram_command; + bool black_bits_inverted; + uint16_t write_color_ram_command; + bool color_bits_inverted; + uint32_t highlight_color; + uint32_t highlight_color2; + const uint8_t *refresh_sequence; + uint16_t refresh_sequence_len; + mp_float_t refresh_time; + const mcu_pin_obj_t *busy_pin; + bool busy_state; + mp_float_t seconds_per_frame; + bool always_toggle_chip_select; + bool grayscale; + bool acep; + bool spectra6; + bool two_byte_sequence_length; + bool address_little_endian; +} epaperdisplay_construct_args_t; + +#define EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS { \ + .bus = mp_const_none, \ + .start_sequence = NULL, \ + .start_sequence_len = 0, \ + .start_up_time = 0.0, \ + .stop_sequence = NULL, \ + .stop_sequence_len = 0, \ + .width = 0, \ + .height = 0, \ + .ram_width = 0, \ + .ram_height = 0, \ + .colstart = 0, \ + .rowstart = 0, \ + .rotation = 0, \ + .set_column_window_command = NO_COMMAND, \ + .set_row_window_command = NO_COMMAND, \ + .set_current_column_command = NO_COMMAND, \ + .set_current_row_command = NO_COMMAND, \ + .write_black_ram_command = NO_COMMAND, \ + .black_bits_inverted = false, \ + .write_color_ram_command = NO_COMMAND, \ + .color_bits_inverted = false, \ + .highlight_color = 0x000000, \ + .highlight_color2 = 0x000000, \ + .refresh_sequence = NULL, \ + .refresh_sequence_len = 0, \ + .refresh_time = 0.0, \ + .busy_pin = NULL, \ + .busy_state = false, \ + .seconds_per_frame = 0.0, \ + .always_toggle_chip_select = false, \ + .grayscale = false, \ + .acep = false, \ + .spectra6 = false, \ + .two_byte_sequence_length = false, \ + .address_little_endian = false \ +} + void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdisplay_obj_t *self, - mp_obj_t bus, const uint8_t *start_sequence, uint16_t start_sequence_len, mp_float_t start_up_time, - const uint8_t *stop_sequence, uint16_t stop_sequence_len, - uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, - int16_t colstart, int16_t rowstart, uint16_t rotation, - uint16_t set_column_window_command, uint16_t set_row_window_command, - uint16_t set_current_column_command, uint16_t set_current_row_command, - uint16_t write_black_ram_command, bool black_bits_inverted, - uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint32_t highlight_color2, - const uint8_t *refresh_sequence, uint16_t refresh_sequence_len, mp_float_t refresh_time, - const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, - bool always_toggle_chip_select, bool grayscale, bool acep, bool spectra6, bool two_byte_sequence_length, - bool address_little_endian); + const epaperdisplay_construct_args_t *args); bool common_hal_epaperdisplay_epaperdisplay_refresh(epaperdisplay_epaperdisplay_obj_t *self); diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index e1f0a07f467ee..556db6ae22002 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -26,82 +26,73 @@ #define DELAY 0x80 void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdisplay_obj_t *self, - mp_obj_t bus, const uint8_t *start_sequence, uint16_t start_sequence_len, mp_float_t start_up_time, - const uint8_t *stop_sequence, uint16_t stop_sequence_len, - uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, - int16_t colstart, int16_t rowstart, uint16_t rotation, - uint16_t set_column_window_command, uint16_t set_row_window_command, - uint16_t set_current_column_command, uint16_t set_current_row_command, - uint16_t write_black_ram_command, bool black_bits_inverted, - uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint32_t highlight_color2, - const uint8_t *refresh_sequence, uint16_t refresh_sequence_len, mp_float_t refresh_time, - const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, - bool chip_select, bool grayscale, bool acep, bool spectra6, bool two_byte_sequence_length, bool address_little_endian) { + const epaperdisplay_construct_args_t *args) { uint16_t color_depth = 1; bool core_grayscale = true; - if (highlight_color != 0x000000) { + if (args->highlight_color != 0x000000) { self->core.colorspace.tricolor = true; - self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color); + self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(args->highlight_color); } else { self->core.colorspace.tricolor = false; } - if (highlight_color != 0x000000 && highlight_color2 != 0x000000) { + if (args->highlight_color != 0x000000 && args->highlight_color2 != 0x000000) { self->core.colorspace.tricolor = false; self->core.colorspace.fourcolor = true; - self->core.colorspace.fourcolor_hue = displayio_colorconverter_compute_hue(highlight_color2); + self->core.colorspace.fourcolor_hue = displayio_colorconverter_compute_hue(args->highlight_color2); } else { self->core.colorspace.fourcolor = false; } - self->acep = acep || spectra6; - self->core.colorspace.sixcolor = spectra6; - self->core.colorspace.sevencolor = acep; + self->acep = args->acep || args->spectra6; + self->core.colorspace.sixcolor = args->spectra6; + self->core.colorspace.sevencolor = args->acep; + bool grayscale = args->grayscale; if (self->acep) { color_depth = 4; // bits. 7 colors + clean grayscale = false; core_grayscale = false; } - if ((highlight_color != 0x000000 || highlight_color2 != 0x000000) && write_color_ram_command == NO_COMMAND) { + if ((args->highlight_color != 0x000000 || args->highlight_color2 != 0x000000) && args->write_color_ram_command == NO_COMMAND) { color_depth = 2; core_grayscale = false; grayscale = false; } - displayio_display_core_construct(&self->core, width, height, rotation, color_depth, core_grayscale, true, 1, true, true); - displayio_display_bus_construct(&self->bus, bus, ram_width, ram_height, - colstart, rowstart, - set_column_window_command, set_row_window_command, set_current_column_command, set_current_row_command, - false /* data_as_commands */, chip_select, - false /* SH1107_addressing */, address_little_endian); + displayio_display_core_construct(&self->core, args->width, args->height, args->rotation, color_depth, core_grayscale, true, 1, true, true); + displayio_display_bus_construct(&self->bus, args->bus, args->ram_width, args->ram_height, + args->colstart, args->rowstart, + args->set_column_window_command, args->set_row_window_command, args->set_current_column_command, args->set_current_row_command, + false /* data_as_commands */, args->always_toggle_chip_select, + false /* SH1107_addressing */, args->address_little_endian); - self->write_black_ram_command = write_black_ram_command; - self->black_bits_inverted = black_bits_inverted; - self->write_color_ram_command = write_color_ram_command; - self->color_bits_inverted = color_bits_inverted; - self->refresh_time = refresh_time * 1000; - self->busy_state = busy_state; + self->write_black_ram_command = args->write_black_ram_command; + self->black_bits_inverted = args->black_bits_inverted; + self->write_color_ram_command = args->write_color_ram_command; + self->color_bits_inverted = args->color_bits_inverted; + self->refresh_time = args->refresh_time * 1000; + self->busy_state = args->busy_state; self->refreshing = false; - self->milliseconds_per_frame = seconds_per_frame * 1000; - self->chip_select = chip_select ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED; + self->milliseconds_per_frame = args->seconds_per_frame * 1000; + self->chip_select = args->always_toggle_chip_select ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED; self->grayscale = grayscale; - self->start_sequence = start_sequence; - self->start_sequence_len = start_sequence_len; - self->start_up_time_ms = start_up_time * 1000; - self->stop_sequence = stop_sequence; - self->stop_sequence_len = stop_sequence_len; - self->refresh_sequence = refresh_sequence; - self->refresh_sequence_len = refresh_sequence_len; + self->start_sequence = args->start_sequence; + self->start_sequence_len = args->start_sequence_len; + self->start_up_time_ms = args->start_up_time * 1000; + self->stop_sequence = args->stop_sequence; + self->stop_sequence_len = args->stop_sequence_len; + self->refresh_sequence = args->refresh_sequence; + self->refresh_sequence_len = args->refresh_sequence_len; self->busy.base.type = &mp_type_NoneType; - self->two_byte_sequence_length = two_byte_sequence_length; - if (busy_pin != NULL) { + self->two_byte_sequence_length = args->two_byte_sequence_length; + if (args->busy_pin != NULL) { self->busy.base.type = &digitalio_digitalinout_type; - common_hal_digitalio_digitalinout_construct(&self->busy, busy_pin); - common_hal_never_reset_pin(busy_pin); + common_hal_digitalio_digitalinout_construct(&self->busy, args->busy_pin); + common_hal_never_reset_pin(args->busy_pin); } // Clear the color memory if it isn't in use. - if (highlight_color == 0x00 && highlight_color2 == 0x00 && write_color_ram_command != NO_COMMAND) { + if (args->highlight_color == 0x00 && args->highlight_color2 == 0x00 && args->write_color_ram_command != NO_COMMAND) { // TODO: Clear } From d7b467fd2df9809d96710bce1644c829a3db390d Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Tue, 12 Aug 2025 04:36:44 +0000 Subject: [PATCH 052/517] Update Pico-PIO-USB to latest (0.7.2) (as suggested by tannewt in issue #10556) --- ports/raspberrypi/lib/Pico-PIO-USB | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/lib/Pico-PIO-USB b/ports/raspberrypi/lib/Pico-PIO-USB index 032a469e79f6a..675543bcc9baa 160000 --- a/ports/raspberrypi/lib/Pico-PIO-USB +++ b/ports/raspberrypi/lib/Pico-PIO-USB @@ -1 +1 @@ -Subproject commit 032a469e79f6a4ba40760d7868e6db26e15002d7 +Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f From 592d3f705eb7c525dc963d8a891ed8b26d3675d4 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Tue, 12 Aug 2025 04:48:20 +0000 Subject: [PATCH 053/517] Turn off SAMD21 options to reduce image size As suggested by tannewt in PR #10559, this turns off codeop for Feather M0 and errno for the Metro M0 and Redboard Turbo. This is trying to free up enough space to fit the latest TinyUSB and Pico-PIO-USB submodule updates. --- ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk | 1 + ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk | 1 + ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk | 1 + 3 files changed, 3 insertions(+) diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk index 004181148415a..783e2cb565783 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk @@ -10,4 +10,5 @@ SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C, W25Q16JVxQ" LONGINT_IMPL = MPZ +CIRCUITPY_CODEOP = 0 CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk index d758d6878fb7f..d4801359cf8c5 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk @@ -11,4 +11,5 @@ EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C, W25Q16JVxQ" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 +CIRCUITPY_ERRNO = 0 CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk b/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk index 64049e8d5981c..b34dfb3d2e624 100755 --- a/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk +++ b/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk @@ -11,4 +11,5 @@ EXTERNAL_FLASH_DEVICES = "W25Q32FV" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 +CIRCUITPY_ERRNO = 0 CIRCUITPY_RAINBOWIO = 0 From c3e38623758cf53f2755d7011a285da15c5c4c09 Mon Sep 17 00:00:00 2001 From: CDarius Date: Tue, 12 Aug 2025 12:06:00 +0200 Subject: [PATCH 054/517] Fixed I2S spelling and separated pin aliases --- ports/espressif/boards/m5stack_cores3/pins.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/ports/espressif/boards/m5stack_cores3/pins.c b/ports/espressif/boards/m5stack_cores3/pins.c index 223a6932c4c05..8b40d9e84842a 100644 --- a/ports/espressif/boards/m5stack_cores3/pins.c +++ b/ports/espressif/boards/m5stack_cores3/pins.c @@ -32,42 +32,55 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO35) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_D44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_PORTC_RX), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_A18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_SDA), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, { MP_ROM_QSTR(MP_QSTR_A8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, { MP_ROM_QSTR(MP_QSTR_A9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_D43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_PORTC_TX), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_A17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_SCL), MP_ROM_PTR(&pin_GPIO1) }, { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO7) }, // I2S { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_ROM_PTR(&pin_GPIO34) }, { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_ROM_PTR(&pin_GPIO33) }, - { MP_ROM_QSTR(MP_QSTR_IS2_DATA_OUT), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_IS2_DATA_IN), MP_ROM_PTR(&pin_GPIO14) }, - { MP_ROM_QSTR(MP_QSTR_IS2_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_OUT), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_IN), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, // Camera { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA), MP_ROM_PTR(&camera_data_tuple) }, From 57c9797b3f32f521ea2dc22dd498fc73e336b7a5 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 12 Aug 2025 09:17:03 -0500 Subject: [PATCH 055/517] Add tempo property --- shared-bindings/synthio/MidiTrack.c | 22 ++++++++++++++++++++++ shared-bindings/synthio/MidiTrack.h | 3 +++ shared-module/synthio/MidiTrack.c | 9 +++++++++ 3 files changed, 34 insertions(+) diff --git a/shared-bindings/synthio/MidiTrack.c b/shared-bindings/synthio/MidiTrack.c index 9add8f1745c83..db75a543c613d 100644 --- a/shared-bindings/synthio/MidiTrack.c +++ b/shared-bindings/synthio/MidiTrack.c @@ -115,6 +115,27 @@ static void check_for_deinit(synthio_miditrack_obj_t *self) { //| """32 bit value that tells how quickly samples are played in Hertz (cycles per second).""" //| +//| tempo: int +//| """Tempo of the streamed events, in MIDI ticks per second.""" +//| +static mp_obj_t synthio_miditrack_obj_get_tempo(mp_obj_t self_in) { + synthio_miditrack_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_int(common_hal_synthio_miditrack_get_tempo(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_miditrack_get_tempo_obj, synthio_miditrack_obj_get_tempo); + +static mp_obj_t synthio_miditrack_obj_set_tempo(mp_obj_t self_in, mp_obj_t arg) { + synthio_miditrack_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_synthio_miditrack_set_tempo(self, mp_obj_get_int(arg)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(synthio_miditrack_set_tempo_obj, synthio_miditrack_obj_set_tempo); +MP_PROPERTY_GETSET(synthio_miditrack_tempo_obj, + (mp_obj_t)&synthio_miditrack_get_tempo_obj, + (mp_obj_t)&synthio_miditrack_set_tempo_obj); + //| error_location: Optional[int] //| """Offset, in bytes within the midi data, of a decoding error""" //| @@ -141,6 +162,7 @@ static const mp_rom_map_elem_t synthio_miditrack_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_error_location), MP_ROM_PTR(&synthio_miditrack_error_location_obj) }, + { MP_ROM_QSTR(MP_QSTR_tempo), MP_ROM_PTR(&synthio_miditrack_tempo_obj) }, AUDIOSAMPLE_FIELDS, }; static MP_DEFINE_CONST_DICT(synthio_miditrack_locals_dict, synthio_miditrack_locals_dict_table); diff --git a/shared-bindings/synthio/MidiTrack.h b/shared-bindings/synthio/MidiTrack.h index 72b217e9613b1..cb154d3996b1a 100644 --- a/shared-bindings/synthio/MidiTrack.h +++ b/shared-bindings/synthio/MidiTrack.h @@ -15,3 +15,6 @@ void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self, const void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self); mp_int_t common_hal_synthio_miditrack_get_error_location(synthio_miditrack_obj_t *self); + +mp_int_t common_hal_synthio_miditrack_get_tempo(synthio_miditrack_obj_t *self); +void common_hal_synthio_miditrack_set_tempo(synthio_miditrack_obj_t *self, mp_int_t value); diff --git a/shared-module/synthio/MidiTrack.c b/shared-module/synthio/MidiTrack.c index 94430289bda10..df8e5bdbea1b2 100644 --- a/shared-module/synthio/MidiTrack.c +++ b/shared-module/synthio/MidiTrack.c @@ -116,6 +116,15 @@ mp_int_t common_hal_synthio_miditrack_get_error_location(synthio_miditrack_obj_t return self->error_location; } +mp_int_t common_hal_synthio_miditrack_get_tempo(synthio_miditrack_obj_t *self) { + return self->tempo; +} + +void common_hal_synthio_miditrack_set_tempo(synthio_miditrack_obj_t *self, mp_int_t value) { + mp_int_t val = mp_arg_validate_int_min(value, 1, MP_QSTR_tempo); + self->tempo = val; +} + void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self, bool single_channel_output, uint8_t channel) { synthio_synth_reset_buffer(&self->synth, single_channel_output, channel); From d2867b4607f582e151e5e4acd3446fda7c1ad5fd Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 19 Aug 2025 13:58:15 -0500 Subject: [PATCH 056/517] update frozen libraries --- frozen/Adafruit_CircuitPython_FakeRequests | 2 +- frozen/Adafruit_CircuitPython_HTTPServer | 2 +- frozen/Adafruit_CircuitPython_LED_Animation | 2 +- frozen/Adafruit_CircuitPython_PortalBase | 2 +- frozen/Adafruit_CircuitPython_SSD1680 | 2 +- frozen/Adafruit_CircuitPython_UC8151D | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_FakeRequests b/frozen/Adafruit_CircuitPython_FakeRequests index aa034280ebfed..020121e90c630 160000 --- a/frozen/Adafruit_CircuitPython_FakeRequests +++ b/frozen/Adafruit_CircuitPython_FakeRequests @@ -1 +1 @@ -Subproject commit aa034280ebfed80c245827ff1d49a098ace64b03 +Subproject commit 020121e90c6306147f91b8079b75f3d14ff86138 diff --git a/frozen/Adafruit_CircuitPython_HTTPServer b/frozen/Adafruit_CircuitPython_HTTPServer index b70106b17bbfa..c43147a016ffd 160000 --- a/frozen/Adafruit_CircuitPython_HTTPServer +++ b/frozen/Adafruit_CircuitPython_HTTPServer @@ -1 +1 @@ -Subproject commit b70106b17bbfa0070f8573e1e06e384d9d4577de +Subproject commit c43147a016ffd13c57a0923730bc6a83afefb4ad diff --git a/frozen/Adafruit_CircuitPython_LED_Animation b/frozen/Adafruit_CircuitPython_LED_Animation index 5d13d0966a775..8af05705962e8 160000 --- a/frozen/Adafruit_CircuitPython_LED_Animation +++ b/frozen/Adafruit_CircuitPython_LED_Animation @@ -1 +1 @@ -Subproject commit 5d13d0966a775369eca5b137cfef9583dfa8bb42 +Subproject commit 8af05705962e8bb7d2f8003e6a70916a9a51b863 diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase index 3dce5bca3bcd2..c87f120723e9f 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit 3dce5bca3bcd27354becc6d3ecf82244f1e26ffe +Subproject commit c87f120723e9fa742e3da25eaf33f3be1ae33715 diff --git a/frozen/Adafruit_CircuitPython_SSD1680 b/frozen/Adafruit_CircuitPython_SSD1680 index b7d511711c8f5..d6aa01c4f8fa1 160000 --- a/frozen/Adafruit_CircuitPython_SSD1680 +++ b/frozen/Adafruit_CircuitPython_SSD1680 @@ -1 +1 @@ -Subproject commit b7d511711c8f5557082d9c4307a89ecdec608727 +Subproject commit d6aa01c4f8fa1004430bfcdd4db2219183425693 diff --git a/frozen/Adafruit_CircuitPython_UC8151D b/frozen/Adafruit_CircuitPython_UC8151D index 776b932ebf769..4ebf9c2854376 160000 --- a/frozen/Adafruit_CircuitPython_UC8151D +++ b/frozen/Adafruit_CircuitPython_UC8151D @@ -1 +1 @@ -Subproject commit 776b932ebf76937e464ab2656834e7a1c1e3434b +Subproject commit 4ebf9c2854376a06766a6ae4732a4537a766fb75 From e0f51355b3f353b3361470ec7b86515b17b5e977 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 20 Aug 2025 11:02:16 -0400 Subject: [PATCH 057/517] allow HID usage of zero; at least one device does this --- shared-bindings/usb_hid/Device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/usb_hid/Device.c b/shared-bindings/usb_hid/Device.c index 7c9c354e4de10..a4a0e8680e531 100644 --- a/shared-bindings/usb_hid/Device.c +++ b/shared-bindings/usb_hid/Device.c @@ -98,7 +98,7 @@ static mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args const uint16_t usage_page = usage_page_arg; const mp_int_t usage_arg = args[ARG_usage].u_int; - mp_arg_validate_int_range(usage_arg, 1, 0xFFFF, MP_QSTR_usage); + mp_arg_validate_int_range(usage_arg, 0, 0xFFFF, MP_QSTR_usage); const uint16_t usage = usage_arg; mp_obj_t report_ids = args[ARG_report_ids].u_obj; From 525d18541c0a01374dfe82c0bd39c917098d177b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 21 Aug 2025 19:14:21 -0400 Subject: [PATCH 058/517] espressif/mpconfigport.mk: make more readable; restore audio on espressif boards --- ports/espressif/mpconfigport.mk | 39 ++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index f05df8c07c87a..42bf3418f9639 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -61,7 +61,7 @@ CIRCUITPY_ALARM_TOUCH ?= 0 CIRCUITPY_ANALOGBUFIO ?= 1 CIRCUITPY_AUDIOBUSIO ?= 1 CIRCUITPY_AUDIOBUSIO_PDMIN ?= 0 -CIRCUITPY_AUDIOIO ?= 0 +CIRCUITPY_AUDIOIO ?= 1 CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_BLEIO_NATIVE ?= 1 CIRCUITPY_CANIO ?= 1 @@ -89,11 +89,11 @@ CIRCUITPY_WATCHDOG ?= 1 CIRCUITPY_WIFI ?= 1 CIRCUITPY_SOCKETPOOL_IPV6 ?= 1 -# Conditionally turn off modules/features +# Conditionally turn off modules/features per chip type +#### esp32 ############################################################ ifeq ($(IDF_TARGET),esp32) # Modules CIRCUITPY_ALARM_TOUCH = 1 -CIRCUITPY_AUDIOIO ?= 1 CIRCUITPY_RGBMATRIX = 0 # SDMMC not supported yet @@ -102,6 +102,7 @@ CIRCUITPY_SDIOIO = 0 # Has no USB CIRCUITPY_USB_DEVICE = 0 +#### esp32c2 ########################################################## else ifeq ($(IDF_TARGET),esp32c2) # C2 ROM spits out the UART at 74880 when connected to a 26mhz crystal! @@ -128,6 +129,9 @@ CIRCUITPY_ANALOGBUFIO = 0 # No I2S CIRCUITPY_AUDIOBUSIO = 0 +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No RMT CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_PULSEIO = 0 @@ -142,6 +146,7 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 +#### esp32c3 ########################################################## else ifeq ($(IDF_TARGET),esp32c3) # Modules CIRCUITPY_ESPCAMERA = 0 @@ -151,6 +156,9 @@ CIRCUITPY_MEMORYMAP = 0 # No I80 support from the IDF CIRCUITPY_PARALLELDISPLAYBUS = 0 +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No PCNT peripheral CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_COUNTIO = 0 @@ -165,6 +173,7 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 +#### esp32c6 ########################################################## else ifeq ($(IDF_TARGET),esp32c6) # Modules CIRCUITPY_ESPCAMERA = 0 @@ -172,6 +181,9 @@ CIRCUITPY_ESPULP = 0 CIRCUITPY_MEMORYMAP = 0 CIRCUITPY_RGBMATRIX = 0 +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No space for this CIRCUITPY_AUDIOBUSIO = 0 @@ -187,6 +199,7 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 +#### esp32h2 ########################################################## else ifeq ($(IDF_TARGET),esp32h2) # Modules CIRCUITPY_ESPCAMERA = 0 @@ -194,6 +207,9 @@ CIRCUITPY_ESPULP = 0 CIRCUITPY_MEMORYMAP = 0 CIRCUITPY_RGBMATRIX = 0 +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No I80 support from the IDF CIRCUITPY_PARALLELDISPLAYBUS = 0 @@ -210,8 +226,12 @@ CIRCUITPY_WIFI = 0 CIRCUITPY_MAX3421E = 0 +#### esp32p4 ########################################################## else ifeq ($(IDF_TARGET),esp32p4) +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No wifi # TODO: Support ESP32-C6 coprocessor on some boards. CIRCUITPY_BLEIO_NATIVE = 0 @@ -246,10 +266,12 @@ CIRCUITPY_PARALLELDISPLAYBUS = 0 # Library doesn't support P4 yet it seems CIRCUITPY_ESPCAMERA = 0 +#### esp32s2 ########################################################## else ifeq ($(IDF_TARGET),esp32s2) # Modules -CIRCUITPY_ALARM_TOUCH = 1 +CIRCUITPY_ALARM_TOUCH = $(CIRCUITPY_ALARM) CIRCUITPY_AUDIOIO ?= 1 + # No BLE in hw CIRCUITPY_BLEIO_NATIVE = 0 @@ -258,12 +280,19 @@ CIRCUITPY_SDIOIO = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 0 +#### esp32s3 ########################################################## else ifeq ($(IDF_TARGET),esp32s3) + # Modules -CIRCUITPY_ALARM_TOUCH = 1 +CIRCUITPY_ALARM_TOUCH = $(CIRCUITPY_ALARM) CIRCUITPY_AUDIOBUSIO_PDMIN = 1 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 0 + +# No DAC +CIRCUITPY_AUDIOIO = 0 + endif +#### end chip-specific choices ######################################## # No room for large modules on 2MB boards # 2MB boards have a single firmware partition, and can't do dualbank. From a4833325125343bf4f3292a5b6a9b9ccf3aa33b0 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Fri, 22 Aug 2025 11:34:33 -0500 Subject: [PATCH 059/517] Scale current duration when tempo is adjusted --- shared-module/synthio/MidiTrack.c | 1 + 1 file changed, 1 insertion(+) diff --git a/shared-module/synthio/MidiTrack.c b/shared-module/synthio/MidiTrack.c index df8e5bdbea1b2..1cccd440fad9e 100644 --- a/shared-module/synthio/MidiTrack.c +++ b/shared-module/synthio/MidiTrack.c @@ -122,6 +122,7 @@ mp_int_t common_hal_synthio_miditrack_get_tempo(synthio_miditrack_obj_t *self) { void common_hal_synthio_miditrack_set_tempo(synthio_miditrack_obj_t *self, mp_int_t value) { mp_int_t val = mp_arg_validate_int_min(value, 1, MP_QSTR_tempo); + self->synth.span.dur = (uint32_t)self->synth.span.dur * self->tempo / val; self->tempo = val; } From 7b5587cf5f5088278ab494d01a16311f7b85899b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 22 Aug 2025 13:55:05 -0400 Subject: [PATCH 060/517] shrink some boards and for some boards make the turned-off modules consistent --- .../boards/feather_m4_express/mpconfigboard.mk | 1 + .../boards/itsybitsy_m4_express/mpconfigboard.mk | 6 ++++-- .../boards/metro_m4_express/mpconfigboard.mk | 1 + .../boards/seeeduino_wio_terminal/mpconfigboard.mk | 10 +++++++++- .../boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk | 2 ++ 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk index 4958581056e68..f780ea4aa3d0f 100644 --- a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk @@ -14,6 +14,7 @@ CIRCUITPY__EVE = 1 CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_SYNTHIO = 0 CIRCUITPY_VECTORIO = 0 diff --git a/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk index 27bf8c165da88..be22cb94fae92 100644 --- a/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk @@ -10,11 +10,13 @@ QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "GD25Q16C,W25Q16JVxQ" LONGINT_IMPL = MPZ +CIRCUITPY__EVE = 1 CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 -CIRCUITPY_GIFIO = 0 CIRCUITPY_JPEGIO = 0 -CIRCUITPY_TILEPALETTEMAPPER = 0 +CIRCUITPY_MSGPACK = 0 +CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_VECTORIO = 0 CIRCUITPY_BITBANG_APA102 = 1 diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk index 55a307ed4e308..d5aef01a0374f 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk @@ -14,6 +14,7 @@ CIRCUITPY__EVE = 1 CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_SYNTHIO = 0 CIRCUITPY_VECTORIO = 0 diff --git a/ports/atmel-samd/boards/seeeduino_wio_terminal/mpconfigboard.mk b/ports/atmel-samd/boards/seeeduino_wio_terminal/mpconfigboard.mk index dd8afd51f6e60..4ade6a148315d 100644 --- a/ports/atmel-samd/boards/seeeduino_wio_terminal/mpconfigboard.mk +++ b/ports/atmel-samd/boards/seeeduino_wio_terminal/mpconfigboard.mk @@ -9,6 +9,14 @@ CHIP_FAMILY = samd51 QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" LONGINT_IMPL = MPZ + +CIRCUITPY__EVE = 1 +CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 -CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 +CIRCUITPY_PARALLELDISPLAYBUS = 0 +CIRCUITPY_RGBMATRIX = 0 +CIRCUITPY_SHARPDISPLAY = 0 CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_VECTORIO = 0 diff --git a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk index 515b6da5c07c4..9ca2eee157748 100644 --- a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk +++ b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk @@ -9,4 +9,6 @@ CIRCUITPY_ESP_FLASH_SIZE = 2MB CIRCUITPY_DUALBANK = 0 +CIRCUITPY_JPEGIO = 0 + CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 From 687d0cbf20371f7f7143093233a4c50cf4a1764c Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 22 Aug 2025 21:51:07 -0400 Subject: [PATCH 061/517] shrink overflowing boards after #10529 --- ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk | 1 + .../atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk | 4 ++++ ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk index 3dbef7c576a13..76c60b4464639 100644 --- a/ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk @@ -16,6 +16,7 @@ CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_I2CDISPLAYBUS = 0 CIRCUITPY_I2CTARGET = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_PARALLELDISPLAYBUS = 0 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_SHARPDISPLAY = 0 diff --git a/ports/atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk index 69cd5b0e2863e..cb7e690c6c02d 100644 --- a/ports/atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk @@ -11,9 +11,13 @@ EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C, W25Q16JVxQ" LONGINT_IMPL = MPZ CIRCUITPY__EVE = 0 +CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_VECTORIO = 0 + # We don't have room for the fonts for terminalio for certain languages, # so turn off terminalio, and if it's off and displayio is on, diff --git a/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk b/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk index 73c56fb5fbab3..8fae53c0101f9 100644 --- a/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk +++ b/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk @@ -10,9 +10,13 @@ QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q16JVxM, W25Q16JVxQ" LONGINT_IMPL = MPZ +CIRCUITPY__EVE = 1 +CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_VECTORIO = 0 CIRCUITPY_BITBANG_APA102 = 1 From 055034f5b4878c846eb1e3f8010327e682326082 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 22 Aug 2025 22:07:27 -0400 Subject: [PATCH 062/517] fix busio.UART.Parity documentation indentation --- shared-bindings/busio/UART.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 5f1d301b675be..4714c3ee0733a 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -368,14 +368,14 @@ static mp_obj_t busio_uart_obj_reset_input_buffer(mp_obj_t self_in) { return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_reset_input_buffer_obj, busio_uart_obj_reset_input_buffer); -//| class Parity: -//| """Enum-like class to define the parity used to verify correct data transfer.""" +//| class Parity: +//| """Enum-like class to define the parity used to verify correct data transfer.""" //| -//| ODD: int -//| """Total number of ones should be odd.""" +//| ODD: int +//| """Total number of ones should be odd.""" //| -//| EVEN: int -//| """Total number of ones should be even.""" +//| EVEN: int +//| """Total number of ones should be even.""" //| //| const mp_obj_type_t busio_uart_parity_type; From 6e37d1b27bbe7d4a2c7f50e33773fff82007d83f Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 26 Aug 2025 18:00:28 -0400 Subject: [PATCH 063/517] claim usb_host pins on construction --- ports/mimxrt10xx/common-hal/usb_host/Port.c | 3 +++ ports/raspberrypi/common-hal/usb_host/Port.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/ports/mimxrt10xx/common-hal/usb_host/Port.c b/ports/mimxrt10xx/common-hal/usb_host/Port.c index 31af4ed582332..a7e73ab5b5c49 100644 --- a/ports/mimxrt10xx/common-hal/usb_host/Port.c +++ b/ports/mimxrt10xx/common-hal/usb_host/Port.c @@ -47,5 +47,8 @@ usb_host_port_obj_t *common_hal_usb_host_port_construct(const mcu_pin_obj_t *dp, self->dp = dp; self->dm = dm; + claim_pin(dp); + claim_pin(dm); + return self; } diff --git a/ports/raspberrypi/common-hal/usb_host/Port.c b/ports/raspberrypi/common-hal/usb_host/Port.c index 5439b39bf3aa3..b8a49725030ea 100644 --- a/ports/raspberrypi/common-hal/usb_host/Port.c +++ b/ports/raspberrypi/common-hal/usb_host/Port.c @@ -146,6 +146,8 @@ usb_host_port_obj_t *common_hal_usb_host_port_construct(const mcu_pin_obj_t *dp, self->base.type = &usb_host_port_type; self->dp = dp; self->dm = dm; + claim_pin(dp); + claim_pin(dm); PIO pio = pio_get_instance(pio_cfg.pio_tx_num); From 2efb47a038f49817ec39091818d6ec237b290612 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 27 Aug 2025 10:34:49 -0400 Subject: [PATCH 064/517] update pico-sdk submodule to raspberrypi 2.2.0 and cyw43-driver to v1.1.0 --- .gitmodules | 2 +- ports/raspberrypi/Makefile | 5 ++++- ports/raspberrypi/lib/cyw43-driver | 2 +- ports/raspberrypi/sdk | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index b1ec0f9faee7f..b89769942af7f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -172,7 +172,7 @@ url = https://github.com/adafruit/Adafruit_CircuitPython_SimpleMath [submodule "ports/raspberrypi/sdk"] path = ports/raspberrypi/sdk - url = https://github.com/adafruit/pico-sdk.git + url = https://github.com/raspberrypi/pico-sdk.git branch = force_inline_critical_section_2.1.1 [submodule "data/nvm.toml"] path = data/nvm.toml diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile index ba2db8a750825..b75d542325166 100644 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -25,7 +25,8 @@ INC_CYW43 := \ CFLAGS_CYW43 := \ -DCYW43_LWIP=1 \ -DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 \ - -DCYW43_USE_SPI \ + -DCYW43_USE_SPI=1 \ + -DUSE_SDIOIT=0 \ -DIGNORE_GPIO25 \ -DIGNORE_GPIO23 \ -DIGNORE_GPIO24 \ @@ -125,6 +126,7 @@ INC += \ -isystem sdk/src/rp2_common/hardware_powman/include/ \ -isystem sdk/src/rp2_common/hardware_pwm/include/ \ -isystem sdk/src/rp2_common/hardware_resets/include/ \ + -isystem sdk/src/rp2_common/hardware_rcp/include/ \ -isystem sdk/src/rp2_common/hardware_rtc/include/ \ -isystem sdk/src/rp2_common/hardware_spi/include/ \ -isystem sdk/src/rp2_common/hardware_sync/include/ \ @@ -149,6 +151,7 @@ INC += \ -isystem sdk/src/rp2_common/pico_float/include/ \ -isystem sdk/src/rp2_common/pico_runtime/include/ \ -isystem sdk/src/rp2_common/pico_runtime_init/include/ \ + -isystem sdk/src/rp2_common/pico_platform_common/include/ \ -isystem sdk/src/rp2_common/pico_platform_compiler/include/ \ -isystem sdk/src/rp2_common/pico_platform_sections/include/ \ -isystem sdk/src/rp2_common/pico_platform_panic/include/ \ diff --git a/ports/raspberrypi/lib/cyw43-driver b/ports/raspberrypi/lib/cyw43-driver index c1075d4bc4404..dd7568229f3bf 160000 --- a/ports/raspberrypi/lib/cyw43-driver +++ b/ports/raspberrypi/lib/cyw43-driver @@ -1 +1 @@ -Subproject commit c1075d4bc440422cf2b2fd12c64a1f53f77660ee +Subproject commit dd7568229f3bf7a37737b9e1ef250c26efe75b23 diff --git a/ports/raspberrypi/sdk b/ports/raspberrypi/sdk index 96b363a15598d..a1438dff1d38b 160000 --- a/ports/raspberrypi/sdk +++ b/ports/raspberrypi/sdk @@ -1 +1 @@ -Subproject commit 96b363a15598d0a17a77542ba63150b7d3fa5fd5 +Subproject commit a1438dff1d38bd9c65dbd693f0e5db4b9ae91779 From 048a904fb469d16b5dc90a0e1fb687f4a84b244d Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 27 Aug 2025 17:06:36 -0400 Subject: [PATCH 065/517] shrink some samd21 boards --- ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h | 2 ++ ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk | 1 + ports/atmel-samd/boards/snekboard/mpconfigboard.mk | 3 +++ 3 files changed, 6 insertions(+) diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h index be48cb810550e..8fdf20f186b00 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h @@ -20,6 +20,8 @@ #define SPI_FLASH_SCK_PIN &pin_PA09 #define SPI_FLASH_CS_PIN &pin_PA13 +#define BOARD_HAS_CRYSTAL 1 + #define DEFAULT_I2C_BUS_SCL (&pin_PA23) #define DEFAULT_I2C_BUS_SDA (&pin_PA22) diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk index 573a69bee327c..bd068adc943e2 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk @@ -11,3 +11,4 @@ EXTERNAL_FLASH_DEVICES = "S25FL064L" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 +CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk index d19e606319b63..441853c554df9 100644 --- a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk +++ b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk @@ -9,3 +9,6 @@ CHIP_FAMILY = samd21 SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" LONGINT_IMPL = MPZ + +CIRCUITPY_CODEOP = 0 +CIRCUITPY_RAINBOWIO = 0 From 57b25b5f3abf39e7ebbab97f4cc0cdbfa73c4d53 Mon Sep 17 00:00:00 2001 From: Peggy Date: Wed, 27 Aug 2025 14:07:34 -0700 Subject: [PATCH 066/517] Add RTC module to ports/analog for MAX32690_apard I added a port-specific module, RTC to ports/analog. Now the analog port is capable of handling `rtc.set_time_source()`, `rtc.RTC()` and `r.datetime()`. Note that the RTC calibration functions are not implemented and is planned for near future. The implementation in the RTC module also enables two functions in the time module, which are `time.time()` and `time.localtime()`. The following example prints the current time every second: import time import rtc r = rtc.RTC) r.datetime = time.struct_Time((2000, 1, 1, 1, 1, 1, 0, -1, -1)) while(True): current_time = r.datetime print(current_time) time.sleep(1) Files I modified: * ports/analog/common-hal/rtc/RTC.c * ports/analog/common-hal/rtc/RTC.h * ports/analog/common-hal/rtc/__init__.c * ports/analog/mpconfigport.mk --- ports/analog/common-hal/rtc/RTC.c | 42 ++++++++++++++++++++++++++ ports/analog/common-hal/rtc/RTC.h | 11 +++++++ ports/analog/common-hal/rtc/__init__.c | 6 ++++ ports/analog/mpconfigport.mk | 2 +- 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 ports/analog/common-hal/rtc/RTC.c create mode 100644 ports/analog/common-hal/rtc/RTC.h create mode 100644 ports/analog/common-hal/rtc/__init__.c diff --git a/ports/analog/common-hal/rtc/RTC.c b/ports/analog/common-hal/rtc/RTC.c new file mode 100644 index 0000000000000..c9d2258c1ece1 --- /dev/null +++ b/ports/analog/common-hal/rtc/RTC.c @@ -0,0 +1,42 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Nick Moore for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2019 Artur Pacholec +// SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" +#include "shared/timeutils/timeutils.h" +#include "supervisor/port.h" + +// This is the time in seconds since 2000 that the RTC was started. +// TODO: Change the offset to ticks so that it can be a subsecond adjustment. +static uint32_t rtc_offset = 0; + +void common_hal_rtc_get_time(timeutils_struct_time_t *tm) { + uint64_t ticks_s = port_get_raw_ticks(NULL) / 1024; + timeutils_seconds_since_2000_to_struct_time(rtc_offset + ticks_s, tm); +} + +void common_hal_rtc_set_time(timeutils_struct_time_t *tm) { + uint64_t ticks_s = port_get_raw_ticks(NULL) / 1024; + uint32_t epoch_s = timeutils_seconds_since_2000( + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec + ); + rtc_offset = epoch_s - ticks_s; +} + +// the calibration function will be implemented in near future +// the RTC oscillator on my MAX32690-APARD is only off by 0.001 second +// the inaccuracy is still tolerable +int common_hal_rtc_get_calibration(void) { + return 0; +} + +void common_hal_rtc_set_calibration(int calibration) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_calibration); +} diff --git a/ports/analog/common-hal/rtc/RTC.h b/ports/analog/common-hal/rtc/RTC.h new file mode 100644 index 0000000000000..590bc93ab5231 --- /dev/null +++ b/ports/analog/common-hal/rtc/RTC.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Noralf Trønnes +// SPDX-FileCopyrightText: Copyright (c) 2019 Artur Pacholec +// SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +extern void rtc_reset(void); diff --git a/ports/analog/common-hal/rtc/__init__.c b/ports/analog/common-hal/rtc/__init__.c new file mode 100644 index 0000000000000..ddf788bb0b5ad --- /dev/null +++ b/ports/analog/common-hal/rtc/__init__.c @@ -0,0 +1,6 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT diff --git a/ports/analog/mpconfigport.mk b/ports/analog/mpconfigport.mk index 67f1f79fb8121..97ead8efdfb0f 100644 --- a/ports/analog/mpconfigport.mk +++ b/ports/analog/mpconfigport.mk @@ -24,7 +24,7 @@ INTERNAL_FLASH_FILESYSTEM = 1 # Plan to implement CIRCUITPY_BUSIO ?= 0 -CIRCUITPY_RTC ?= 0 +CIRCUITPY_RTC ?= 1 # Other modules (may or may not implement): CIRCUITPY_ANALOGIO ?= 0 From 795d40f594339703e7f7e5b60fe9ade606dcdb61 Mon Sep 17 00:00:00 2001 From: Bernhard Bablok Date: Thu, 28 Aug 2025 18:02:38 +0200 Subject: [PATCH 067/517] fix MICROPY_HW_MCU_NAME --- ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h | 2 +- ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h index 72b1e1d04399f..097cbd8baa301 100644 --- a/ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h +++ b/ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h @@ -5,7 +5,7 @@ // SPDX-License-Identifier: MIT #define MICROPY_HW_BOARD_NAME "Pimoroni Plasma 2350" -#define MICROPY_HW_MCU_NAME "rp2350b" +#define MICROPY_HW_MCU_NAME "rp2350a" #define CIRCUITPY_RGB_STATUS_INVERTED_PWM #define CIRCUITPY_RGB_STATUS_R (&pin_GPIO16) diff --git a/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h index d9e62abb1edd2..576299d939cfb 100644 --- a/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h +++ b/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h @@ -5,7 +5,7 @@ // SPDX-License-Identifier: MIT #define MICROPY_HW_BOARD_NAME "Pimoroni Plasma 2350W" -#define MICROPY_HW_MCU_NAME "rp2350A" +#define MICROPY_HW_MCU_NAME "rp2350a" #define CIRCUITPY_RGB_STATUS_INVERTED_PWM #define CIRCUITPY_RGB_STATUS_R (&pin_GPIO16) From c74929a3d4576b29f4269a4bc448263833693336 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 29 Aug 2025 09:48:33 -0500 Subject: [PATCH 068/517] moving cron workflows from adabot repo --- .github/workflows/bundle_cron.yml | 58 +++++++++++++++++++ .github/workflows/learn_cron.yml | 41 ++++++++++++++ .github/workflows/reports_cron.yml | 91 ++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 .github/workflows/bundle_cron.yml create mode 100644 .github/workflows/learn_cron.yml create mode 100644 .github/workflows/reports_cron.yml diff --git a/.github/workflows/bundle_cron.yml b/.github/workflows/bundle_cron.yml new file mode 100644 index 0000000000000..cf1d51a3aa8fb --- /dev/null +++ b/.github/workflows/bundle_cron.yml @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: 2019 Michael Schroeder +# +# SPDX-License-Identifier: MIT + +name: Update Bundles + +on: + schedule: + - cron: 0 5 * * * + workflow_dispatch: + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + steps: + - name: repository + env: + OWNER_IS_ADAFRUIT: ${{ startswith(github.repository, 'adafruit/') }} + run: | + echo "This workflow will only run if Adafruit is the repository owner." + echo "Repository owner is Adafruit: $OWNER_IS_ADAFRUIT" + update-bundles: + runs-on: ubuntu-latest + # Only run the build on Adafruit's repository. Forks won't have the secrets. + # Its necessary to do this here, since 'schedule' events cannot (currently) + # be limited (they run on all forks' default branches). + if: startswith(github.repository, 'adafruit/') + steps: + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - name: Load contributor cache + uses: actions/cache@v4 + with: + key: "contributor-cache" + path: "contributors.json" + - name: Versions + run: | + python3 --version + - uses: actions/checkout@v4 + with: + repository: 'adafruit/adabot' + submodules: true + - name: Install deps + run: | + pip install -r requirements.txt + - name: Run adabot.circuitpython_bundle + env: + ADABOT_EMAIL: ${{ secrets.ADABOT_EMAIL }} + ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }} + ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + BIGQUERY_PRIVATE_KEY: ${{ secrets.BIGQUERY_PRIVATE_KEY }} + BIGQUERY_CLIENT_EMAIL: ${{ secrets.BIGQUERY_CLIENT_EMAIL }} + run: | + python3 -u -m adabot.circuitpython_bundle diff --git a/.github/workflows/learn_cron.yml b/.github/workflows/learn_cron.yml new file mode 100644 index 0000000000000..540b95a63b5f3 --- /dev/null +++ b/.github/workflows/learn_cron.yml @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Tag Learning System Guides + +on: + schedule: + - cron: 0 5 * * * + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + if: ${{ (github.repository_owner != 'adafruit') }} + steps: + - run: | + echo "This workflow is only intended to run in the adafruit fork" + + update-learn: + runs-on: ubuntu-latest + # Only run the build if the access token has been configured. THs will be + # the case on Adafruit's repository. Its necessary to do this here, since + # 'schedule' events cannot (currently) be limited (they run on all forks' + # default branches). + if: ${{ (github.repository_owner == 'adafruit') }} + steps: + - uses: actions/checkout@v4 + with: + repository: ${{ github.repository_owner }}/Adafruit_Learning_System_Guides + token: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + - name: Tag a release + env: + ADABOT_EMAIL: ${{ secrets.ADABOT_EMAIL }} + run: | + git config --global user.name adabot + git config --global user.email "$ADABOT_EMAIL" + TAG_NAME=`date +%Y%m%d` + git tag $TAG_NAME + git push origin $TAG_NAME diff --git a/.github/workflows/reports_cron.yml b/.github/workflows/reports_cron.yml new file mode 100644 index 0000000000000..652e0379fd2ca --- /dev/null +++ b/.github/workflows/reports_cron.yml @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: 2019 Michael Schroeder +# +# SPDX-License-Identifier: MIT + +name: Run Daily Reports + +on: + schedule: + # The actor (github.actor) that runs the cron job may be the user who created the cron job + # initially. It does not appear to be settable via a secret or environment variable. + - cron: 15 5 * * * + workflow_dispatch: + + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + steps: + - name: repository + env: + OWNER_IS_ADAFRUIT: ${{ startswith(github.repository, 'adafruit/') }} + run: | + echo "This workflow will only run if Adafruit is the repository owner." + echo "Repository owner is Adafruit: $OWNER_IS_ADAFRUIT" + run-reports: + runs-on: ubuntu-latest + # Only run the build on Adafruit's repository. Forks won't have the secrets. + # Its necessary to do this here, since 'schedule' events cannot (currently) + # be limited (they run on all forks' default branches). + if: startswith(github.repository, 'adafruit/') + env: + ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }} + ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + BIGQUERY_PRIVATE_KEY: ${{ secrets.BIGQUERY_PRIVATE_KEY }} + BIGQUERY_CLIENT_EMAIL: ${{ secrets.BIGQUERY_CLIENT_EMAIL }} + steps: + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: 3.11 + - name: Versions + run: | + python3 --version + - uses: actions/checkout@v4 + with: + repository: 'adafruit/adabot' + submodules: true + - name: Install deps + run: | + pip install -r requirements.txt + - name: Make Directory For Report Files + run: mkdir -p bin/adabot + - name: Set Date Variable + id: today + run: | + echo date=$( + date +%Y%m%d + ) >> $GITHUB_OUTPUT + - name: Run adabot.circuitpython_libraries + env: + # LIB_CHECK_CP_FILE is for circuitpython_libraries.py output + LIB_CHECK_CP_FILE: bin/adabot/circuitpython_library_report_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.circuitpython_libraries -o $LIB_CHECK_CP_FILE + continue-on-error: true + - name: Run adabot.circuitpython_library_download_stats + env: + # LIB_DL_STATS_FILE is for future Bundle and PyPi download stats script + LIB_DL_STATS_FILE: bin/adabot/library_download_stats_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.circuitpython_library_download_stats -o $LIB_DL_STATS_FILE + continue-on-error: true + - name: Run adabot.arduino_libraries + env: + # LIB_CHECK_ARD_FILE is for arduino_libraries.py output + LIB_CHECK_ARD_FILE: bin/adabot/arduino_library_report_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.arduino_libraries -o $LIB_CHECK_ARD_FILE + continue-on-error: true + - name: Check For Files + run: | + ls bin/adabot + - name: Upload Reports To AWS S3 + if: ${{ github.event_name != 'workflow_dispatch' }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: "[ -z \"$AWS_ACCESS_KEY_ID\" ] || aws s3 cp bin/adabot/ s3://adafruit-circuit-python/adabot/bin/reports/ --recursive --no-progress --region us-east-1" From 92a87a4c834e992d021ef8d445cb512d63eb51a8 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 29 Aug 2025 09:55:55 -0500 Subject: [PATCH 069/517] spelling fixes --- .github/workflows/bundle_cron.yml | 2 +- .github/workflows/learn_cron.yml | 4 ++-- .github/workflows/reports_cron.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/bundle_cron.yml b/.github/workflows/bundle_cron.yml index cf1d51a3aa8fb..606707d4102f1 100644 --- a/.github/workflows/bundle_cron.yml +++ b/.github/workflows/bundle_cron.yml @@ -24,7 +24,7 @@ jobs: update-bundles: runs-on: ubuntu-latest # Only run the build on Adafruit's repository. Forks won't have the secrets. - # Its necessary to do this here, since 'schedule' events cannot (currently) + # It's necessary to do this here, since 'schedule' events cannot (currently) # be limited (they run on all forks' default branches). if: startswith(github.repository, 'adafruit/') steps: diff --git a/.github/workflows/learn_cron.yml b/.github/workflows/learn_cron.yml index 540b95a63b5f3..6100e6637c20e 100644 --- a/.github/workflows/learn_cron.yml +++ b/.github/workflows/learn_cron.yml @@ -20,8 +20,8 @@ jobs: update-learn: runs-on: ubuntu-latest - # Only run the build if the access token has been configured. THs will be - # the case on Adafruit's repository. Its necessary to do this here, since + # Only run the build if the access token has been configured. This will be + # the case on Adafruit's repository. It's necessary to do this here, since # 'schedule' events cannot (currently) be limited (they run on all forks' # default branches). if: ${{ (github.repository_owner == 'adafruit') }} diff --git a/.github/workflows/reports_cron.yml b/.github/workflows/reports_cron.yml index 652e0379fd2ca..b4e9a43024e27 100644 --- a/.github/workflows/reports_cron.yml +++ b/.github/workflows/reports_cron.yml @@ -27,7 +27,7 @@ jobs: run-reports: runs-on: ubuntu-latest # Only run the build on Adafruit's repository. Forks won't have the secrets. - # Its necessary to do this here, since 'schedule' events cannot (currently) + # It's necessary to do this here, since 'schedule' events cannot (currently) # be limited (they run on all forks' default branches). if: startswith(github.repository, 'adafruit/') env: From dd44278f778ee1c714f31fe8b9e7aff3a9dc6b5b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 29 Aug 2025 12:37:39 -0400 Subject: [PATCH 070/517] update frozen libraries --- frozen/Adafruit_CircuitPython_DotStar | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frozen/Adafruit_CircuitPython_DotStar b/frozen/Adafruit_CircuitPython_DotStar index 163f2f166aee1..4b0ba649e5abd 160000 --- a/frozen/Adafruit_CircuitPython_DotStar +++ b/frozen/Adafruit_CircuitPython_DotStar @@ -1 +1 @@ -Subproject commit 163f2f166aee11d82303492bb1e5af4937e57b62 +Subproject commit 4b0ba649e5abdebead5b9a47a6c695d67c2c25fa From 91f6426c0f2ee5bae3a379a49ea29d3c8be1ce07 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 29 Aug 2025 10:06:06 -0700 Subject: [PATCH 071/517] Fix DVI blanking during SPI transactions SPI was using DMA to transfer to PSRAM. When a cache miss occurs, the DMA can't switch to another transfer and throws off DVI timing. So, only use DMA with SPI when the buffers are in SRAM. This will slow down SPI transactions when the FIFOs are empty and the CPU is busy running a background task. It will still be correct though since we control the SPI clock. Fixes #10557 --- ports/raspberrypi/common-hal/busio/SPI.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/raspberrypi/common-hal/busio/SPI.c b/ports/raspberrypi/common-hal/busio/SPI.c index d20bc4d7d10aa..4735d1284f98e 100644 --- a/ports/raspberrypi/common-hal/busio/SPI.c +++ b/ports/raspberrypi/common-hal/busio/SPI.c @@ -182,7 +182,10 @@ static bool _transfer(busio_spi_obj_t *self, chan_tx = dma_claim_unused_channel(false); chan_rx = dma_claim_unused_channel(false); } - bool use_dma = chan_rx >= 0 && chan_tx >= 0; + bool has_dma_channels = chan_rx >= 0 && chan_tx >= 0; + // Only use DMA if both data buffers are in SRAM. Otherwise, we'll stall the DMA with PSRAM or flash cache misses. + bool data_in_sram = data_in >= (uint8_t *)SRAM_BASE && data_out >= (uint8_t *)SRAM_BASE; + bool use_dma = has_dma_channels && data_in_sram; if (use_dma) { dma_channel_config c = dma_channel_get_default_config(chan_tx); channel_config_set_transfer_data_size(&c, DMA_SIZE_8); From 7fd8b4835789f4a9b2d6bb60b55eac445b9f744f Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 29 Aug 2025 11:00:43 -0700 Subject: [PATCH 072/517] Fix lvfontio memory allocation with fixed width fonts There was a branch of the loading that failed to set max_slots. This led to unpredicatable allocation failures. Fixes #10560 --- shared-module/lvfontio/OnDiskFont.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared-module/lvfontio/OnDiskFont.c b/shared-module/lvfontio/OnDiskFont.c index 0cf65301d2270..368bd381475b1 100644 --- a/shared-module/lvfontio/OnDiskFont.c +++ b/shared-module/lvfontio/OnDiskFont.c @@ -253,9 +253,10 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max *max_slots = advance_count[1] * 2 + advance_count[0]; } } + } else { + *max_slots = advance_count[0] + advance_count[1]; } - found_glyf = true; } From 059b0e712aa3b496d747187f9b4d7e7a35495afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brad=C3=A1n=20Lane?= Date: Fri, 29 Aug 2025 14:28:39 -0400 Subject: [PATCH 073/517] force neopixel_write into build --- .../atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk index 33165c8cfc5d0..11f090ed76305 100644 --- a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk @@ -38,7 +38,7 @@ CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 CIRCUITPY_USB_HID = 1 CIRCUITPY_USB_MIDI = 0 - +CIRCUITPY_NEOPIXEL_WRITE = 1 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HID From 81afa660785ceb18a501b059cb3f0133d97e4db7 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 29 Aug 2025 11:56:36 -0700 Subject: [PATCH 074/517] Fix display glitch during neopixel update The DMA can get stalled by DMA to/from PSRAM. So, copy the data into SRAM when we need to. Fail if we can't. Fixes #10564 --- .../common-hal/rp2pio/StateMachine.c | 90 +++++++++++++++---- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index fefecf05ff715..74b9a7f0c31b0 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -853,25 +853,64 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, // This implementation is based on SPI but varies because the tx and rx buffers // may be different lengths and occur at different times or speeds. - // Use DMA for large transfers if channels are available. - // Don't exceed FIFO size. - const size_t dma_min_size_threshold = self->fifo_depth; int chan_tx = -1; int chan_rx = -1; size_t len = MAX(out_len, in_len); bool tx = data_out != NULL; bool rx = data_in != NULL; - bool use_dma = len >= dma_min_size_threshold || swap_out || swap_in; + bool free_data_out = false; + bool free_data_in = false; + uint8_t *sram_data_out = (uint8_t *)data_out; + uint8_t *sram_data_in = data_in; + bool tx_fits_in_fifo = (out_len / out_stride_in_bytes) <= self->fifo_depth; + bool rx_fits_in_fifo = (in_len / in_stride_in_bytes) <= self->fifo_depth; + bool use_dma = !(tx_fits_in_fifo && rx_fits_in_fifo) || swap_out || swap_in; + if (use_dma) { - // Use DMA channels to service the two FIFOs + // We can only reliably use DMA for SRAM buffers. So, if we're given PSRAM buffers, + // then copy them to SRAM first. If we can't, then fail. + // Use DMA channels to service the two FIFOs. Fail if we can't allocate DMA channels. if (tx) { + if (data_out < (uint8_t *)SRAM_BASE) { + // Try to allocate a temporary buffer for DMA transfer + uint8_t *temp_buffer = (uint8_t *)port_malloc(len, true); + if (temp_buffer == NULL) { + mp_printf(&mp_plat_print, "Failed to allocate temporary buffer for DMA tx\n"); + return false; + } + memcpy(temp_buffer, data_out, len); + sram_data_out = temp_buffer; + free_data_out = true; + } chan_tx = dma_claim_unused_channel(false); // DMA allocation failed... if (chan_tx < 0) { + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + port_free(sram_data_in); + } return false; } } if (rx) { + if (data_in < (uint8_t *)SRAM_BASE) { + // Try to allocate a temporary buffer for DMA transfer + uint8_t *temp_buffer = (uint8_t *)port_malloc(len, true); + if (temp_buffer == NULL) { + mp_printf(&mp_plat_print, "Failed to allocate temporary buffer for DMA rx\n"); + if (chan_tx >= 0) { + dma_channel_unclaim(chan_tx); + } + if (free_data_out) { + port_free(sram_data_out); + } + return false; + } + sram_data_in = temp_buffer; + free_data_in = true; + } chan_rx = dma_claim_unused_channel(false); // DMA allocation failed... if (chan_rx < 0) { @@ -879,6 +918,12 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, if (chan_tx >= 0) { dma_channel_unclaim(chan_tx); } + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + port_free(sram_data_in); + } return false; } } @@ -910,7 +955,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, channel_config_set_bswap(&c, swap_out); dma_channel_configure(chan_tx, &c, tx_destination, - data_out, + sram_data_out, out_len / out_stride_in_bytes, false); channel_mask |= 1u << chan_tx; @@ -923,7 +968,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, channel_config_set_write_increment(&c, true); channel_config_set_bswap(&c, swap_in); dma_channel_configure(chan_rx, &c, - data_in, + sram_data_in, rx_source, in_len / in_stride_in_bytes, false); @@ -950,8 +995,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, self->pio->fdebug = stall_mask; } - // If we have claimed only one channel successfully, we should release immediately. This also - // releases the DMA after use_dma has been done. + // Release the DMA channels after use_dma has been done. if (chan_rx >= 0) { dma_channel_unclaim(chan_rx); } @@ -960,31 +1004,31 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, } if (!use_dma && !(self->user_interruptible && mp_hal_is_interrupted())) { - // Use software for small transfers, or if couldn't claim two DMA channels + // Use software for small transfers size_t rx_remaining = in_len / in_stride_in_bytes; size_t tx_remaining = out_len / out_stride_in_bytes; while (rx_remaining || tx_remaining) { while (tx_remaining && !pio_sm_is_tx_fifo_full(self->pio, self->state_machine)) { if (out_stride_in_bytes == 1) { - *tx_destination = *data_out; + *tx_destination = *sram_data_out; } else if (out_stride_in_bytes == 2) { - *((uint16_t *)tx_destination) = *((uint16_t *)data_out); + *((uint16_t *)tx_destination) = *((uint16_t *)sram_data_out); } else if (out_stride_in_bytes == 4) { - *((uint32_t *)tx_destination) = *((uint32_t *)data_out); + *((uint32_t *)tx_destination) = *((uint32_t *)sram_data_out); } - data_out += out_stride_in_bytes; + sram_data_out += out_stride_in_bytes; --tx_remaining; } while (rx_remaining && !pio_sm_is_rx_fifo_empty(self->pio, self->state_machine)) { if (in_stride_in_bytes == 1) { - *data_in = (uint8_t)*rx_source; + *sram_data_in = (uint8_t)*rx_source; } else if (in_stride_in_bytes == 2) { - *((uint16_t *)data_in) = *((uint16_t *)rx_source); + *((uint16_t *)sram_data_in) = *((uint16_t *)rx_source); } else if (in_stride_in_bytes == 4) { - *((uint32_t *)data_in) = *((uint32_t *)rx_source); + *((uint32_t *)sram_data_in) = *((uint32_t *)rx_source); } - data_in += in_stride_in_bytes; + sram_data_in += in_stride_in_bytes; --rx_remaining; } RUN_BACKGROUND_TASKS; @@ -996,7 +1040,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, self->pio->fdebug = stall_mask; } // Wait for the state machine to finish transmitting the data we've queued - // up. + // up (either from the CPU or via DMA.) if (tx) { while (!pio_sm_is_tx_fifo_empty(self->pio, self->state_machine) || (self->wait_for_txstall && (self->pio->fdebug & stall_mask) == 0)) { @@ -1006,6 +1050,14 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, } } } + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + // Copy the data from the SRAM buffer to the user PSRAM buffer. + memcpy(data_in, sram_data_in, len); + port_free(sram_data_in); + } return true; } From 4b2624a27baabe499efb5b46805cdd82599f2f58 Mon Sep 17 00:00:00 2001 From: bradanlane Date: Fri, 29 Aug 2025 16:35:32 -0400 Subject: [PATCH 075/517] Enable CIRCUITPY_PIXELBUF in mpconfigboard.mk --- .../atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk index 11f090ed76305..894037ec0148a 100644 --- a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk @@ -39,6 +39,7 @@ CIRCUITPY_RTC = 0 CIRCUITPY_USB_HID = 1 CIRCUITPY_USB_MIDI = 0 CIRCUITPY_NEOPIXEL_WRITE = 1 +CIRCUITPY_PIXELBUF = 1 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HID From 3c44c9d908952305349bb79ce032249a4e505835 Mon Sep 17 00:00:00 2001 From: bradanlane Date: Fri, 29 Aug 2025 17:41:57 -0400 Subject: [PATCH 076/517] Update mpconfigboard.mk to incorporate limited changes suggested by halbert. --- .../boards/bradanlanestudio_coin_m0/mpconfigboard.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk index 894037ec0148a..9a2cdcd668ed2 100644 --- a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk @@ -18,7 +18,7 @@ CHIP_FAMILY = samd21 SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" -LONGINT_IMPL = NONE +LONGINT_IMPL = MPZ # the M0 Coin has limited functionality and many modules can be eliminated @@ -26,7 +26,7 @@ LONGINT_IMPL = NONE # Disable modules that are unusable on this special-purpose board. -CIRCUITPY_FULL_BUILD = 0 +CIRCUITPY_FULL_BUILD = 1 CIRCUITPY_AUDIOIO = 1 CIRCUITPY_DISPLAYIO = 0 From a4a383630267c8fecf216900aec4b1e8b4f00642 Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sun, 31 Aug 2025 00:09:32 -0500 Subject: [PATCH 077/517] Fix radio union types --- shared-bindings/wifi/Radio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shared-bindings/wifi/Radio.c b/shared-bindings/wifi/Radio.c index a267dea11c1ff..9a7d445fd0473 100644 --- a/shared-bindings/wifi/Radio.c +++ b/shared-bindings/wifi/Radio.c @@ -99,7 +99,7 @@ MP_PROPERTY_GETSET(wifi_radio_enabled_obj, (mp_obj_t)&wifi_radio_get_enabled_obj, (mp_obj_t)&wifi_radio_set_enabled_obj); -//| hostname: Union[str | ReadableBuffer] +//| hostname: Union[str, ReadableBuffer] //| """Hostname for wifi interface. When the hostname is altered after interface started/connected //| the changes would only be reflected once the interface restarts/reconnects.""" static mp_obj_t wifi_radio_get_hostname(mp_obj_t self_in) { @@ -325,8 +325,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station); //| def start_ap( //| self, -//| ssid: Union[str | ReadableBuffer], -//| password: Union[str | ReadableBuffer] = b"", +//| ssid: Union[str, ReadableBuffer], +//| password: Union[str, ReadableBuffer] = b"", //| *, //| channel: int = 1, //| authmode: Iterable[AuthMode] = (), @@ -438,11 +438,11 @@ MP_PROPERTY_GETTER(wifi_radio_ap_active_obj, //| def connect( //| self, -//| ssid: Union[str | ReadableBuffer], -//| password: Union[str | ReadableBuffer] = b"", +//| ssid: Union[str, ReadableBuffer], +//| password: Union[str, ReadableBuffer] = b"", //| *, //| channel: int = 0, -//| bssid: Optional[Union[str | ReadableBuffer]] = None, +//| bssid: Optional[Union[str, ReadableBuffer]] = None, //| timeout: Optional[float] = None, //| ) -> None: //| """Connects to the given ssid and waits for an ip address. Reconnections are handled From 36feb41bf5d3b15a43532ba0ec6b218cf2067f94 Mon Sep 17 00:00:00 2001 From: Nate Gay Date: Sun, 31 Aug 2025 00:29:41 -0500 Subject: [PATCH 078/517] Fix struct_time type hints --- shared-bindings/time/__init__.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/shared-bindings/time/__init__.c b/shared-bindings/time/__init__.c index 504cd07d90111..8a591da368858 100644 --- a/shared-bindings/time/__init__.c +++ b/shared-bindings/time/__init__.c @@ -83,6 +83,16 @@ static mp_obj_t struct_time_make_new(const mp_obj_type_t *type, size_t n_args, s } //| class struct_time: +//| tm_year: int +//| tm_mon: int +//| tm_mday: int +//| tm_hour: int +//| tm_min: int +//| tm_sec: int +//| tm_wday: int +//| tm_yday: int +//| tm_isdst: int +//| //| def __init__(self, time_tuple: Sequence[int]) -> None: //| """Structure used to capture a date and time. Can be constructed from a `struct_time`, `tuple`, `list`, or `namedtuple` with 9 elements. //| @@ -210,7 +220,7 @@ static mp_obj_t time_monotonic_ns(void) { } MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_ns_obj, time_monotonic_ns); -//| def localtime(secs: int) -> struct_time: +//| def localtime(secs: Optional[int] = None) -> struct_time: //| """Convert a time expressed in seconds since Jan 1, 1970 to a struct_time in //| local time. If secs is not provided or None, the current time as returned //| by time() is used. From 5480bd47684b0e0b3537a2f365bc94b2c0361633 Mon Sep 17 00:00:00 2001 From: allexok Date: Tue, 2 Sep 2025 17:28:09 +0200 Subject: [PATCH 079/517] Add Ai-On-The-Edge-Cam board --- .../prokyber_ai_on_the_edge_cam/board.c | 9 ++ .../mpconfigboard.h | 22 +++++ .../mpconfigboard.mk | 14 +++ .../boards/prokyber_ai_on_the_edge_cam/pins.c | 99 +++++++++++++++++++ .../prokyber_ai_on_the_edge_cam/sdkconfig | 14 +++ 5 files changed, 158 insertions(+) create mode 100644 ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c create mode 100644 ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h create mode 100644 ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk create mode 100644 ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c create mode 100644 ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c new file mode 100644 index 0000000000000..a3a9eec047145 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h new file mode 100644 index 0000000000000..2114fecf373a4 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Prokyber Ai-On-The-Edge-Cam" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO12) + +#define MICROPY_HW_LED_STATUS (&pin_GPIO48) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}} diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk new file mode 100644 index 0000000000000..82419bb80745c --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x1209 +USB_PID = 0xDABB +USB_PRODUCT = "AI-ON-THE-EDGE-CAM" +USB_MANUFACTURER = "Prokyber" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m +CIRCUITPY_ESP_PSRAM_SIZE = 8MB diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c new file mode 100644 index 0000000000000..b11b33877c34b --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c @@ -0,0 +1,99 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" + +static const mp_rom_obj_tuple_t camera_data_tuple = { + {&mp_type_tuple}, + 8, + { + MP_ROM_PTR(&pin_GPIO11), + MP_ROM_PTR(&pin_GPIO9), + MP_ROM_PTR(&pin_GPIO8), + MP_ROM_PTR(&pin_GPIO10), + MP_ROM_PTR(&pin_GPIO47), + MP_ROM_PTR(&pin_GPIO18), + MP_ROM_PTR(&pin_GPIO17), + MP_ROM_PTR(&pin_GPIO16), + } +}; + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_PER_EN), MP_ROM_PTR(&pin_GPIO46) }, + + // SD + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, + + // Ethernet + { MP_ROM_QSTR(MP_QSTR_ETH_RST), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_ETH_INT), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_ETH_MOSI), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_ETH_MISO), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_ETH_CLK), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_ETH_CS), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, + + // LEDs + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, + + // UART + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, + + //I2C + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA), MP_ROM_PTR(&camera_data_tuple) }, + + { MP_ROM_QSTR(MP_QSTR_CAMERA_VSYNC), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_HREF), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA9), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_XCLK), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA8), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA7), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_PCLK), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA6), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA2), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA5), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA3), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA4), MP_ROM_PTR(&pin_GPIO8) }, + // { MP_ROM_QSTR(MP_QSTR_CAMERA_RESET), MP_ROM_PTR(&pin_GPIO47) }, + // { MP_ROM_QSTR(MP_QSTR_CAMERA_PWDN), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOD), MP_ROM_PTR(&pin_GPIO4) }, // SDA + { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOC), MP_ROM_PTR(&pin_GPIO5) }, // SCL + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig new file mode 100644 index 0000000000000..ac1c535717672 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP +CONFIG_OV2640_SUPPORT=y +# end of Component config + +# end of Espressif IoT Development Framework Configuration From 5493a6a1bde5538a4bfedad502f3ca3834b79b65 Mon Sep 17 00:00:00 2001 From: allexok Date: Tue, 2 Sep 2025 17:38:23 +0200 Subject: [PATCH 080/517] prettier text after 'pre-commit run --all-files' --- ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c index b11b33877c34b..305bdb3e0b69a 100644 --- a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c @@ -68,7 +68,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, - //I2C + // I2C { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, From de2efe0c2cceefa1a5ae5d14e6145ec8716a66f4 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 2 Sep 2025 20:43:39 -0400 Subject: [PATCH 081/517] Fix cardputer boot loop, and do some cleanup. - For `cardputer_keyboard.c`, allocate `DemuxKeyMatrix` it creates and the objects it holds (all the way down) on the port heap, because it lives past VM's. - To do the above, add support for non-VM heap for `DemuxKeyMatrix` and the `keypad` allocations it needs by passing an arg `use_gc_allocator`, as is done in `lvfontio`. - Add `mp_obj_port_malloc()`, `mp_obj_port_malloc_var()`, `mp_obj_new_port_tuple()`, all of which allocate on the port heap, not the VM heap. - Add `port_malloc_zero()`, because `port_malloc()` did not zero out its allocation, unlike VM heap allocations. I got caught by this. - Move `port_gc_collect()` from `main.c` to `supervisor/port.c` for consistency. This function is not actually used by anyone, but I was considering using it. - Add an `MP_WEAK` `mp_board_init()`, for board-specific initializations needed when a VM starts. Also considered, but not used. Added anyway for possible future use. - Add an `MP_WEAK` `board_gc_collect()`, for board-specific gc. Not used. - Add an `MP_WEAK` `port_gc_collect()`, for port-specific gc. Not used. - Don't duplicate keyboard code between `boards/m5stack_cardputer` and `boards/m5stack_cardputer_ros`. Instead, add a `ports/espressif/module` directory for this code, in the style of a port-specific `module` directory, like the port-specific `bindings` directories. --- main.c | 8 +- .../boards/m5stack_cardputer/mpconfigboard.mk | 2 +- .../cardputer_keyboard.c | 238 ------------------ .../boards/m5stack_cardputer_ros/keymap.h | 214 ---------------- .../m5stack_cardputer_ros/mpconfigboard.mk | 2 +- .../cardputer_keyboard.c | 59 ++--- .../keymap.h => module/cardputer_keymap.h} | 0 py/objtuple.c | 3 + shared-bindings/keypad/EventQueue.h | 2 +- shared-bindings/keypad_demux/DemuxKeyMatrix.c | 3 +- shared-bindings/keypad_demux/DemuxKeyMatrix.h | 2 +- shared-module/keypad/EventQueue.c | 9 +- shared-module/keypad/KeyMatrix.c | 2 +- shared-module/keypad/Keys.c | 2 +- shared-module/keypad/ShiftRegisterKeys.c | 2 +- shared-module/keypad/__init__.c | 14 +- shared-module/keypad/__init__.h | 2 +- shared-module/keypad_demux/DemuxKeyMatrix.c | 25 +- supervisor/board.h | 7 + supervisor/port.h | 8 + supervisor/port_heap.h | 1 + supervisor/shared/board.c | 8 + supervisor/shared/port.c | 36 +++ 23 files changed, 140 insertions(+), 509 deletions(-) delete mode 100644 ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c delete mode 100644 ports/espressif/boards/m5stack_cardputer_ros/keymap.h rename ports/espressif/{boards/m5stack_cardputer => module}/cardputer_keyboard.c (83%) rename ports/espressif/{boards/m5stack_cardputer/keymap.h => module/cardputer_keymap.h} (100%) diff --git a/main.c b/main.c index 1f387b7ca3ceb..cf9cf377d690b 100644 --- a/main.c +++ b/main.c @@ -207,6 +207,9 @@ static void start_mp(safe_mode_t safe_mode) { // Always return to root common_hal_os_chdir("/"); + + // Initialization for individual boards when a VM starts. + mp_board_init(); } static void stop_mp(void) { @@ -1149,6 +1152,7 @@ void gc_collect(void) { gc_collect_root((void **)&MP_STATE_VM(vfs_mount_table), sizeof(mp_vfs_mount_t) / sizeof(mp_uint_t)); port_gc_collect(); + board_gc_collect(); background_callback_gc_collect(); @@ -1179,10 +1183,6 @@ void gc_collect(void) { gc_collect_end(); } -// Ports may provide an implementation of this function if it is needed -MP_WEAK void port_gc_collect(void) { -} - size_t gc_get_max_new_split(void) { return port_heap_get_largest_free_size(); } diff --git a/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk b/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk index cd18ccf8a5a81..bc30ae3afd5e9 100644 --- a/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk @@ -13,4 +13,4 @@ CIRCUITPY_ESP_FLASH_SIZE = 8MB CIRCUITPY_ESPCAMERA = 0 CIRCUITPY_MAX3421E = 0 -SRC_C += boards/$(BOARD)/cardputer_keyboard.c +SRC_C += module/cardputer_keyboard.c diff --git a/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c b/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c deleted file mode 100644 index 73880f66e19d9..0000000000000 --- a/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c +++ /dev/null @@ -1,238 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft -// -// SPDX-License-Identifier: MIT - -#include "py/obj.h" -#include "py/objstr.h" -#include "py/runtime.h" - -#include "supervisor/shared/serial.h" -#include "shared-bindings/keypad/EventQueue.h" -#include "shared-bindings/keypad_demux/DemuxKeyMatrix.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-module/keypad/EventQueue.h" -#include "shared-module/keypad_demux/DemuxKeyMatrix.h" -#include "supervisor/shared/reload.h" - -#include "keymap.h" - -//| """M5Stack Cardputer keyboard integration. -//| """ -//| -//| """The KEYBOARD object is an instance of DemuxKeyMatrix, configured with correct pins. -//| The pins cannot be used for any other purposes (even though exposed in the board module). -//| By default all keyboard events are consumed and routed to the standard input - there is -//| not much use of the KEYBOARD object in this configuration - just read the input via sys.stdin. -//| -//| If you need to manually process individual key up / key down events via KEYBOARD.events, -//| call `detach_serial()`. -//| """" -//| KEYBOARD: keypad_demux.DemuxKeymatrix -//| -keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard_obj; -bool cardputer_keyboard_serial_attached = false; - -void cardputer_keyboard_init(void); -void keyboard_seq(const char *seq); -void update_keyboard(keypad_eventqueue_obj_t *queue); - -//| def detach_serial() -> None: -//| """Stops consuming keyboard events and routing them to sys.stdin.""" -//| ... -//| -static mp_obj_t detach_serial(void) { - cardputer_keyboard_serial_attached = false; - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, NULL); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); - -//| def attach_serial() -> None: -//| """Starts consuming keyboard events and routing them to sys.stdin.""" -//| ... -//| -static mp_obj_t attach_serial(void) { - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, update_keyboard); - cardputer_keyboard_serial_attached = true; - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_0(attach_serial_obj, attach_serial); - -//| def key_to_char(key: int, shifted: bool) -> str | None: -//| """Converts a key index to the respective key (with or without shift modifier). -//| Returns None for functional & modifier keys or whenever not 0 <= key < 56. -//| """ -//| ... -//| -static mp_obj_t key_to_char(mp_obj_t key_obj, mp_obj_t shifted_obj) { - mp_int_t key = mp_obj_get_int(key_obj); - if (key < 0 || key > (mp_int_t)(sizeof keymap / sizeof *keymap) || keymap[key] == 0) { - return mp_const_none; - } else if (shifted_obj == mp_const_true) { - return mp_obj_new_str(&keymap_shifted[key], 1); - } else { - return mp_obj_new_str(&keymap[key], 1); - } -} -static MP_DEFINE_CONST_FUN_OBJ_2(key_to_char_obj, key_to_char); - -// Ring buffer of characters consumed from keyboard events (when serial attached) -ringbuf_t keyqueue; -char keybuf[32]; - -keypad_event_obj_t event; -char keystate[56]; - -// Keyboard pins -const mcu_pin_obj_t *row_addr_pins[] = { - &pin_GPIO8, - &pin_GPIO9, - &pin_GPIO11, -}; - -const mcu_pin_obj_t *column_pins[] = { - &pin_GPIO13, - &pin_GPIO15, - &pin_GPIO3, - &pin_GPIO4, - &pin_GPIO5, - &pin_GPIO6, - &pin_GPIO7 -}; - -void cardputer_keyboard_init(void) { - cardputer_keyboard_obj.base.type = &keypad_demux_demuxkeymatrix_type; - common_hal_keypad_demux_demuxkeymatrix_construct( - &cardputer_keyboard_obj, // self - 3, // num_row_addr_pins - row_addr_pins, // row_addr_pins - 7, // num_column_pins - column_pins, // column_pins - true, // columns_to_anodes - false, // transpose - 0.01f, // interval - 20, // max_events - 2 // debounce_threshold - ); - demuxkeymatrix_never_reset(&cardputer_keyboard_obj); - - ringbuf_init(&keyqueue, (uint8_t *)keybuf, sizeof(keybuf)); - attach_serial(); -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -void board_serial_init(void) { - cardputer_keyboard_init(); -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -bool board_serial_connected(void) { - return cardputer_keyboard_serial_attached; -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -uint32_t board_serial_bytes_available(void) { - if (cardputer_keyboard_serial_attached) { - return ringbuf_num_filled(&keyqueue); - } else { - return 0; - } -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -char board_serial_read(void) { - if (cardputer_keyboard_serial_attached) { - return ringbuf_get(&keyqueue); - } else { - return 0; - } -} - -void keyboard_seq(const char *seq) { - while (*seq) { - ringbuf_put(&keyqueue, *seq++); - } -} - -void update_keyboard(keypad_eventqueue_obj_t *queue) { - uint8_t ascii = 0; - - if (common_hal_keypad_eventqueue_get_length(queue) == 0) { - return; - } - - while (common_hal_keypad_eventqueue_get_into(queue, &event)) { - if (event.pressed) { - keystate[event.key_number] = 1; - - if (keystate[KEY_CTRL]) { - if (keystate[KEY_ALT] && keystate[KEY_BACKSPACE]) { - reload_initiate(RUN_REASON_REPL_RELOAD); - } - ascii = keymap[event.key_number]; - if (ascii >= 'a' && ascii <= 'z') { - ascii -= 'a' - 1; - } - - if (ascii == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - } - } else if (keystate[KEY_SHIFT]) { - ascii = keymap_shifted[event.key_number]; - } else if (keystate[KEY_FN] && event.key_number != KEY_FN) { - switch (event.key_number | FN_MOD) - { - case KEY_DOWN: - keyboard_seq("\e[B"); - break; - case KEY_UP: - keyboard_seq("\e[A"); - break; - case KEY_DELETE: - keyboard_seq("\e[3~"); - break; - case KEY_LEFT: - keyboard_seq("\e[D"); - break; - case KEY_RIGHT: - keyboard_seq("\e[C"); - break; - case KEY_ESC: - ringbuf_put(&keyqueue, '\e'); - break; - } - } else { - ascii = keymap[event.key_number]; - } - - if (ascii > 0) { - if (keystate[KEY_ALT]) { - ringbuf_put(&keyqueue, '\e'); - } else if (keystate[KEY_OPT]) { - ringbuf_put(&keyqueue, '\x10'); - } - ringbuf_put(&keyqueue, ascii); - } - } else { - keystate[event.key_number] = 0; - } - } -} - -static const mp_rom_map_elem_t cardputer_keyboard_module_globals_table[] = { - {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cardputer_keyboard)}, - {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard_obj)}, - {MP_ROM_QSTR(MP_QSTR_attach_serial), MP_ROM_PTR(&attach_serial_obj)}, - {MP_ROM_QSTR(MP_QSTR_detach_serial), MP_ROM_PTR(&detach_serial_obj)}, - {MP_ROM_QSTR(MP_QSTR_key_to_char), MP_ROM_PTR(&key_to_char_obj)}, -}; -MP_DEFINE_CONST_DICT(cardputer_keyboard_module_globals, cardputer_keyboard_module_globals_table); - -const mp_obj_module_t cardputer_keyboard_module = { - .base = {&mp_type_module}, - .globals = (mp_obj_dict_t *)&cardputer_keyboard_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_cardputer_keyboard, cardputer_keyboard_module); diff --git a/ports/espressif/boards/m5stack_cardputer_ros/keymap.h b/ports/espressif/boards/m5stack_cardputer_ros/keymap.h deleted file mode 100644 index 0256fafaa0f57..0000000000000 --- a/ports/espressif/boards/m5stack_cardputer_ros/keymap.h +++ /dev/null @@ -1,214 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#define SHIFT_MOD 0x40 -#define FN_MOD 0x80 - -#define KEY_OPT 0 -#define KEY_Z 1 -#define KEY_C 2 -#define KEY_B 3 -#define KEY_M 4 -#define KEY_DOT 5 -#define KEY_SPACE 6 -#define KEY_SHIFT 7 -#define KEY_S 8 -#define KEY_F 9 -#define KEY_H 10 -#define KEY_K 11 -#define KEY_SEMICOLON 12 -#define KEY_ENTER 13 -#define KEY_Q 14 -#define KEY_E 15 -#define KEY_T 16 -#define KEY_U 17 -#define KEY_O 18 -#define KEY_LEFT_BRACKET 19 -#define KEY_BACKSLASH 20 -#define KEY_1 21 -#define KEY_3 22 -#define KEY_5 23 -#define KEY_7 24 -#define KEY_9 25 -#define KEY_UNDERSCORE 26 -#define KEY_BACKSPACE 27 -#define KEY_CTRL 28 -#define KEY_ALT 29 -#define KEY_X 30 -#define KEY_V 31 -#define KEY_N 32 -#define KEY_COMMA 33 -#define KEY_SLASH 34 -#define KEY_FN 35 -#define KEY_A 36 -#define KEY_D 37 -#define KEY_G 38 -#define KEY_J 39 -#define KEY_L 40 -#define KEY_APOSTROPHE 41 -#define KEY_TAB 42 -#define KEY_W 43 -#define KEY_R 44 -#define KEY_Y 45 -#define KEY_I 46 -#define KEY_P 47 -#define KEY_RIGHT_BRACKET 48 -#define KEY_GRAVE 49 -#define KEY_2 50 -#define KEY_4 51 -#define KEY_6 52 -#define KEY_8 53 -#define KEY_0 54 -#define KEY_EQUALS 55 - -#define KEY_GREATER (5 | SHIFT_MOD) -#define KEY_COLON (12 | SHIFT_MOD) -#define KEY_LEFT_CURLY_BRACKET (19 | SHIFT_MOD) -#define KEY_PIPE (20 | SHIFT_MOD) -#define KEY_EXCLAMATION (21 | SHIFT_MOD) -#define KEY_HASH (22 | SHIFT_MOD) -#define KEY_PERCENT (23 | SHIFT_MOD) -#define KEY_AMPERSAND (24 | SHIFT_MOD) -#define KEY_OPEN_PARENTHESIS (25 | SHIFT_MOD) -#define KEY_MINUS (26 | SHIFT_MOD) -#define KEY_LESS (33 | SHIFT_MOD) -#define KEY_QUESTION (34 | SHIFT_MOD) -#define KEY_DOUBLE_QUOTE (41 | SHIFT_MOD) -#define KEY_RIGHT_CURLY_BRACKET (48 | SHIFT_MOD) -#define KEY_TILDE (49 | SHIFT_MOD) -#define KEY_AT (50 | SHIFT_MOD) -#define KEY_DOLLAR (51 | SHIFT_MOD) -#define KEY_CARET (52 | SHIFT_MOD) -#define KEY_ASTERISK (53 | SHIFT_MOD) -#define KEY_CLOSE_PARENTHESIS (54 | SHIFT_MOD) -#define KEY_PLUS (55 | SHIFT_MOD) - -#define KEY_DOWN (5 | FN_MOD) -#define KEY_UP (12 | FN_MOD) -#define KEY_DELETE (27 | FN_MOD) -#define KEY_LEFT (33 | FN_MOD) -#define KEY_RIGHT (34 | FN_MOD) -#define KEY_ESC (49 | FN_MOD) - -const char keymap[56] = { - 0, // KEY_OPT - 'z', // KEY_Z - 'c', // KEY_C - 'b', // KEY_B - 'm', // KEY_M - '.', // KEY_DOT - ' ', // KEY_SPACE - 0, // KEY_SHIFT - 's', // KEY_S - 'f', // KEY_F - 'h', // KEY_H - 'k', // KEY_K - ';', // KEY_SEMICOLON - '\r',// KEY_ENTER - 'q', // KEY_Q - 'e', // KEY_E - 't', // KEY_T - 'u', // KEY_U - 'o', // KEY_O - '[', // KEY_LEFT_BRACKET - '\\',// KEY_BACKSLASH - '1', // KEY_1 - '3', // KEY_3 - '5', // KEY_5 - '7', // KEY_7 - '9', // KEY_9 - '_', // KEY_UNDERSCORE - '\b',// KEY_BACKSPACE - 0, // KEY_CTRL - 0, // KEY_ALT - 'x', // KEY_X - 'v', // KEY_V - 'n', // KEY_N - ',', // KEY_COMMA - '/', // KEY_SLASH - 0, // KEY_FN - 'a', // KEY_A - 'd', // KEY_D - 'g', // KEY_G - 'j', // KEY_J - 'l', // KEY_L - '\'',// KEY_APOSTROPHE - '\t',// KEY_TAB - 'w', // KEY_W - 'r', // KEY_R - 'y', // KEY_Y - 'i', // KEY_I - 'p', // KEY_P - ']', // KEY_RIGHT_BRACKET - '`', // KEY_GRAVE - '2', // KEY_2 - '4', // KEY_4 - '6', // KEY_6 - '8', // KEY_8 - '0', // KEY_0 - '=' // KEY_EQUALS -}; - -const char keymap_shifted[56] = { - 0, // KEY_OPT - 'Z', // KEY_Z - 'C', // KEY_C - 'B', // KEY_B - 'M', // KEY_M - '>', // KEY_DOT -> '>' - ' ', // KEY_SPACE - 0, // KEY_SHIFT - 'S', // KEY_S - 'F', // KEY_F - 'H', // KEY_H - 'K', // KEY_K - ':', // KEY_SEMICOLON -> ':' - '\r',// KEY_ENTER - 'Q', // KEY_Q - 'E', // KEY_E - 'T', // KEY_T - 'U', // KEY_U - 'O', // KEY_O - '{', // KEY_LEFT_BRACKET -> '{' - '|', // KEY_BACKSLASH -> '|' - '!', // KEY_1 -> '!' - '#', // KEY_3 -> '#' - '%', // KEY_5 -> '%' - '&', // KEY_7 -> '&' - '(', // KEY_9 -> '(' - '-', // KEY_UNDERSCORE -> '-' - '\b',// KEY_BACKSPACE - 0, // KEY_CTRL - 0, // KEY_ALT - 'X', // KEY_X - 'V', // KEY_V - 'N', // KEY_N - '<', // KEY_COMMA -> '<' - '?', // KEY_SLASH -> '?' - 0, // KEY_FN - 'A', // KEY_A - 'D', // KEY_D - 'G', // KEY_G - 'J', // KEY_J - 'L', // KEY_L - '"', // KEY_APOSTROPHE -> '"' - '\t',// KEY_TAB - 'W', // KEY_W - 'R', // KEY_R - 'Y', // KEY_Y - 'I', // KEY_I - 'P', // KEY_P - '}', // KEY_RIGHT_BRACKET -> '}' - '~', // KEY_GRAVE -> '~' - '@', // KEY_2 -> '@' - '$', // KEY_4 -> '$' - '^', // KEY_6 -> '^' - '*', // KEY_8 -> '*' - ')', // KEY_0 -> ')' - '+' // KEY_EQUALS -> '+' -}; diff --git a/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk b/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk index 573f947f0a8f5..ede48c2f01523 100644 --- a/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk @@ -15,4 +15,4 @@ CIRCUITPY_RCLCPY = 1 CIRCUITPY_ESPCAMERA = 0 CIRCUITPY_MAX3421E = 0 -SRC_C += boards/$(BOARD)/cardputer_keyboard.c +SRC_C += module/cardputer_keyboard.c diff --git a/ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c b/ports/espressif/module/cardputer_keyboard.c similarity index 83% rename from ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c rename to ports/espressif/module/cardputer_keyboard.c index 73880f66e19d9..548275001acc5 100644 --- a/ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c +++ b/ports/espressif/module/cardputer_keyboard.c @@ -16,7 +16,7 @@ #include "shared-module/keypad_demux/DemuxKeyMatrix.h" #include "supervisor/shared/reload.h" -#include "keymap.h" +#include "cardputer_keymap.h" //| """M5Stack Cardputer keyboard integration. //| """ @@ -31,12 +31,13 @@ //| """" //| KEYBOARD: keypad_demux.DemuxKeymatrix //| -keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard_obj; -bool cardputer_keyboard_serial_attached = false; -void cardputer_keyboard_init(void); -void keyboard_seq(const char *seq); -void update_keyboard(keypad_eventqueue_obj_t *queue); +keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard; +static bool cardputer_keyboard_serial_attached = false; + +// Forward declarations. +static void keyboard_seq(const char *seq); +static void update_keyboard(keypad_eventqueue_obj_t *queue); //| def detach_serial() -> None: //| """Stops consuming keyboard events and routing them to sys.stdin.""" @@ -44,7 +45,7 @@ void update_keyboard(keypad_eventqueue_obj_t *queue); //| static mp_obj_t detach_serial(void) { cardputer_keyboard_serial_attached = false; - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, NULL); + common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard.events, NULL); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); @@ -54,7 +55,7 @@ static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); //| ... //| static mp_obj_t attach_serial(void) { - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, update_keyboard); + common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard.events, update_keyboard); cardputer_keyboard_serial_attached = true; return mp_const_none; } @@ -79,20 +80,20 @@ static mp_obj_t key_to_char(mp_obj_t key_obj, mp_obj_t shifted_obj) { static MP_DEFINE_CONST_FUN_OBJ_2(key_to_char_obj, key_to_char); // Ring buffer of characters consumed from keyboard events (when serial attached) -ringbuf_t keyqueue; -char keybuf[32]; +static ringbuf_t keyqueue; +static char keybuf[32]; keypad_event_obj_t event; -char keystate[56]; +static char keystate[56] = {0}; // Keyboard pins -const mcu_pin_obj_t *row_addr_pins[] = { +static const mcu_pin_obj_t *row_addr_pins[] = { &pin_GPIO8, &pin_GPIO9, &pin_GPIO11, }; -const mcu_pin_obj_t *column_pins[] = { +static const mcu_pin_obj_t *column_pins[] = { &pin_GPIO13, &pin_GPIO15, &pin_GPIO3, @@ -102,22 +103,22 @@ const mcu_pin_obj_t *column_pins[] = { &pin_GPIO7 }; -void cardputer_keyboard_init(void) { - cardputer_keyboard_obj.base.type = &keypad_demux_demuxkeymatrix_type; +static void cardputer_keyboard_init(void) { common_hal_keypad_demux_demuxkeymatrix_construct( - &cardputer_keyboard_obj, // self - 3, // num_row_addr_pins - row_addr_pins, // row_addr_pins - 7, // num_column_pins - column_pins, // column_pins - true, // columns_to_anodes - false, // transpose - 0.01f, // interval - 20, // max_events - 2 // debounce_threshold + &cardputer_keyboard, // self + MP_ARRAY_SIZE(row_addr_pins), // num_row_addr_pins + row_addr_pins, // row_addr_pins + MP_ARRAY_SIZE(column_pins), // num_column_pins + column_pins, // column_pins + true, // columns_to_anodes + false, // transpose + 0.01f, // interval + 20, // max_events + 2, // debounce_threshold + false // use_gc_allocator ); - demuxkeymatrix_never_reset(&cardputer_keyboard_obj); + demuxkeymatrix_never_reset(&cardputer_keyboard); ringbuf_init(&keyqueue, (uint8_t *)keybuf, sizeof(keybuf)); attach_serial(); } @@ -150,13 +151,13 @@ char board_serial_read(void) { } } -void keyboard_seq(const char *seq) { +static void keyboard_seq(const char *seq) { while (*seq) { ringbuf_put(&keyqueue, *seq++); } } -void update_keyboard(keypad_eventqueue_obj_t *queue) { +static void update_keyboard(keypad_eventqueue_obj_t *queue) { uint8_t ascii = 0; if (common_hal_keypad_eventqueue_get_length(queue) == 0) { @@ -223,7 +224,7 @@ void update_keyboard(keypad_eventqueue_obj_t *queue) { static const mp_rom_map_elem_t cardputer_keyboard_module_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cardputer_keyboard)}, - {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard_obj)}, + {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard)}, {MP_ROM_QSTR(MP_QSTR_attach_serial), MP_ROM_PTR(&attach_serial_obj)}, {MP_ROM_QSTR(MP_QSTR_detach_serial), MP_ROM_PTR(&detach_serial_obj)}, {MP_ROM_QSTR(MP_QSTR_key_to_char), MP_ROM_PTR(&key_to_char_obj)}, diff --git a/ports/espressif/boards/m5stack_cardputer/keymap.h b/ports/espressif/module/cardputer_keymap.h similarity index 100% rename from ports/espressif/boards/m5stack_cardputer/keymap.h rename to ports/espressif/module/cardputer_keymap.h diff --git a/py/objtuple.c b/py/objtuple.c index 42e8d56a806cc..e0b31edaf2376 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -250,6 +250,9 @@ MP_DEFINE_CONST_OBJ_TYPE( // the zero-length tuple const mp_obj_tuple_t mp_const_empty_tuple_obj = {{&mp_type_tuple}, 0}; +// CIRCUITPY-CHANGE: No change here, but implementation was copied for +// mp_obj_new_port_tuple in supervisor/shared/port.c, which allocates using port_malloc(). +// Change that to match if this changes. mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items) { if (n == 0) { return mp_const_empty_tuple; diff --git a/shared-bindings/keypad/EventQueue.h b/shared-bindings/keypad/EventQueue.h index 893165e3220f1..02c3414b0764c 100644 --- a/shared-bindings/keypad/EventQueue.h +++ b/shared-bindings/keypad/EventQueue.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t keypad_eventqueue_type; -void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events); +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events, bool use_gc_allocator); void common_hal_keypad_eventqueue_clear(keypad_eventqueue_obj_t *self); size_t common_hal_keypad_eventqueue_get_length(keypad_eventqueue_obj_t *self); diff --git a/shared-bindings/keypad_demux/DemuxKeyMatrix.c b/shared-bindings/keypad_demux/DemuxKeyMatrix.c index d76a20aec9d01..f1b5ecae9ceb6 100644 --- a/shared-bindings/keypad_demux/DemuxKeyMatrix.c +++ b/shared-bindings/keypad_demux/DemuxKeyMatrix.c @@ -123,7 +123,8 @@ static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type, column_pins_array[column] = pin; } - common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, args[ARG_transpose].u_bool, interval, max_events, debounce_threshold); + // Last arg is use_gc_allocator, true during VM use. + common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, args[ARG_transpose].u_bool, interval, max_events, debounce_threshold, true); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/keypad_demux/DemuxKeyMatrix.h b/shared-bindings/keypad_demux/DemuxKeyMatrix.h index 8bdaa597dc035..25d36283e90fc 100644 --- a/shared-bindings/keypad_demux/DemuxKeyMatrix.h +++ b/shared-bindings/keypad_demux/DemuxKeyMatrix.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t keypad_demux_demuxkeymatrix_type; -void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold); +void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator); void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_obj_t *self); diff --git a/shared-module/keypad/EventQueue.c b/shared-module/keypad/EventQueue.c index e5b362a045ddf..3130d8b59a1d7 100644 --- a/shared-module/keypad/EventQueue.c +++ b/shared-module/keypad/EventQueue.c @@ -15,9 +15,14 @@ #define EVENT_SIZE_BYTES (sizeof(uint16_t) + sizeof(mp_obj_t)) -void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events) { +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events, bool use_gc_allocator) { // Event queue is 16-bit values. - ringbuf_alloc(&self->encoded_events, max_events * EVENT_SIZE_BYTES); + const size_t size = max_events * EVENT_SIZE_BYTES; + if (use_gc_allocator) { + ringbuf_alloc(&self->encoded_events, size); + } else { + ringbuf_init(&self->encoded_events, port_malloc(size, false), size); + } self->overflowed = false; self->event_handler = NULL; } diff --git a/shared-module/keypad/KeyMatrix.c b/shared-module/keypad/KeyMatrix.c index 67a5546853552..28830f591b129 100644 --- a/shared-module/keypad/KeyMatrix.c +++ b/shared-module/keypad/KeyMatrix.c @@ -56,7 +56,7 @@ void common_hal_keypad_keymatrix_construct(keypad_keymatrix_obj_t *self, mp_uint self->columns_to_anodes = columns_to_anodes; self->funcs = &keymatrix_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self) { diff --git a/shared-module/keypad/Keys.c b/shared-module/keypad/Keys.c index 39d50f95e3238..80dcf36e163ad 100644 --- a/shared-module/keypad/Keys.c +++ b/shared-module/keypad/Keys.c @@ -41,7 +41,7 @@ void common_hal_keypad_keys_construct(keypad_keys_obj_t *self, mp_uint_t num_pin self->value_when_pressed = value_when_pressed; self->funcs = &keys_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } diff --git a/shared-module/keypad/ShiftRegisterKeys.c b/shared-module/keypad/ShiftRegisterKeys.c index 97917434ff81b..aa0c14dd36b50 100644 --- a/shared-module/keypad/ShiftRegisterKeys.c +++ b/shared-module/keypad/ShiftRegisterKeys.c @@ -71,7 +71,7 @@ void common_hal_keypad_shiftregisterkeys_construct(keypad_shiftregisterkeys_obj_ self->value_when_pressed = value_when_pressed; self->funcs = &shiftregisterkeys_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } void common_hal_keypad_shiftregisterkeys_deinit(keypad_shiftregisterkeys_obj_t *self) { diff --git a/shared-module/keypad/__init__.c b/shared-module/keypad/__init__.c index 3ffa0433174cf..f1d5d395df789 100644 --- a/shared-module/keypad/__init__.c +++ b/shared-module/keypad/__init__.c @@ -85,14 +85,20 @@ void keypad_deregister_scanner(keypad_scanner_obj_t *scanner) { supervisor_release_lock(&keypad_scanners_linked_list_lock); } -void keypad_construct_common(keypad_scanner_obj_t *self, mp_float_t interval, size_t max_events, uint8_t debounce_threshold) { +void keypad_construct_common(keypad_scanner_obj_t *self, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator) { size_t key_count = common_hal_keypad_generic_get_key_count(self); - self->debounce_counter = (int8_t *)m_malloc_without_collect(sizeof(int8_t) * key_count); + self->debounce_counter = + use_gc_allocator + ? (int8_t *)m_malloc_without_collect(sizeof(int8_t) * key_count) + : (int8_t *)port_malloc_zero(sizeof(int8_t) * key_count, false); self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) - keypad_eventqueue_obj_t *events = mp_obj_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type); - common_hal_keypad_eventqueue_construct(events, max_events); + keypad_eventqueue_obj_t *events = + use_gc_allocator + ? mp_obj_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type) + : mp_obj_port_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type); + common_hal_keypad_eventqueue_construct(events, max_events, use_gc_allocator); self->events = events; self->debounce_threshold = debounce_threshold; diff --git a/shared-module/keypad/__init__.h b/shared-module/keypad/__init__.h index a7d21753f6625..66fea4878543b 100644 --- a/shared-module/keypad/__init__.h +++ b/shared-module/keypad/__init__.h @@ -39,7 +39,7 @@ void keypad_reset(void); void keypad_register_scanner(keypad_scanner_obj_t *scanner); void keypad_deregister_scanner(keypad_scanner_obj_t *scanner); -void keypad_construct_common(keypad_scanner_obj_t *scanner, mp_float_t interval, size_t max_events, uint8_t debounce_cycles); +void keypad_construct_common(keypad_scanner_obj_t *scanner, mp_float_t interval, size_t max_events, uint8_t debounce_cycles, bool use_gc_allocator); bool keypad_debounce(keypad_scanner_obj_t *self, mp_uint_t key_number, bool current); void keypad_never_reset(keypad_scanner_obj_t *self); diff --git a/shared-module/keypad_demux/DemuxKeyMatrix.c b/shared-module/keypad_demux/DemuxKeyMatrix.c index b90669d772fcd..329f7679d3f76 100644 --- a/shared-module/keypad_demux/DemuxKeyMatrix.c +++ b/shared-module/keypad_demux/DemuxKeyMatrix.c @@ -31,38 +31,45 @@ static mp_uint_t row_column_to_key_number(keypad_demux_demuxkeymatrix_obj_t *sel return row * common_hal_keypad_demux_demuxkeymatrix_get_column_count(self) + column; } -void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold) { - +void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator) { // the multiplexed pins are outputs so we can set the address for the target row // the sense of the address pins themselves doesn't change with columns_to_anodes // but the value output on the selected row line will be !columns_to_anodes mp_obj_t row_addr_dios[num_row_addr_pins]; for (size_t row = 0; row < num_row_addr_pins; row++) { digitalio_digitalinout_obj_t *dio = - mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); + use_gc_allocator + ? mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type) + : mp_obj_port_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); common_hal_digitalio_digitalinout_construct(dio, row_addr_pins[row]); common_hal_digitalio_digitalinout_switch_to_output(dio, false, DRIVE_MODE_PUSH_PULL); row_addr_dios[row] = dio; } - self->row_addr_digitalinouts = mp_obj_new_tuple(num_row_addr_pins, row_addr_dios); + self->row_addr_digitalinouts = + use_gc_allocator + ? mp_obj_new_tuple(num_row_addr_pins, row_addr_dios) + : mp_obj_new_port_tuple(num_row_addr_pins, row_addr_dios); // the column pins are always inputs, with default state based on columns_to_anodes mp_obj_t column_dios[num_column_pins]; for (size_t column = 0; column < num_column_pins; column++) { digitalio_digitalinout_obj_t *dio = - mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); - dio->base.type = &digitalio_digitalinout_type; + use_gc_allocator + ? mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type) + : mp_obj_port_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); common_hal_digitalio_digitalinout_construct(dio, column_pins[column]); common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN); column_dios[column] = dio; } - self->column_digitalinouts = mp_obj_new_tuple(num_column_pins, column_dios); - + self->column_digitalinouts = + use_gc_allocator + ? mp_obj_new_tuple(num_column_pins, column_dios) + : mp_obj_new_port_tuple(num_column_pins, column_dios); self->columns_to_anodes = columns_to_anodes; self->transpose = transpose; self->funcs = &keymatrix_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, use_gc_allocator); } void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_obj_t *self) { diff --git a/supervisor/board.h b/supervisor/board.h index 0920bf4e5777b..1200eccc9b68c 100644 --- a/supervisor/board.h +++ b/supervisor/board.h @@ -25,3 +25,10 @@ void reset_board(void); // state. It should not prevent the user access method from working (such as // disabling USB, BLE or flash) because CircuitPython may continue to run. void board_deinit(void); + +// Initialization for individual boards when a VM starts. +void mp_board_init(void); + +// Some boards may want to mark additional pointers as gc roots. +// A default weak implementation is provided that does nothing. +void board_gc_collect(void); diff --git a/supervisor/port.h b/supervisor/port.h index cc49538218fac..0bf8fd8d01350 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -9,6 +9,7 @@ #include #include +#include "py/obj.h" #include "supervisor/shared/safe_mode.h" // Provided by the linker; @@ -107,3 +108,10 @@ void port_gc_collect(void); // this function to sense the button. Ports that need to can override this // function to provide their own implementation. bool port_boot_button_pressed(void); + +// Allocating objects on the port heap, not the VM heap. +#define mp_obj_port_malloc(struct_type, obj_type) ((struct_type *)mp_obj_port_malloc_helper(sizeof(struct_type), obj_type)) +#define mp_obj_port_malloc_var(struct_type, var_field, var_type, var_num, obj_type) ((struct_type *)mp_obj_port_malloc_helper(offsetof(struct_type, var_field) + sizeof(var_type) * (var_num), obj_type)) + +void *mp_obj_port_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); +mp_obj_t mp_obj_new_port_tuple(size_t n, const mp_obj_t *items); diff --git a/supervisor/port_heap.h b/supervisor/port_heap.h index 07a3c884e241e..40abd21438741 100644 --- a/supervisor/port_heap.h +++ b/supervisor/port_heap.h @@ -21,6 +21,7 @@ void port_heap_init(void); void *port_malloc(size_t size, bool dma_capable); +void *port_malloc_zero(size_t size, bool dma_capable); void port_free(void *ptr); diff --git a/supervisor/shared/board.c b/supervisor/shared/board.c index 192bf7a65c806..40bc1e45a1049 100644 --- a/supervisor/shared/board.c +++ b/supervisor/shared/board.c @@ -44,3 +44,11 @@ MP_WEAK void board_deinit(void) { // Do-nothing so not all boards need to provide this function. MP_WEAK void reset_board(void) { } + +// Do-nothing so not all boards need to provide this function. +MP_WEAK void mp_board_init(void) { +} + +// Do-nothing so not all boards need to provide this function. +MP_WEAK void board_gc_collect(void) { +} diff --git a/supervisor/shared/port.c b/supervisor/shared/port.c index 3b9f0c8718161..aabf003fb302d 100644 --- a/supervisor/shared/port.c +++ b/supervisor/shared/port.c @@ -43,6 +43,15 @@ MP_WEAK void *port_malloc(size_t size, bool dma_capable) { return block; } +// Ensure allocated memory is zero. +MP_WEAK void *port_malloc_zero(size_t size, bool dma_capable) { + void *ptr = port_malloc(size, dma_capable); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + MP_WEAK void port_free(void *ptr) { tlsf_free(heap, ptr); } @@ -80,3 +89,30 @@ MP_WEAK bool port_boot_button_pressed(void) { return false; #endif } + +// Ports may provide an implementation of this function if it is needed +MP_WEAK void port_gc_collect(void) { +} + +// Allocates an object in the port heap, not the VM heap, and also sets type, for mp_obj_malloc{,_var} macros. +MP_NOINLINE void *mp_obj_port_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) { + mp_obj_base_t *base = (mp_obj_base_t *)port_malloc_zero(num_bytes, false); + base->type = type; + return base; +} + +// Creates a tuple on the port heap, not the VM heap. +// Implementation copied from py/objtuple.c. +mp_obj_t mp_obj_new_port_tuple(size_t n, const mp_obj_t *items) { + if (n == 0) { + return mp_const_empty_tuple; + } + mp_obj_tuple_t *o = mp_obj_port_malloc_var(mp_obj_tuple_t, items, mp_obj_t, n, &mp_type_tuple); + o->len = n; + if (items) { + for (size_t i = 0; i < n; i++) { + o->items[i] = items[i]; + } + } + return MP_OBJ_FROM_PTR(o); +} From 1d8352845684fefb22fd888bcf255ef23dc28791 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 9 May 2025 14:34:09 +1000 Subject: [PATCH 082/517] lib/littlefs: Fix string initializer in lfs1.c. Avoids the new Wunterminated-string-literal when compiled with gcc 15.1. It would be preferable to just disable this warning, but Clang -Wunknown-warning-option kicks in even when disabling warnings so this becomes fiddly to apply. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- lib/littlefs/lfs1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/littlefs/lfs1.c b/lib/littlefs/lfs1.c index 6a3fd670012cc..ec18dc470258c 100644 --- a/lib/littlefs/lfs1.c +++ b/lib/littlefs/lfs1.c @@ -2141,7 +2141,7 @@ int lfs1_format(lfs1_t *lfs1, const struct lfs1_config *cfg) { .d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4, .d.nlen = sizeof(superblock.d.magic), .d.version = LFS1_DISK_VERSION, - .d.magic = {"littlefs"}, + .d.magic = {'l', 'i', 't', 't', 'l', 'e', 'f', 's'}, .d.block_size = lfs1->cfg->block_size, .d.block_count = lfs1->cfg->block_count, .d.root = {lfs1->root[0], lfs1->root[1]}, From 1630f215b709da2c98a4aa521833b1ccbf7e0c3d Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 9 May 2025 13:36:05 +1000 Subject: [PATCH 083/517] py/emitinlinethumb: Refactor string literal as array initializer. Avoids the new Wunterminated-string-literal when compiled with gcc 15.1. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton --- py/emitinlinethumb.c | 108 +++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 7818bb4f46da8..d6596337ae5a6 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -150,27 +150,27 @@ typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; static const reg_name_t reg_name_table[] = { - {0, "r0\0"}, - {1, "r1\0"}, - {2, "r2\0"}, - {3, "r3\0"}, - {4, "r4\0"}, - {5, "r5\0"}, - {6, "r6\0"}, - {7, "r7\0"}, - {8, "r8\0"}, - {9, "r9\0"}, - {10, "r10"}, - {11, "r11"}, - {12, "r12"}, - {13, "r13"}, - {14, "r14"}, - {15, "r15"}, - {10, "sl\0"}, - {11, "fp\0"}, - {13, "sp\0"}, - {14, "lr\0"}, - {15, "pc\0"}, + {0, {'r', '0' }}, + {1, {'r', '1' }}, + {2, {'r', '2' }}, + {3, {'r', '3' }}, + {4, {'r', '4' }}, + {5, {'r', '5' }}, + {6, {'r', '6' }}, + {7, {'r', '7' }}, + {8, {'r', '8' }}, + {9, {'r', '9' }}, + {10, {'r', '1', '0' }}, + {11, {'r', '1', '1' }}, + {12, {'r', '1', '2' }}, + {13, {'r', '1', '3' }}, + {14, {'r', '1', '4' }}, + {15, {'r', '1', '5' }}, + {10, {'s', 'l' }}, + {11, {'f', 'p' }}, + {13, {'s', 'p' }}, + {14, {'l', 'r' }}, + {15, {'p', 'c' }}, }; #define MAX_SPECIAL_REGISTER_NAME_LENGTH 7 @@ -368,20 +368,20 @@ typedef struct _cc_name_t { byte cc; byte name[2]; } cc_name_t; static const cc_name_t cc_name_table[] = { - { ASM_THUMB_CC_EQ, "eq" }, - { ASM_THUMB_CC_NE, "ne" }, - { ASM_THUMB_CC_CS, "cs" }, - { ASM_THUMB_CC_CC, "cc" }, - { ASM_THUMB_CC_MI, "mi" }, - { ASM_THUMB_CC_PL, "pl" }, - { ASM_THUMB_CC_VS, "vs" }, - { ASM_THUMB_CC_VC, "vc" }, - { ASM_THUMB_CC_HI, "hi" }, - { ASM_THUMB_CC_LS, "ls" }, - { ASM_THUMB_CC_GE, "ge" }, - { ASM_THUMB_CC_LT, "lt" }, - { ASM_THUMB_CC_GT, "gt" }, - { ASM_THUMB_CC_LE, "le" }, + { ASM_THUMB_CC_EQ, { 'e', 'q' }}, + { ASM_THUMB_CC_NE, { 'n', 'e' }}, + { ASM_THUMB_CC_CS, { 'c', 's' }}, + { ASM_THUMB_CC_CC, { 'c', 'c' }}, + { ASM_THUMB_CC_MI, { 'm', 'i' }}, + { ASM_THUMB_CC_PL, { 'p', 'l' }}, + { ASM_THUMB_CC_VS, { 'v', 's' }}, + { ASM_THUMB_CC_VC, { 'v', 'c' }}, + { ASM_THUMB_CC_HI, { 'h', 'i' }}, + { ASM_THUMB_CC_LS, { 'l', 's' }}, + { ASM_THUMB_CC_GE, { 'g', 'e' }}, + { ASM_THUMB_CC_LT, { 'l', 't' }}, + { ASM_THUMB_CC_GT, { 'g', 't' }}, + { ASM_THUMB_CC_LE, { 'l', 'e' }}, }; typedef struct _format_4_op_t { byte op; @@ -389,21 +389,21 @@ typedef struct _format_4_op_t { byte op; } format_4_op_t; #define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops static const format_4_op_t format_4_op_table[] = { - { X(ASM_THUMB_FORMAT_4_EOR), "eor" }, - { X(ASM_THUMB_FORMAT_4_LSL), "lsl" }, - { X(ASM_THUMB_FORMAT_4_LSR), "lsr" }, - { X(ASM_THUMB_FORMAT_4_ASR), "asr" }, - { X(ASM_THUMB_FORMAT_4_ADC), "adc" }, - { X(ASM_THUMB_FORMAT_4_SBC), "sbc" }, - { X(ASM_THUMB_FORMAT_4_ROR), "ror" }, - { X(ASM_THUMB_FORMAT_4_TST), "tst" }, - { X(ASM_THUMB_FORMAT_4_NEG), "neg" }, - { X(ASM_THUMB_FORMAT_4_CMP), "cmp" }, - { X(ASM_THUMB_FORMAT_4_CMN), "cmn" }, - { X(ASM_THUMB_FORMAT_4_ORR), "orr" }, - { X(ASM_THUMB_FORMAT_4_MUL), "mul" }, - { X(ASM_THUMB_FORMAT_4_BIC), "bic" }, - { X(ASM_THUMB_FORMAT_4_MVN), "mvn" }, + { X(ASM_THUMB_FORMAT_4_EOR), {'e', 'o', 'r' }}, + { X(ASM_THUMB_FORMAT_4_LSL), {'l', 's', 'l' }}, + { X(ASM_THUMB_FORMAT_4_LSR), {'l', 's', 'r' }}, + { X(ASM_THUMB_FORMAT_4_ASR), {'a', 's', 'r' }}, + { X(ASM_THUMB_FORMAT_4_ADC), {'a', 'd', 'c' }}, + { X(ASM_THUMB_FORMAT_4_SBC), {'s', 'b', 'c' }}, + { X(ASM_THUMB_FORMAT_4_ROR), {'r', 'o', 'r' }}, + { X(ASM_THUMB_FORMAT_4_TST), {'t', 's', 't' }}, + { X(ASM_THUMB_FORMAT_4_NEG), {'n', 'e', 'g' }}, + { X(ASM_THUMB_FORMAT_4_CMP), {'c', 'm', 'p' }}, + { X(ASM_THUMB_FORMAT_4_CMN), {'c', 'm', 'n' }}, + { X(ASM_THUMB_FORMAT_4_ORR), {'o', 'r', 'r' }}, + { X(ASM_THUMB_FORMAT_4_MUL), {'m', 'u', 'l' }}, + { X(ASM_THUMB_FORMAT_4_BIC), {'b', 'i', 'c' }}, + { X(ASM_THUMB_FORMAT_4_MVN), {'m', 'v', 'n' }}, }; #undef X @@ -428,10 +428,10 @@ typedef struct _format_vfp_op_t { char name[3]; } format_vfp_op_t; static const format_vfp_op_t format_vfp_op_table[] = { - { 0x30, "add" }, - { 0x34, "sub" }, - { 0x20, "mul" }, - { 0x80, "div" }, + { 0x30, {'a', 'd', 'd' }}, + { 0x34, {'s', 'u', 'b' }}, + { 0x20, {'m', 'u', 'l' }}, + { 0x80, {'d', 'i', 'v' }}, }; // shorthand alias for whether we allow ARMv7-M instructions From 8e6cf02761e39801e9f9bca9fb7f86e724716b53 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 3 Sep 2025 12:37:56 -0400 Subject: [PATCH 084/517] Remove unused pins; group aliased pins --- .../boards/prokyber_ai_on_the_edge_cam/pins.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c index 305bdb3e0b69a..68dc40781a480 100644 --- a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c @@ -34,24 +34,32 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { // SD { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO3) }, { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO41) }, { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO42) }, { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, // Ethernet { MP_ROM_QSTR(MP_QSTR_ETH_RST), MP_ROM_PTR(&pin_GPIO45) }, { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_ETH_INT), MP_ROM_PTR(&pin_GPIO38) }, { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_ETH_MOSI), MP_ROM_PTR(&pin_GPIO1) }, { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_ETH_MISO), MP_ROM_PTR(&pin_GPIO14) }, { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_ETH_CLK), MP_ROM_PTR(&pin_GPIO21) }, { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_ETH_CS), MP_ROM_PTR(&pin_GPIO39) }, { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, @@ -65,6 +73,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { // UART { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, @@ -86,8 +95,6 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA5), MP_ROM_PTR(&pin_GPIO10) }, { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA3), MP_ROM_PTR(&pin_GPIO9) }, { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA4), MP_ROM_PTR(&pin_GPIO8) }, - // { MP_ROM_QSTR(MP_QSTR_CAMERA_RESET), MP_ROM_PTR(&pin_GPIO47) }, - // { MP_ROM_QSTR(MP_QSTR_CAMERA_PWDN), MP_ROM_PTR(&pin_GPIO21) }, { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOD), MP_ROM_PTR(&pin_GPIO4) }, // SDA { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOC), MP_ROM_PTR(&pin_GPIO5) }, // SCL From a16137f9619e70e26bef27e3c676796f2c2ba5d9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 3 Sep 2025 14:42:49 -0400 Subject: [PATCH 085/517] sdkconfig.defaults: use standard defaults --- ports/espressif/esp-idf-config/sdkconfig.defaults | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ports/espressif/esp-idf-config/sdkconfig.defaults b/ports/espressif/esp-idf-config/sdkconfig.defaults index b54f599b2a5b7..dd766675136d9 100644 --- a/ports/espressif/esp-idf-config/sdkconfig.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig.defaults @@ -71,16 +71,6 @@ CONFIG_LWIP_IPV6_AUTOCONFIG=y CONFIG_LWIP_IPV6_RDNSS_MAX_DNS_SERVERS=0 CONFIG_LWIP_IPV6_DHCP6=y # -# TCP -# -CONFIG_LWIP_MAX_ACTIVE_TCP=4 -CONFIG_LWIP_MAX_LISTENING_TCP=4 -CONFIG_LWIP_TCP_SYNMAXRTX=6 -CONFIG_LWIP_TCP_SND_BUF_DEFAULT=2880 -CONFIG_LWIP_TCP_WND_DEFAULT=2880 -CONFIG_LWIP_TCP_RTO_TIME=3000 -# end of TCP - # end of LWIP # From 8993356f0532c345b70f07c72e218fceea27bce9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 3 Sep 2025 19:54:33 -0400 Subject: [PATCH 086/517] remove unused mp_board_init() and board_gc_collect() per review comments --- main.c | 4 ---- supervisor/board.h | 7 ------- supervisor/shared/board.c | 8 -------- 3 files changed, 19 deletions(-) diff --git a/main.c b/main.c index cf9cf377d690b..6c8e9c8804906 100644 --- a/main.c +++ b/main.c @@ -207,9 +207,6 @@ static void start_mp(safe_mode_t safe_mode) { // Always return to root common_hal_os_chdir("/"); - - // Initialization for individual boards when a VM starts. - mp_board_init(); } static void stop_mp(void) { @@ -1152,7 +1149,6 @@ void gc_collect(void) { gc_collect_root((void **)&MP_STATE_VM(vfs_mount_table), sizeof(mp_vfs_mount_t) / sizeof(mp_uint_t)); port_gc_collect(); - board_gc_collect(); background_callback_gc_collect(); diff --git a/supervisor/board.h b/supervisor/board.h index 1200eccc9b68c..0920bf4e5777b 100644 --- a/supervisor/board.h +++ b/supervisor/board.h @@ -25,10 +25,3 @@ void reset_board(void); // state. It should not prevent the user access method from working (such as // disabling USB, BLE or flash) because CircuitPython may continue to run. void board_deinit(void); - -// Initialization for individual boards when a VM starts. -void mp_board_init(void); - -// Some boards may want to mark additional pointers as gc roots. -// A default weak implementation is provided that does nothing. -void board_gc_collect(void); diff --git a/supervisor/shared/board.c b/supervisor/shared/board.c index 40bc1e45a1049..192bf7a65c806 100644 --- a/supervisor/shared/board.c +++ b/supervisor/shared/board.c @@ -44,11 +44,3 @@ MP_WEAK void board_deinit(void) { // Do-nothing so not all boards need to provide this function. MP_WEAK void reset_board(void) { } - -// Do-nothing so not all boards need to provide this function. -MP_WEAK void mp_board_init(void) { -} - -// Do-nothing so not all boards need to provide this function. -MP_WEAK void board_gc_collect(void) { -} From eaca0bb751e2dd07deb4e1e1233f1677502580d3 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 5 Sep 2025 15:05:50 -0400 Subject: [PATCH 087/517] ports/raspberrypi/lwip_src/lwip_mem.c: put storage ops in a critical section --- ports/raspberrypi/lwip_src/lwip_mem.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/raspberrypi/lwip_src/lwip_mem.c b/ports/raspberrypi/lwip_src/lwip_mem.c index 244f51e289bc9..d7d5468a42011 100644 --- a/ports/raspberrypi/lwip_src/lwip_mem.c +++ b/ports/raspberrypi/lwip_src/lwip_mem.c @@ -8,14 +8,20 @@ #include #include "lib/tlsf/tlsf.h" #include "lwip_mem.h" +#include "shared-bindings/microcontroller/__init__.h" #include "supervisor/port_heap.h" void *lwip_heap_malloc(size_t size) { - return port_malloc(size, true); + common_hal_mcu_disable_interrupts(); + void *ptr = port_malloc(size, true); + common_hal_mcu_enable_interrupts(); + return ptr; } void lwip_heap_free(void *ptr) { + common_hal_mcu_disable_interrupts(); port_free(ptr); + common_hal_mcu_enable_interrupts(); } void *lwip_heap_calloc(size_t num, size_t size) { From 05db6eefb2f08e579d5c03d462e507a1ba5c8a57 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Tue, 2 Sep 2025 22:16:35 +0000 Subject: [PATCH 088/517] fix bad xfer_result_t enum values shared-module/usb/core/Device.c was using its own 0xff value for the xfer_result_t enum defined by tinyusb/src/common/tusb_types.h. The 0xff value served the same purpose as the already exisiting XFER_RESULT_INVALID enum value (a placeholder to mark in-progress transactions). This commit standardizes on XFER_RESULT_INVALID in usb.core.Device consistent with the usage in tinyusb. Making this change allows implementing `switch(result){...}` style result code checks without compiler errors about 0xff not being a valid value for the enum. --- shared-module/usb/core/Device.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index c9df0cf73805f..f6cd03ebcd40a 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -45,7 +45,7 @@ bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t d } self->device_address = device_address; self->first_langid = 0; - _xfer_result = 0xff; + _xfer_result = XFER_RESULT_INVALID; return true; } @@ -91,13 +91,13 @@ static void _transfer_done_cb(tuh_xfer_t *xfer) { static bool _wait_for_callback(void) { while (!mp_hal_is_interrupted() && - _xfer_result == 0xff) { + _xfer_result == XFER_RESULT_INVALID) { // The background tasks include TinyUSB which will call the function // we provided above. In other words, the callback isn't in an interrupt. RUN_BACKGROUND_TASKS; } xfer_result_t result = _xfer_result; - _xfer_result = 0xff; + _xfer_result = XFER_RESULT_INVALID; return result == XFER_RESULT_SUCCESS; } @@ -225,7 +225,7 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m } static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) { - _xfer_result = 0xff; + _xfer_result = XFER_RESULT_INVALID; xfer->complete_cb = _transfer_done_cb; if (!tuh_edpt_xfer(xfer)) { mp_raise_usb_core_USBError(NULL); @@ -234,7 +234,7 @@ static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) { uint32_t start_time = supervisor_ticks_ms32(); while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && !mp_hal_is_interrupted() && - _xfer_result == 0xff) { + _xfer_result == XFER_RESULT_INVALID) { // The background tasks include TinyUSB which will call the function // we provided above. In other words, the callback isn't in an interrupt. RUN_BACKGROUND_TASKS; @@ -244,11 +244,11 @@ static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) { return 0; } xfer_result_t result = _xfer_result; - _xfer_result = 0xff; + _xfer_result = XFER_RESULT_INVALID; if (result == XFER_RESULT_STALLED) { mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); } - if (result == 0xff) { + if (result == XFER_RESULT_INVALID) { tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); mp_raise_usb_core_USBTimeoutError(); } @@ -355,7 +355,7 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, .complete_cb = _transfer_done_cb, }; - _xfer_result = 0xff; + _xfer_result = XFER_RESULT_INVALID; if (!tuh_control_xfer(&xfer)) { mp_raise_usb_core_USBError(NULL); @@ -364,7 +364,7 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, uint32_t start_time = supervisor_ticks_ms32(); while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && !mp_hal_is_interrupted() && - _xfer_result == 0xff) { + _xfer_result == XFER_RESULT_INVALID) { // The background tasks include TinyUSB which will call the function // we provided above. In other words, the callback isn't in an interrupt. RUN_BACKGROUND_TASKS; @@ -374,11 +374,11 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, return 0; } xfer_result_t result = _xfer_result; - _xfer_result = 0xff; + _xfer_result = XFER_RESULT_INVALID; if (result == XFER_RESULT_STALLED) { mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); } - if (result == 0xff) { + if (result == XFER_RESULT_INVALID) { tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); mp_raise_usb_core_USBTimeoutError(); } From b0760773077aaab556394061fde17168cbf52df3 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Tue, 2 Sep 2025 22:40:46 +0000 Subject: [PATCH 089/517] use switch for ctrl_transfer result check This directly translates the Device.ctrl_transfer() result check logic from its old if-statements to an equivalent switch-statement. The point is to make it clear how each possible result code is handled. Note that XFER_RESULT_FAILED and XFER_RESULT_TIMEOUT both return 0 without generating any exception. (but also, tinyusb may not actually use XFER_RESULT_TIMOUT if its comments are still accurate) --- shared-module/usb/core/Device.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index f6cd03ebcd40a..87d1141bfde39 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -375,17 +375,21 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, } xfer_result_t result = _xfer_result; _xfer_result = XFER_RESULT_INVALID; - if (result == XFER_RESULT_STALLED) { - mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); - } - if (result == XFER_RESULT_INVALID) { - tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); - mp_raise_usb_core_USBTimeoutError(); - } - if (result == XFER_RESULT_SUCCESS) { - return len; + switch (result) { + case XFER_RESULT_SUCCESS: + return len; + case XFER_RESULT_FAILED: + break; + case XFER_RESULT_STALLED: + mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); + break; + case XFER_RESULT_TIMEOUT: + break; + case XFER_RESULT_INVALID: + tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); + mp_raise_usb_core_USBTimeoutError(); + break; } - return 0; } From e9da6bce6dd037f4befe9c5966cd81b4960f9a7d Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:02:39 +0000 Subject: [PATCH 090/517] return actual length from ctrl_transfer Previously this returned the requested transfer length argument, ignoring the actual length of transferred bytes. This changes to returning the actual length. --- shared-module/usb/core/Device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index 87d1141bfde39..517eb5f357a07 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -355,7 +355,11 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, .complete_cb = _transfer_done_cb, }; + // Prepare for transfer. Unless there is a timeout, these static globals will + // get modified by the _transfer_done_cb() callback when tinyusb finishes the + // transfer or encounters an error condition. _xfer_result = XFER_RESULT_INVALID; + _actual_len = 0; if (!tuh_control_xfer(&xfer)) { mp_raise_usb_core_USBError(NULL); @@ -377,7 +381,7 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, _xfer_result = XFER_RESULT_INVALID; switch (result) { case XFER_RESULT_SUCCESS: - return len; + return _actual_len; case XFER_RESULT_FAILED: break; case XFER_RESULT_STALLED: From f3560f98b5527e469977b5d450b3dd813df06d1e Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Wed, 3 Sep 2025 00:41:44 +0000 Subject: [PATCH 091/517] handle XFER_RESULT_FAILED with exception Previously, usb.core.Device.ctrl_transfer() did not raise an exception when TinyUSB returned an XFER_RESULT_FAILED result code. This change raises an exception which prevents a failed transfer from returning an all zero result buffer. --- shared-module/usb/core/Device.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index 517eb5f357a07..79b6775dd0c1a 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -374,22 +374,30 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, RUN_BACKGROUND_TASKS; } if (mp_hal_is_interrupted()) { + // Handle case of VM being interrupted by Ctrl-C or autoreload tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); return 0; } + // Handle control transfer result code from TinyUSB xfer_result_t result = _xfer_result; _xfer_result = XFER_RESULT_INVALID; switch (result) { case XFER_RESULT_SUCCESS: return _actual_len; case XFER_RESULT_FAILED: + mp_raise_usb_core_USBError(NULL); break; case XFER_RESULT_STALLED: mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); break; case XFER_RESULT_TIMEOUT: + // This timeout comes from TinyUSB, so assume that it has stopped the + // transfer (note: timeout logic may be unimplemented on TinyUSB side) + mp_raise_usb_core_USBTimeoutError(); break; case XFER_RESULT_INVALID: + // This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB + // to stop the transfer tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); mp_raise_usb_core_USBTimeoutError(); break; From 7d26fe986600718ac687f7bc2230c679ef3d6f2a Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Wed, 3 Sep 2025 01:50:48 +0000 Subject: [PATCH 092/517] factor out timed transfer setup & err handling The code for endpoint transfers and control transfers previously had a bunch of duplicated logic for setup, timeout checking, and result code error handling. This change factors that stuff out into functions, using my new transfer result error handling code from the last commit. Now the endpoint and control transfers can share the setup, timeout, and error check logic. For me, this refactor reduced the firmware image size by 176 bytes. --- shared-module/usb/core/Device.c | 128 ++++++++++++++------------------ 1 file changed, 57 insertions(+), 71 deletions(-) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index 79b6775dd0c1a..f71ac8844bb73 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -101,6 +101,59 @@ static bool _wait_for_callback(void) { return result == XFER_RESULT_SUCCESS; } +static void _prepare_for_transfer(void) { + // Prepare for transfer. Unless there is a timeout, these static globals will + // get modified by the _transfer_done_cb() callback when tinyusb finishes the + // transfer or encounters an error condition. + _xfer_result = XFER_RESULT_INVALID; + _actual_len = 0; +} + +static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout) { + if (xfer == NULL) { + mp_raise_usb_core_USBError(NULL); + return 0; + } + uint32_t start_time = supervisor_ticks_ms32(); + while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && + !mp_hal_is_interrupted() && + _xfer_result == XFER_RESULT_INVALID) { + // The background tasks include TinyUSB which will call the function + // we provided above. In other words, the callback isn't in an interrupt. + RUN_BACKGROUND_TASKS; + } + if (mp_hal_is_interrupted()) { + // Handle case of VM being interrupted by Ctrl-C or autoreload + tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); + return 0; + } + // Handle control transfer result code from TinyUSB + xfer_result_t result = _xfer_result; + _xfer_result = XFER_RESULT_INVALID; + switch (result) { + case XFER_RESULT_SUCCESS: + return _actual_len; + case XFER_RESULT_FAILED: + mp_raise_usb_core_USBError(NULL); + break; + case XFER_RESULT_STALLED: + mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); + break; + case XFER_RESULT_TIMEOUT: + // This timeout comes from TinyUSB, so assume that it has stopped the + // transfer (note: timeout logic may be unimplemented on TinyUSB side) + mp_raise_usb_core_USBTimeoutError(); + break; + case XFER_RESULT_INVALID: + // This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB + // to stop the transfer + tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); + mp_raise_usb_core_USBTimeoutError(); + break; + } + return 0; +} + static mp_obj_t _get_string(const uint16_t *temp_buf) { size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t); if (utf16_len == 0) { @@ -225,38 +278,13 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m } static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) { - _xfer_result = XFER_RESULT_INVALID; + _prepare_for_transfer(); xfer->complete_cb = _transfer_done_cb; if (!tuh_edpt_xfer(xfer)) { mp_raise_usb_core_USBError(NULL); return 0; } - uint32_t start_time = supervisor_ticks_ms32(); - while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && - !mp_hal_is_interrupted() && - _xfer_result == XFER_RESULT_INVALID) { - // The background tasks include TinyUSB which will call the function - // we provided above. In other words, the callback isn't in an interrupt. - RUN_BACKGROUND_TASKS; - } - if (mp_hal_is_interrupted()) { - tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); - return 0; - } - xfer_result_t result = _xfer_result; - _xfer_result = XFER_RESULT_INVALID; - if (result == XFER_RESULT_STALLED) { - mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); - } - if (result == XFER_RESULT_INVALID) { - tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); - mp_raise_usb_core_USBTimeoutError(); - } - if (result == XFER_RESULT_SUCCESS) { - return _actual_len; - } - - return 0; + return _handle_timed_transfer_callback(xfer, timeout); } static bool _open_endpoint(usb_core_device_obj_t *self, mp_int_t endpoint) { @@ -355,54 +383,12 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, .complete_cb = _transfer_done_cb, }; - // Prepare for transfer. Unless there is a timeout, these static globals will - // get modified by the _transfer_done_cb() callback when tinyusb finishes the - // transfer or encounters an error condition. - _xfer_result = XFER_RESULT_INVALID; - _actual_len = 0; - + _prepare_for_transfer(); if (!tuh_control_xfer(&xfer)) { mp_raise_usb_core_USBError(NULL); return 0; } - uint32_t start_time = supervisor_ticks_ms32(); - while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && - !mp_hal_is_interrupted() && - _xfer_result == XFER_RESULT_INVALID) { - // The background tasks include TinyUSB which will call the function - // we provided above. In other words, the callback isn't in an interrupt. - RUN_BACKGROUND_TASKS; - } - if (mp_hal_is_interrupted()) { - // Handle case of VM being interrupted by Ctrl-C or autoreload - tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); - return 0; - } - // Handle control transfer result code from TinyUSB - xfer_result_t result = _xfer_result; - _xfer_result = XFER_RESULT_INVALID; - switch (result) { - case XFER_RESULT_SUCCESS: - return _actual_len; - case XFER_RESULT_FAILED: - mp_raise_usb_core_USBError(NULL); - break; - case XFER_RESULT_STALLED: - mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); - break; - case XFER_RESULT_TIMEOUT: - // This timeout comes from TinyUSB, so assume that it has stopped the - // transfer (note: timeout logic may be unimplemented on TinyUSB side) - mp_raise_usb_core_USBTimeoutError(); - break; - case XFER_RESULT_INVALID: - // This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB - // to stop the transfer - tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); - mp_raise_usb_core_USBTimeoutError(); - break; - } - return 0; + return (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout); } bool common_hal_usb_core_device_is_kernel_driver_active(usb_core_device_obj_t *self, mp_int_t interface) { From e8a6d38ec4396a4ed0987829e01db9deabbfc84f Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Sat, 6 Sep 2025 00:52:51 +0000 Subject: [PATCH 093/517] fix idVendor and idProduct error check Previously these weren't checking to see if TinyUSB reported a failure. Now they check. --- shared-module/usb/core/Device.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index f71ac8844bb73..df108776fd446 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Sam Blenny // // SPDX-License-Identifier: MIT @@ -70,14 +71,18 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) { uint16_t common_hal_usb_core_device_get_idVendor(usb_core_device_obj_t *self) { uint16_t vid; uint16_t pid; - tuh_vid_pid_get(self->device_address, &vid, &pid); + if (!tuh_vid_pid_get(self->device_address, &vid, &pid)) { + mp_raise_usb_core_USBError(NULL); + } return vid; } uint16_t common_hal_usb_core_device_get_idProduct(usb_core_device_obj_t *self) { uint16_t vid; uint16_t pid; - tuh_vid_pid_get(self->device_address, &vid, &pid); + if (!tuh_vid_pid_get(self->device_address, &vid, &pid)) { + mp_raise_usb_core_USBError(NULL); + } return pid; } From 99f244e033d08c7ae862a52e8eccc831661608ed Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Sat, 6 Sep 2025 07:46:34 +0000 Subject: [PATCH 094/517] fix comment --- shared-module/usb/core/Device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index df108776fd446..604e945f1db96 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -132,7 +132,7 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); return 0; } - // Handle control transfer result code from TinyUSB + // Handle transfer result code from TinyUSB xfer_result_t result = _xfer_result; _xfer_result = XFER_RESULT_INVALID; switch (result) { From ad745c2c33a9e91fcbda22b9b58a08a56eb25fc4 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Sun, 7 Sep 2025 01:00:31 +0000 Subject: [PATCH 095/517] error checks for usb device string properties Improve the error handling for usb.core.Device string properties: .serial_number, .product, .manufacturer Previously, the property getters didn't check the device descriptor to see if the device actually had the requested string. Instead, they relied on TinyUSB to return a failure result if the string was not available. That made it impossible to distinguish missing strings from other more serious USB errors (e.g. unplugged device). These changes make it possible to return None for a missing string or raise a USBError exception in case of a more serious problem. --- shared-module/usb/core/Device.c | 87 ++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index 604e945f1db96..c07ffda1149c9 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -101,9 +101,28 @@ static bool _wait_for_callback(void) { // we provided above. In other words, the callback isn't in an interrupt. RUN_BACKGROUND_TASKS; } + if (mp_hal_is_interrupted()) { + // Handle case of VM being interrupted by Ctrl-C or autoreload + return false; + } + // Handle callback result code from TinyUSB xfer_result_t result = _xfer_result; _xfer_result = XFER_RESULT_INVALID; - return result == XFER_RESULT_SUCCESS; + switch (result) { + case XFER_RESULT_SUCCESS: + return true; + case XFER_RESULT_FAILED: + mp_raise_usb_core_USBError(NULL); + break; + case XFER_RESULT_STALLED: + mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); + break; + case XFER_RESULT_TIMEOUT: + case XFER_RESULT_INVALID: + mp_raise_usb_core_USBTimeoutError(); + break; + } + return false; } static void _prepare_for_transfer(void) { @@ -173,41 +192,75 @@ static void _get_langid(usb_core_device_obj_t *self) { } // Two control bytes and one uint16_t language code. uint16_t temp_buf[2]; - if (!tuh_descriptor_get_string(self->device_address, 0, 0, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { - return; + _prepare_for_transfer(); + if (!tuh_descriptor_get_string(self->device_address, 0, 0, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + self->first_langid = temp_buf[1]; } - self->first_langid = temp_buf[1]; } mp_obj_t common_hal_usb_core_device_get_serial_number(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_serial_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { + return mp_const_none; + } + if (descriptor.iSerialNumber == 0) { return mp_const_none; } - return _get_string(temp_buf); + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_serial_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } mp_obj_t common_hal_usb_core_device_get_product(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_product_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { return mp_const_none; } - return _get_string(temp_buf); + if (descriptor.iProduct == 0) { + return mp_const_none; + } + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_product_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } mp_obj_t common_hal_usb_core_device_get_manufacturer(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_manufacturer_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { return mp_const_none; } - return _get_string(temp_buf); + if (descriptor.iManufacturer == 0) { + return mp_const_none; + } + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_manufacturer_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } From 5d1bbca8ec1ea2a72b250a081b4233293f1a17fe Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 9 Sep 2025 13:42:09 -0400 Subject: [PATCH 096/517] Better error when mistakenly importing MicroPython .mpy file --- locale/circuitpython.pot | 4 ++++ py/persistentcode.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 262b5de769c89..7cb03d45088cc 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1394,6 +1394,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" diff --git a/py/persistentcode.c b/py/persistentcode.c index 1976729f4d879..93f4c33deb4e2 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -471,6 +471,9 @@ void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { read_bytes(reader, header, sizeof(header)); byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); // CIRCUITPY-CHANGE: 'C', not 'M' + if (header[0] == 'M') { + mp_raise_ValueError(MP_ERROR_TEXT("MicroPython .mpy file; use CircuitPython mpy-cross")); + } if (header[0] != 'C' || header[1] != MPY_VERSION || (arch != MP_NATIVE_ARCH_NONE && MPY_FEATURE_DECODE_SUB_VERSION(header[2]) != MPY_SUB_VERSION) From 8d6bd4b746017eedf31ac1d40ef32928d78b2949 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 9 Sep 2025 21:20:33 +0200 Subject: [PATCH 097/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 4 ++++ locale/el.po | 4 ++++ locale/hi.po | 4 ++++ locale/ko.po | 4 ++++ locale/ru.po | 4 ++++ locale/tr.po | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/locale/cs.po b/locale/cs.po index 58785e0a5d75a..67ece22664d76 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -1414,6 +1414,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" diff --git a/locale/el.po b/locale/el.po index 082ae20351e3b..9e31051bcc03b 100644 --- a/locale/el.po +++ b/locale/el.po @@ -1420,6 +1420,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" diff --git a/locale/hi.po b/locale/hi.po index 8e4aa0990aee7..7ab47ff0953b5 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -1396,6 +1396,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" diff --git a/locale/ko.po b/locale/ko.po index 8bde494eeec78..30205cad930e0 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -1447,6 +1447,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "매핑은 투플이어야 합니다" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" diff --git a/locale/ru.po b/locale/ru.po index d3d9415230455..49fb1eccdedef 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -1435,6 +1435,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "Сопоставление должно быть кортежом" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" diff --git a/locale/tr.po b/locale/tr.po index 10a288553a698..1bb304eaba6ca 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -1415,6 +1415,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "Map tuple olmalıdır" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" From 77b613769d9432562caea23e375fefc3bc9cc45d Mon Sep 17 00:00:00 2001 From: gychang Date: Tue, 9 Sep 2025 15:44:01 -0700 Subject: [PATCH 098/517] Initial support for ESP32-C3-Lyra-V2 dev board See https://docs.espressif.com/projects/esp-adf/en/latest/design-guide/dev-boards/user-guide-esp32-c3-lyra.html This commit adds support for the status LED. It's not 100% clear to me why the default neopixel_write implementation doesn't work, but it seems that deleting the RMT channel triggers some sort of glitch signal that clears the WS2812C LED. Tested on this board: - console/REPL over USB/UART (connection works but is not stable) - web interface and editor over wifi - audiomp3 playback (sounds terrible) - neopixel library --- .../boards/espressif_esp32c3_lyra_v2/board.c | 9 ++++ .../espressif_esp32c3_lyra_v2/mpconfigboard.h | 23 +++++++++++ .../mpconfigboard.mk | 14 +++++++ .../boards/espressif_esp32c3_lyra_v2/pins.c | 41 +++++++++++++++++++ .../espressif_esp32c3_lyra_v2/sdkconfig | 0 .../common-hal/neopixel_write/__init__.c | 10 +++++ 6 files changed, 97 insertions(+) create mode 100644 ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c create mode 100644 ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h create mode 100644 ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk create mode 100644 ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c create mode 100644 ports/espressif/boards/espressif_esp32c3_lyra_v2/sdkconfig diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c b/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c new file mode 100644 index 0000000000000..b3b20cfe2d3f0 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h new file mode 100644 index 0000000000000..d38d4f4d09fdf --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h @@ -0,0 +1,23 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Board setup +#define MICROPY_HW_BOARD_NAME "ESP32-C3-Lyra-V2" +#define MICROPY_HW_MCU_NAME "ESP32-C3N4" + +// Status LED +#define MICROPY_HW_NEOPIXEL (&pin_GPIO10) +#define MICROPY_HW_NEOPIXEL_COUNT (1) + +// Default bus pins +#define DEFAULT_UART_BUS_RX (&pin_GPIO20) +#define DEFAULT_UART_BUS_TX (&pin_GPIO21) + +// Serial over UART +#define CIRCUITPY_CONSOLE_UART_RX DEFAULT_UART_BUS_RX +#define CIRCUITPY_CONSOLE_UART_TX DEFAULT_UART_BUS_TX diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk new file mode 100644 index 0000000000000..a233eb526fdbd --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk @@ -0,0 +1,14 @@ +CIRCUITPY_CREATOR_ID = 0x000C303A +CIRCUITPY_CREATION_ID = 0x00C3A000 + +IDF_TARGET = esp32c3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 4MB + +CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 + +ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD = 1 + +CFLAGS += -DESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD=$(ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD) diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c b/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c new file mode 100644 index 0000000000000..5f7c3d084dd3f --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c @@ -0,0 +1,41 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_MTMS), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_MTDI), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_MTCK), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_MTDO), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/sdkconfig b/ports/espressif/boards/espressif_esp32c3_lyra_v2/sdkconfig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/common-hal/neopixel_write/__init__.c b/ports/espressif/common-hal/neopixel_write/__init__.c index d0d46a3179787..945b309372ac7 100644 --- a/ports/espressif/common-hal/neopixel_write/__init__.c +++ b/ports/espressif/common-hal/neopixel_write/__init__.c @@ -126,6 +126,11 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, // Update the next start to +2 ticks. It ensures that we've gone 300+ us. next_start_raw_ticks = port_get_raw_ticks(NULL) + 2; + #if defined(ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD) + // Hold the pin, deleting the channel seems to glitch pixels into turning off. + gpio_hold_en(digitalinout->pin->number); + #endif + // Free channel again rmt_del_encoder(encoder); rmt_disable(channel); @@ -133,4 +138,9 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, CHECK_ESP_RESULT(result); // Swap pin back to GPIO mode gpio_set_direction(digitalinout->pin->number, GPIO_MODE_OUTPUT); + + #if defined(ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD) + // Release hold + gpio_hold_dis(digitalinout->pin->number); + #endif } From 06f7192cba388a3da683387a959c8bc1a73f2e82 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 9 Sep 2025 21:24:03 -0400 Subject: [PATCH 099/517] Rev 3 board changes; lots of flash so turn on most features --- .../mpconfigboard.h | 4 ++-- .../mpconfigboard.mk | 19 ------------------- .../pins.c | 16 ++++++++-------- 3 files changed, 10 insertions(+), 29 deletions(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h index 31559f318ada7..6e67197e757f1 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h @@ -5,8 +5,8 @@ #define MICROPY_HW_MCU_NAME "same51j20" #define CIRCUITPY_MCU_FAMILY samd51 -#define MICROPY_HW_LED_STATUS (&pin_PB23) -#define MICROPY_HW_NEOPIXEL (&pin_PB22) +#define MICROPY_HW_LED_STATUS (&pin_PB23) +#define MICROPY_HW_NEOPIXEL (&pin_PB22) #define BOARD_HAS_CRYSTAL 1 diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk index 408c4eca9fd60..34c9d7bcaab28 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk @@ -9,24 +9,5 @@ CHIP_FAMILY = same51 QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "SST26VF016B,SST26VF032B,SST26VF064B" LONGINT_IMPL = MPZ -CIRCUITPY_ULAB = 0 CIRCUITPY__EVE = 1 -CIRCUITPY_BITMAPFILTER = 0 -CIRCUITPY_CANIO = 1 -CIRCUITPY_FLOPPYIO = 0 -CIRCUITPY_SYNTHIO = 0 -CIRCUITPY_GIFIO = 0 -CIRCUITPY_JPEGIO = 0 - -CIRCUITPY_LTO_PARTITION = one - -# We don't have room for the fonts for terminalio for certain languages, -# so turn off terminalio, and if it's off and displayio is on, -# force a clean build. -# Note that we cannot test $(CIRCUITPY_DISPLAYIO) directly with an -# ifeq, because it's not set yet. -ifneq (,$(filter $(TRANSLATION),ja ko ru)) -CIRCUITPY_TERMINALIO = 0 -RELEASE_NEEDS_CLEAN_BUILD = $(CIRCUITPY_DISPLAYIO) -endif diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c index b4184e3cecd30..f3069930c6b0b 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c @@ -22,13 +22,11 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_PB14) }, { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_PB15) }, { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_PB16) }, - { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, - { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB22) }, - { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_PB23) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, - { MP_ROM_QSTR(MP_QSTR_NEOPIX), MP_ROM_PTR(&pin_PB22) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_PB22) }, { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_PTR(&pin_PA03) }, @@ -60,23 +58,25 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_PA19) }, { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_PB00) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_0), MP_ROM_PTR(&pin_PB00) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB00) }, { MP_ROM_QSTR(MP_QSTR_CS), MP_ROM_PTR(&pin_PB01) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_1), MP_ROM_PTR(&pin_PB01) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_PB01) }, { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB02) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_2), MP_ROM_PTR(&pin_PB02) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_PB02) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_PB03) }, - { MP_ROM_QSTR(MP_QSTR_SPARE_3), MP_ROM_PTR(&pin_PB03) }, + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_PB03) }, { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, { MP_ROM_QSTR(MP_QSTR_DEBUG_RX), MP_ROM_PTR(&pin_PA23) }, { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, From 146c199f34624add97a6287875a7287022accd3c Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 10 Sep 2025 09:57:30 -0400 Subject: [PATCH 100/517] ports/raspberrypi/rp2pio/StateMachine.c: fix wait pin validation --- ports/raspberrypi/common-hal/rp2pio/StateMachine.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index 74b9a7f0c31b0..8d7f7f2ed6f7e 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -523,9 +523,10 @@ static void consider_instruction(introspect_t *state, uint16_t full_instruction, } } if (instruction == pio_instr_bits_wait) { - uint16_t wait_source = (full_instruction & 0x0060) >> 5; - uint16_t wait_index = (full_instruction & 0x001f) + state->inputs.pio_gpio_offset; - if (wait_source == 0 && !PIO_PINMASK_IS_SET(state->inputs.pins_we_use, wait_index)) { // GPIO + const uint16_t wait_source = (full_instruction & 0x0060) >> 5; + const uint16_t wait_index = full_instruction & 0x001f; + const uint16_t wait_pin = wait_index + state->inputs.pio_gpio_offset; + if (wait_source == 0 && !PIO_PINMASK_IS_SET(state->inputs.pins_we_use, wait_pin)) { // GPIO mp_raise_ValueError_varg(MP_ERROR_TEXT("%q[%u] uses extra pin"), what_program, i); } else if (wait_source == 1) { // Input pin if (!state->inputs.has_in_pin) { From 0013b48091424e0beaac2d704fe7a1be4a951f82 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 11 Sep 2025 16:22:09 -0400 Subject: [PATCH 101/517] py/builtinimport.c: always name module in error msg when an import fails --- py/builtinimport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/py/builtinimport.c b/py/builtinimport.c index 17b3f11c4a001..e4e06d8e2cc8b 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -451,7 +451,9 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, if (stat == MP_IMPORT_STAT_NO_EXIST) { // Not found -- fail. - #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // CIRCUITPY-CHANGE: always the use more verbose error message that names missing module. + // Otherwise `import a` where `a` imports `b`, but `b` is missing will give a confusing error. + #if 0 && (MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE) mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found")); #else mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), full_mod_name); From 64b9c05779dd3db85fda5227ea31130e733bf4df Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 13 Sep 2025 15:44:51 -0400 Subject: [PATCH 102/517] Use critical sections for port_* storage operations, not just in LWIP --- .../common-hal/socketpool/Socket.c | 25 ++++++++++++++----- ports/raspberrypi/lwip_src/lwip_mem.c | 4 --- ports/raspberrypi/supervisor/port.c | 13 +++++++++- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/ports/raspberrypi/common-hal/socketpool/Socket.c b/ports/raspberrypi/common-hal/socketpool/Socket.c index 018365def8c75..aeab1498510a0 100644 --- a/ports/raspberrypi/common-hal/socketpool/Socket.c +++ b/ports/raspberrypi/common-hal/socketpool/Socket.c @@ -89,7 +89,7 @@ static mp_obj_t socketpool_ip_addr_and_port_to_tuple(const ip_addr_t *addr, int // socket API. // Extension to lwIP error codes -// Matches lwIP 2.0.3 +// Matches lwIP 2.2.1 #undef _ERR_BADF #define _ERR_BADF -17 static const int error_lookup_table[] = { @@ -473,7 +473,12 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, MICROPY_PY_LWIP_ENTER - u16_t available = tcp_sndbuf(socket->pcb.tcp); + // If the socket is still connecting then don't let data be written to it. + // Otherwise, get the number of available bytes in the output buffer. + u16_t available = 0; + if (socket->state != STATE_CONNECTING) { + available = tcp_sndbuf(socket->pcb.tcp); + } if (available == 0) { // Non-blocking socket @@ -490,7 +495,8 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, // If peer fully closed socket, we would have socket->state set to ERR_RST (connection // reset) by error callback. // Avoid sending too small packets, so wait until at least 16 bytes available - while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) { + while (socket->state == STATE_CONNECTING + || (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16)) { MICROPY_PY_LWIP_EXIT if (socket->timeout != (unsigned)-1 && mp_hal_ticks_ms() - start > socket->timeout) { *_errno = MP_ETIMEDOUT; @@ -531,9 +537,10 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, MICROPY_PY_LWIP_REENTER } - // If the output buffer is getting full then send the data to the lower layers - if (err == ERR_OK && tcp_sndbuf(socket->pcb.tcp) < TCP_SND_BUF / 4) { - err = tcp_output(socket->pcb.tcp); + // Use nagle algorithm to determine when to send segment buffer (can be + // disabled with TCP_NODELAY socket option) + if (err == ERR_OK) { + err = tcp_output_nagle(socket->pcb.tcp); } MICROPY_PY_LWIP_EXIT @@ -551,6 +558,12 @@ static mp_uint_t lwip_tcp_receive(socketpool_socket_obj_t *socket, byte *buf, mp // 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; + return -1; + } + if (socket->incoming.pbuf == NULL) { // Non-blocking socket diff --git a/ports/raspberrypi/lwip_src/lwip_mem.c b/ports/raspberrypi/lwip_src/lwip_mem.c index d7d5468a42011..23b185354b072 100644 --- a/ports/raspberrypi/lwip_src/lwip_mem.c +++ b/ports/raspberrypi/lwip_src/lwip_mem.c @@ -12,16 +12,12 @@ #include "supervisor/port_heap.h" void *lwip_heap_malloc(size_t size) { - common_hal_mcu_disable_interrupts(); void *ptr = port_malloc(size, true); - common_hal_mcu_enable_interrupts(); return ptr; } void lwip_heap_free(void *ptr) { - common_hal_mcu_disable_interrupts(); port_free(ptr); - common_hal_mcu_enable_interrupts(); } void *lwip_heap_calloc(size_t num, size_t size) { diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 4be42380491cb..25b0bb6650f0c 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -264,31 +264,42 @@ void port_heap_init(void) { void *port_malloc(size_t size, bool dma_capable) { if (!dma_capable && _psram_size > 0) { + common_hal_mcu_disable_interrupts(); void *block = tlsf_malloc(_psram_heap, size); + common_hal_mcu_enable_interrupts(); if (block) { return block; } } + common_hal_mcu_disable_interrupts(); void *block = tlsf_malloc(_heap, size); + common_hal_mcu_enable_interrupts(); return block; } void port_free(void *ptr) { + common_hal_mcu_disable_interrupts(); if (((size_t)ptr) < SRAM_BASE) { tlsf_free(_psram_heap, ptr); } else { tlsf_free(_heap, ptr); } + common_hal_mcu_enable_interrupts(); } void *port_realloc(void *ptr, size_t size, bool dma_capable) { if (_psram_size > 0 && ((ptr != NULL && ((size_t)ptr) < SRAM_BASE) || (ptr == NULL && !dma_capable))) { + common_hal_mcu_disable_interrupts(); void *block = tlsf_realloc(_psram_heap, ptr, size); + common_hal_mcu_enable_interrupts(); if (block) { return block; } } - return tlsf_realloc(_heap, ptr, size); + common_hal_mcu_disable_interrupts(); + void *new_ptr = tlsf_realloc(_heap, ptr, size); + common_hal_mcu_enable_interrupts(); + return new_ptr; } static bool max_size_walker(void *ptr, size_t size, int used, void *user) { From 4008e1499735bb789d8cc95af2628d7327eb29aa Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 13 Sep 2025 15:46:00 -0400 Subject: [PATCH 103/517] espressif/common-hal/socketpool/Socket.c: return correct errno connect failure --- ports/espressif/common-hal/socketpool/Socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/common-hal/socketpool/Socket.c b/ports/espressif/common-hal/socketpool/Socket.c index 0a36eff95b454..5128e93c8589e 100644 --- a/ports/espressif/common-hal/socketpool/Socket.c +++ b/ports/espressif/common-hal/socketpool/Socket.c @@ -483,7 +483,7 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, socklen_t socklen = sizeof(error_code); result = getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); if (result < 0 || error_code != 0) { - mp_raise_OSError(errno); + mp_raise_OSError(error_code); } self->connected = true; return; From a2c4dfa1ea5442b6416bc3be0c4a17c2ce89ec52 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 15 Sep 2025 12:20:20 -0700 Subject: [PATCH 104/517] Ensure USB host buffers are allocated in internal RAM --- locale/circuitpython.pot | 4 + ports/raspberrypi/mpconfigport.mk | 3 + ports/raspberrypi/supervisor/port.c | 8 ++ py/circuitpy_mpconfig.mk | 5 ++ shared-module/usb/core/Device.c | 116 ++++++++++++++++++++++++++-- shared-module/usb/core/Device.h | 4 + supervisor/port_heap.h | 6 ++ 7 files changed, 141 insertions(+), 5 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 7cb03d45088cc..98fc54741eb39 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -844,6 +844,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 1404b2b06777c..bbce13d13811f 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -59,6 +59,9 @@ CIRCUITPY_CYW43_INIT_DELAY ?= 1000 endif ifeq ($(CHIP_VARIANT),RP2350) +# RP2350 has PSRAM that is not DMA-capable +CIRCUITPY_ALL_MEMORY_DMA_CAPABLE = 0 + # This needs to be implemented. CIRCUITPY_ALARM = 0 # Default PICODVI on because it doesn't require much code in RAM to talk to HSTX. diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 25b0bb6650f0c..66e63248c4810 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -302,6 +302,14 @@ void *port_realloc(void *ptr, size_t size, bool dma_capable) { return new_ptr; } +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +bool port_buffer_is_dma_capable(const void *ptr) { + // For RP2350, DMA can only access SRAM, not PSRAM + // PSRAM addresses are below SRAM_BASE + return ptr != NULL && ((size_t)ptr) >= SRAM_BASE; +} +#endif + static bool max_size_walker(void *ptr, size_t size, int used, void *user) { size_t *max_size = (size_t *)user; if (!used && *max_size < size) { diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 2009c4e177da6..8c838a41218a3 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -98,6 +98,11 @@ CFLAGS += -DCIRCUITPY_ALARM=$(CIRCUITPY_ALARM) CIRCUITPY_ALARM_TOUCH ?= $(CIRCUITPY_ALARM) CFLAGS += -DCIRCUITPY_ALARM_TOUCH=$(CIRCUITPY_ALARM_TOUCH) +# Enable DMA buffer management for platforms where not all memory is DMA-capable +# Platforms with PSRAM or other non-DMA memory should set this to 0 +CIRCUITPY_ALL_MEMORY_DMA_CAPABLE ?= 1 +CFLAGS += -DCIRCUITPY_ALL_MEMORY_DMA_CAPABLE=$(CIRCUITPY_ALL_MEMORY_DMA_CAPABLE) + CIRCUITPY_ANALOGBUFIO ?= 0 CFLAGS += -DCIRCUITPY_ANALOGBUFIO=$(CIRCUITPY_ANALOGBUFIO) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index c07ffda1149c9..c4342e10d1d65 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -8,6 +8,8 @@ #include "shared-bindings/usb/core/Device.h" #include "tusb_config.h" +#include "supervisor/port.h" +#include "supervisor/port_heap.h" #include "lib/tinyusb/src/host/hcd.h" #include "lib/tinyusb/src/host/usbh.h" @@ -33,6 +35,39 @@ void tuh_umount_cb(uint8_t dev_addr) { static xfer_result_t _xfer_result; static size_t _actual_len; + +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +// Helper to ensure buffer is DMA-capable for transfer operations +static uint8_t *_ensure_dma_buffer(usb_core_device_obj_t *self, const uint8_t *buffer, size_t len, bool for_write) { + if (port_buffer_is_dma_capable(buffer)) { + return (uint8_t *)buffer; // Already DMA-capable, use directly + } + + // Need to allocate/reallocate temporary buffer in DMA-capable memory + if (self->temp_buffer_size < len) { + self->temp_buffer = port_realloc(self->temp_buffer, len, true); // true = DMA capable + if (self->temp_buffer == NULL) { + self->temp_buffer_size = 0; + return NULL; // Allocation failed + } + self->temp_buffer_size = len; + } + + // Copy data to DMA buffer if writing + if (for_write && buffer != NULL) { + memcpy(self->temp_buffer, buffer, len); + } + + return self->temp_buffer; +} + +// Copy data back from DMA buffer to original buffer after read +static void _copy_from_dma_buffer(usb_core_device_obj_t *self, uint8_t *original_buffer, size_t len) { + if (!port_buffer_is_dma_capable(original_buffer) && self->temp_buffer != NULL) { + memcpy(original_buffer, self->temp_buffer, len); + } +} +#endif bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_address) { if (!tuh_inited()) { mp_raise_RuntimeError(MP_ERROR_TEXT("No usb host port initialized")); @@ -46,6 +81,10 @@ bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t d } self->device_address = device_address; self->first_langid = 0; + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + self->temp_buffer = NULL; + self->temp_buffer_size = 0; + #endif _xfer_result = XFER_RESULT_INVALID; return true; } @@ -65,6 +104,14 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) { self->open_endpoints[i] = 0; } } + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Clean up temporary buffer + if (self->temp_buffer != NULL) { + port_free(self->temp_buffer); + self->temp_buffer = NULL; + self->temp_buffer_size = 0; + } + #endif self->device_address = 0; } @@ -399,10 +446,22 @@ mp_int_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t mp_raise_usb_core_USBError(NULL); return 0; } + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = _ensure_dma_buffer(self, buffer, len, true); // true = for write + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + #else + uint8_t *dma_buffer = (uint8_t *)buffer; // All memory is DMA-capable + #endif + tuh_xfer_t xfer; xfer.daddr = self->device_address; xfer.ep_addr = endpoint; - xfer.buffer = (uint8_t *)buffer; + xfer.buffer = dma_buffer; xfer.buflen = len; return _xfer(&xfer, timeout); } @@ -412,12 +471,33 @@ mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t e mp_raise_usb_core_USBError(NULL); return 0; } + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = _ensure_dma_buffer(self, buffer, len, false); // false = for read + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + #else + uint8_t *dma_buffer = buffer; // All memory is DMA-capable + #endif + tuh_xfer_t xfer; xfer.daddr = self->device_address; xfer.ep_addr = endpoint; - xfer.buffer = buffer; + xfer.buffer = dma_buffer; xfer.buflen = len; - return _xfer(&xfer, timeout); + mp_int_t result = _xfer(&xfer, timeout); + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Copy data back to original buffer if needed + if (result > 0) { + _copy_from_dma_buffer(self, buffer, result); + } + #endif + + return result; } mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, @@ -426,6 +506,23 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, uint8_t *buffer, mp_int_t len, mp_int_t timeout) { // Timeout is in ms. + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Determine if this is a write (host-to-device) or read (device-to-host) transfer + bool is_write = (bmRequestType & 0x80) == 0; // Bit 7: 0=host-to-device, 1=device-to-host + + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = NULL; + if (len > 0 && buffer != NULL) { + dma_buffer = _ensure_dma_buffer(self, buffer, len, is_write); + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + } + #else + uint8_t *dma_buffer = buffer; // All memory is DMA-capable + #endif + tusb_control_request_t request = { .bmRequestType = bmRequestType, .bRequest = bRequest, @@ -437,7 +534,7 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, .daddr = self->device_address, .ep_addr = 0, .setup = &request, - .buffer = buffer, + .buffer = dma_buffer, .complete_cb = _transfer_done_cb, }; @@ -446,7 +543,16 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, mp_raise_usb_core_USBError(NULL); return 0; } - return (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout); + mp_int_t result = (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout); + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Copy data back to original buffer if this was a read transfer and we got data + if ((bmRequestType & 0x80) != 0 && result > 0 && buffer != NULL) { // Read transfer (device-to-host) + _copy_from_dma_buffer(self, buffer, result); + } + #endif + + return result; } bool common_hal_usb_core_device_is_kernel_driver_active(usb_core_device_obj_t *self, mp_int_t interface) { diff --git a/shared-module/usb/core/Device.h b/shared-module/usb/core/Device.h index c7392a04ba7bc..8737eeaf86bf7 100644 --- a/shared-module/usb/core/Device.h +++ b/shared-module/usb/core/Device.h @@ -15,4 +15,8 @@ typedef struct { uint8_t *configuration_descriptor; // Contains the length of the all descriptors. uint8_t open_endpoints[8]; uint16_t first_langid; + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + uint8_t *temp_buffer; // Temporary buffer for PSRAM data + size_t temp_buffer_size; // Size of temporary buffer + #endif } usb_core_device_obj_t; diff --git a/supervisor/port_heap.h b/supervisor/port_heap.h index 40abd21438741..3e6b5a660fa79 100644 --- a/supervisor/port_heap.h +++ b/supervisor/port_heap.h @@ -27,4 +27,10 @@ void port_free(void *ptr); void *port_realloc(void *ptr, size_t size, bool dma_capable); +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +// Check if a buffer pointer is in DMA-capable memory. DMA-capable memory is also accessible during +// flash operations. +bool port_buffer_is_dma_capable(const void *ptr); +#endif + size_t port_heap_get_largest_free_size(void); From 989a857a335643fa280f6e72a39da3ad6b31e2fb Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 15 Sep 2025 23:10:38 -0400 Subject: [PATCH 105/517] rotaryio.IncrementalEncoder deinit fix and improvements --- .../common-hal/rotaryio/IncrementalEncoder.c | 13 ++++++++++--- .../common-hal/rotaryio/IncrementalEncoder.c | 8 ++++++++ .../common-hal/rotaryio/IncrementalEncoder.c | 10 ++++++++-- .../nordic/common-hal/rotaryio/IncrementalEncoder.c | 9 ++++++++- ports/raspberrypi/bindings/rp2pio/StateMachine.h | 1 + .../common-hal/rotaryio/IncrementalEncoder.c | 9 +++++++++ ports/raspberrypi/common-hal/rp2pio/StateMachine.c | 9 ++++++++- shared-bindings/rotaryio/IncrementalEncoder.h | 4 ++++ 8 files changed, 56 insertions(+), 7 deletions(-) diff --git a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c index c6cd4605784ab..a8c87d82ed268 100644 --- a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c @@ -17,6 +17,10 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { + + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + if (!pin_a->has_extint) { raise_ValueError_invalid_pin_name(MP_QSTR_pin_a); } @@ -83,10 +87,13 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o turn_off_eic_channel(self->eic_channel_b); reset_pin_number(self->pin_a); - self->pin_a = NO_PIN; - reset_pin_number(self->pin_b); - self->pin_b = NO_PIN; + + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { + self->pin_a = NO_PIN; } void incrementalencoder_interrupt_handler(uint8_t channel) { diff --git a/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c b/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c index ba8b70221b729..f65c7fdd9f110 100644 --- a/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c @@ -21,6 +21,10 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode // // These routines also implicitly configure the weak internal pull-ups, as expected // in CircuitPython. + + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + pcnt_unit_config_t unit_config = { // Set counter limit .low_limit = INT16_MIN, @@ -87,6 +91,10 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o pcnt_del_channel(self->channel_a); pcnt_del_channel(self->channel_b); pcnt_del_unit(self->unit); + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->unit = NULL; } diff --git a/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c b/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c index c71f8b212e183..211b5c186f3fa 100644 --- a/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c @@ -26,6 +26,9 @@ static void encoder_change(void *self_in) { void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + self->pin_a = pin_a; self->pin_b = pin_b; @@ -49,7 +52,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode } bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) { - return !self->pin_a; + return self->pin_a == NULL; } void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self) { @@ -62,6 +65,9 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o common_hal_reset_pin(self->pin_a); common_hal_reset_pin(self->pin_b); + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->pin_a = NULL; - self->pin_b = NULL; } diff --git a/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c b/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c index b0617fae7e566..2e8b308ff64d9 100644 --- a/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c @@ -32,6 +32,9 @@ static void _intr_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + self->pin_a = pin_a->number; self->pin_b = pin_b->number; @@ -78,6 +81,10 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o nrfx_gpiote_in_uninit(self->pin_b); reset_pin_number(self->pin_a); reset_pin_number(self->pin_b); + + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->pin_a = NO_PIN; - self->pin_b = NO_PIN; } diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.h b/ports/raspberrypi/bindings/rp2pio/StateMachine.h index afdffd1eccd3b..16f884bcfca32 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.h +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.h @@ -42,6 +42,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self); bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self); +void common_hal_rp2pio_statemachine_mark_deinit(rp2pio_statemachine_obj_t *self); void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self); diff --git a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c index 113bd1900dd88..d71ff60b2ebc3 100644 --- a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c @@ -43,6 +43,9 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { const mcu_pin_obj_t *pins[] = { pin_a, pin_b }; + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + // Start out with swapped to match behavior with other ports. self->swapped = true; if (!common_hal_rp2pio_pins_are_sequential(2, pins)) { @@ -89,6 +92,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode } bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) { + // Use the deinit state of the PIO state machine. return common_hal_rp2pio_statemachine_deinited(&self->state_machine); } @@ -100,6 +104,11 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o common_hal_rp2pio_statemachine_deinit(&self->state_machine); } +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { + // Use the deinit state of the PIO state machine. + common_hal_rp2pio_statemachine_mark_deinit(&self->state_machine); +} + static void incrementalencoder_interrupt_handler(void *self_in) { rotaryio_incrementalencoder_obj_t *self = self_in; diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index 8d7f7f2ed6f7e..71050677f225f 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -628,6 +628,9 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, int mov_status_type, int mov_status_n) { + // Ensure object starts in its deinit state. + common_hal_rp2pio_statemachine_mark_deinit(self); + // First, check that all pins are free OR already in use by any PIO if exclusive_pin_use is false. pio_pinmask_t pins_we_use = wait_gpio_mask; PIO_PINMASK_MERGE(pins_we_use, _check_pins_free(first_out_pin, out_pin_count, exclusive_pin_use)); @@ -744,7 +747,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, mov_status_type, mov_status_n); if (!ok) { // indicate state machine never inited - self->state_machine = NUM_PIO_STATE_MACHINES; + common_hal_rp2pio_statemachine_mark_deinit(self); mp_raise_RuntimeError(MP_ERROR_TEXT("All state machines in use")); } } @@ -827,6 +830,10 @@ void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self) { rp2pio_statemachine_deinit(self, false); } +void common_hal_rp2pio_statemachine_mark_deinit(rp2pio_statemachine_obj_t *self) { + self->state_machine = NUM_PIO_STATE_MACHINES; +} + void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self) { rp2pio_statemachine_never_reset(self->pio, self->state_machine); // TODO: never reset all the pins diff --git a/shared-bindings/rotaryio/IncrementalEncoder.h b/shared-bindings/rotaryio/IncrementalEncoder.h index 1c60dd69a1937..b3ceb3eeb1513 100644 --- a/shared-bindings/rotaryio/IncrementalEncoder.h +++ b/shared-bindings/rotaryio/IncrementalEncoder.h @@ -13,11 +13,15 @@ extern const mp_obj_type_t rotaryio_incrementalencoder_type; extern void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b); + extern void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self); extern bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self); +extern void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self); + extern mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self); extern void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t new_position); + extern mp_int_t common_hal_rotaryio_incrementalencoder_get_divisor(rotaryio_incrementalencoder_obj_t *self); extern void common_hal_rotaryio_incrementalencoder_set_divisor(rotaryio_incrementalencoder_obj_t *self, mp_int_t new_divisor); From 908e1341c152c383316fb96aebe11700d222057a Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 16 Sep 2025 08:33:17 -0500 Subject: [PATCH 106/517] Return self object from `play` methods to allow for chaining --- shared-bindings/audiodelays/Chorus.c | 4 ++-- shared-bindings/audiodelays/Echo.c | 4 ++-- shared-bindings/audiodelays/MultiTapDelay.c | 4 ++-- shared-bindings/audiodelays/PitchShift.c | 4 ++-- shared-bindings/audiofilters/Distortion.c | 4 ++-- shared-bindings/audiofilters/Filter.c | 4 ++-- shared-bindings/audiofilters/Phaser.c | 4 ++-- shared-bindings/audiofreeverb/Freeverb.c | 4 ++-- shared-bindings/audiomixer/Mixer.c | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/shared-bindings/audiodelays/Chorus.c b/shared-bindings/audiodelays/Chorus.c index c6bfa2ffa7eb1..67b9bc32745c4 100644 --- a/shared-bindings/audiodelays/Chorus.c +++ b/shared-bindings/audiodelays/Chorus.c @@ -206,7 +206,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_chorus_get_playing_obj, audiodelays_chorus MP_PROPERTY_GETTER(audiodelays_chorus_playing_obj, (mp_obj_t)&audiodelays_chorus_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Chorus: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -228,7 +228,7 @@ static mp_obj_t audiodelays_chorus_obj_play(size_t n_args, const mp_obj_t *pos_a mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_chorus_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_chorus_play_obj, 1, audiodelays_chorus_obj_play); diff --git a/shared-bindings/audiodelays/Echo.c b/shared-bindings/audiodelays/Echo.c index 5ae849b19aa41..68216d2355b70 100644 --- a/shared-bindings/audiodelays/Echo.c +++ b/shared-bindings/audiodelays/Echo.c @@ -230,7 +230,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_echo_get_playing_obj, audiodelays_echo_obj MP_PROPERTY_GETTER(audiodelays_echo_playing_obj, (mp_obj_t)&audiodelays_echo_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Echo: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -252,7 +252,7 @@ static mp_obj_t audiodelays_echo_obj_play(size_t n_args, const mp_obj_t *pos_arg mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_echo_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_echo_play_obj, 1, audiodelays_echo_obj_play); diff --git a/shared-bindings/audiodelays/MultiTapDelay.c b/shared-bindings/audiodelays/MultiTapDelay.c index 5b057eaf80d1b..ce11b2f1d3c61 100644 --- a/shared-bindings/audiodelays/MultiTapDelay.c +++ b/shared-bindings/audiodelays/MultiTapDelay.c @@ -233,7 +233,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_get_playing_obj, audiodela MP_PROPERTY_GETTER(audiodelays_multi_tap_delay_playing_obj, (mp_obj_t)&audiodelays_multi_tap_delay_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> MultiTapDelay: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -255,7 +255,7 @@ static mp_obj_t audiodelays_multi_tap_delay_obj_play(size_t n_args, const mp_obj mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_multi_tap_delay_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_multi_tap_delay_play_obj, 1, audiodelays_multi_tap_delay_obj_play); diff --git a/shared-bindings/audiodelays/PitchShift.c b/shared-bindings/audiodelays/PitchShift.c index df2189945aa5d..e595beec6b8de 100644 --- a/shared-bindings/audiodelays/PitchShift.c +++ b/shared-bindings/audiodelays/PitchShift.c @@ -190,7 +190,7 @@ MP_PROPERTY_GETTER(audiodelays_pitch_shift_playing_obj, (mp_obj_t)&audiodelays_pitch_shift_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> PitchShift: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -211,7 +211,7 @@ static mp_obj_t audiodelays_pitch_shift_obj_play(size_t n_args, const mp_obj_t * mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_pitch_shift_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_pitch_shift_play_obj, 1, audiodelays_pitch_shift_obj_play); diff --git a/shared-bindings/audiofilters/Distortion.c b/shared-bindings/audiofilters/Distortion.c index cb888d0c71b7b..3120111ca9a80 100644 --- a/shared-bindings/audiofilters/Distortion.c +++ b/shared-bindings/audiofilters/Distortion.c @@ -309,7 +309,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_distortion_get_playing_obj, audiofilters_ MP_PROPERTY_GETTER(audiofilters_distortion_playing_obj, (mp_obj_t)&audiofilters_distortion_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Distortion: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -331,7 +331,7 @@ static mp_obj_t audiofilters_distortion_obj_play(size_t n_args, const mp_obj_t * mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_distortion_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_distortion_play_obj, 1, audiofilters_distortion_obj_play); diff --git a/shared-bindings/audiofilters/Filter.c b/shared-bindings/audiofilters/Filter.c index 2a4887a4d42a2..2d8829d8a815d 100644 --- a/shared-bindings/audiofilters/Filter.c +++ b/shared-bindings/audiofilters/Filter.c @@ -179,7 +179,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_filter_get_playing_obj, audiofilters_filt MP_PROPERTY_GETTER(audiofilters_filter_playing_obj, (mp_obj_t)&audiofilters_filter_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Filter: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -201,7 +201,7 @@ static mp_obj_t audiofilters_filter_obj_play(size_t n_args, const mp_obj_t *pos_ mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_filter_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_play_obj, 1, audiofilters_filter_obj_play); diff --git a/shared-bindings/audiofilters/Phaser.c b/shared-bindings/audiofilters/Phaser.c index e7ddd986176b3..daff50e983492 100644 --- a/shared-bindings/audiofilters/Phaser.c +++ b/shared-bindings/audiofilters/Phaser.c @@ -214,7 +214,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_get_playing_obj, audiofilters_phas MP_PROPERTY_GETTER(audiofilters_phaser_playing_obj, (mp_obj_t)&audiofilters_phaser_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Phaser: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -236,7 +236,7 @@ static mp_obj_t audiofilters_phaser_obj_play(size_t n_args, const mp_obj_t *pos_ mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_phaser_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_phaser_play_obj, 1, audiofilters_phaser_obj_play); diff --git a/shared-bindings/audiofreeverb/Freeverb.c b/shared-bindings/audiofreeverb/Freeverb.c index 62c9237a0d271..5e4c9644d2e8d 100644 --- a/shared-bindings/audiofreeverb/Freeverb.c +++ b/shared-bindings/audiofreeverb/Freeverb.c @@ -196,7 +196,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofreeverb_freeverb_get_playing_obj, audiofreeverb_ MP_PROPERTY_GETTER(audiofreeverb_freeverb_playing_obj, (mp_obj_t)&audiofreeverb_freeverb_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Freeverb: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -218,7 +218,7 @@ static mp_obj_t audiofreeverb_freeverb_obj_play(size_t n_args, const mp_obj_t *p mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofreeverb_freeverb_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiofreeverb_freeverb_play_obj, 1, audiofreeverb_freeverb_obj_play); diff --git a/shared-bindings/audiomixer/Mixer.c b/shared-bindings/audiomixer/Mixer.c index dda6d06bd500c..0944f58ddbe45 100644 --- a/shared-bindings/audiomixer/Mixer.c +++ b/shared-bindings/audiomixer/Mixer.c @@ -160,7 +160,7 @@ MP_PROPERTY_GETTER(audiomixer_mixer_voice_obj, //| def play( //| self, sample: circuitpython_typing.AudioSample, *, voice: int = 0, loop: bool = False -//| ) -> None: +//| ) -> Mixer: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| @@ -189,7 +189,7 @@ static mp_obj_t audiomixer_mixer_obj_play(size_t n_args, const mp_obj_t *pos_arg mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiomixer_mixervoice_play(voice, sample, args[ARG_loop].u_bool); - return mp_const_none; + return pos_args[0]; } MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixer_play_obj, 1, audiomixer_mixer_obj_play); From 2e6dbaf5a00be4429592a8240c2f95c0e1add8ac Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 16 Sep 2025 10:09:22 -0500 Subject: [PATCH 107/517] Improve clarity of self reference --- shared-bindings/audiodelays/Chorus.c | 2 +- shared-bindings/audiodelays/Echo.c | 2 +- shared-bindings/audiodelays/MultiTapDelay.c | 2 +- shared-bindings/audiodelays/PitchShift.c | 2 +- shared-bindings/audiofilters/Distortion.c | 2 +- shared-bindings/audiofilters/Filter.c | 2 +- shared-bindings/audiofilters/Phaser.c | 2 +- shared-bindings/audiofreeverb/Freeverb.c | 2 +- shared-bindings/audiomixer/Mixer.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/shared-bindings/audiodelays/Chorus.c b/shared-bindings/audiodelays/Chorus.c index 67b9bc32745c4..94360ee558d5a 100644 --- a/shared-bindings/audiodelays/Chorus.c +++ b/shared-bindings/audiodelays/Chorus.c @@ -228,7 +228,7 @@ static mp_obj_t audiodelays_chorus_obj_play(size_t n_args, const mp_obj_t *pos_a mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_chorus_play(self, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_chorus_play_obj, 1, audiodelays_chorus_obj_play); diff --git a/shared-bindings/audiodelays/Echo.c b/shared-bindings/audiodelays/Echo.c index 68216d2355b70..273d0e8e18dcb 100644 --- a/shared-bindings/audiodelays/Echo.c +++ b/shared-bindings/audiodelays/Echo.c @@ -252,7 +252,7 @@ static mp_obj_t audiodelays_echo_obj_play(size_t n_args, const mp_obj_t *pos_arg mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_echo_play(self, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_echo_play_obj, 1, audiodelays_echo_obj_play); diff --git a/shared-bindings/audiodelays/MultiTapDelay.c b/shared-bindings/audiodelays/MultiTapDelay.c index ce11b2f1d3c61..75b029600a2c0 100644 --- a/shared-bindings/audiodelays/MultiTapDelay.c +++ b/shared-bindings/audiodelays/MultiTapDelay.c @@ -255,7 +255,7 @@ static mp_obj_t audiodelays_multi_tap_delay_obj_play(size_t n_args, const mp_obj mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_multi_tap_delay_play(self, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_multi_tap_delay_play_obj, 1, audiodelays_multi_tap_delay_obj_play); diff --git a/shared-bindings/audiodelays/PitchShift.c b/shared-bindings/audiodelays/PitchShift.c index e595beec6b8de..c8b4ea4b81c79 100644 --- a/shared-bindings/audiodelays/PitchShift.c +++ b/shared-bindings/audiodelays/PitchShift.c @@ -211,7 +211,7 @@ static mp_obj_t audiodelays_pitch_shift_obj_play(size_t n_args, const mp_obj_t * mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_pitch_shift_play(self, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_pitch_shift_play_obj, 1, audiodelays_pitch_shift_obj_play); diff --git a/shared-bindings/audiofilters/Distortion.c b/shared-bindings/audiofilters/Distortion.c index 3120111ca9a80..956a14e97e345 100644 --- a/shared-bindings/audiofilters/Distortion.c +++ b/shared-bindings/audiofilters/Distortion.c @@ -331,7 +331,7 @@ static mp_obj_t audiofilters_distortion_obj_play(size_t n_args, const mp_obj_t * mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_distortion_play(self, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_distortion_play_obj, 1, audiofilters_distortion_obj_play); diff --git a/shared-bindings/audiofilters/Filter.c b/shared-bindings/audiofilters/Filter.c index 2d8829d8a815d..d82208d11ed81 100644 --- a/shared-bindings/audiofilters/Filter.c +++ b/shared-bindings/audiofilters/Filter.c @@ -201,7 +201,7 @@ static mp_obj_t audiofilters_filter_obj_play(size_t n_args, const mp_obj_t *pos_ mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_filter_play(self, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_play_obj, 1, audiofilters_filter_obj_play); diff --git a/shared-bindings/audiofilters/Phaser.c b/shared-bindings/audiofilters/Phaser.c index daff50e983492..8472b01505cec 100644 --- a/shared-bindings/audiofilters/Phaser.c +++ b/shared-bindings/audiofilters/Phaser.c @@ -236,7 +236,7 @@ static mp_obj_t audiofilters_phaser_obj_play(size_t n_args, const mp_obj_t *pos_ mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_phaser_play(self, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_phaser_play_obj, 1, audiofilters_phaser_obj_play); diff --git a/shared-bindings/audiofreeverb/Freeverb.c b/shared-bindings/audiofreeverb/Freeverb.c index 5e4c9644d2e8d..de061734997a0 100644 --- a/shared-bindings/audiofreeverb/Freeverb.c +++ b/shared-bindings/audiofreeverb/Freeverb.c @@ -218,7 +218,7 @@ static mp_obj_t audiofreeverb_freeverb_obj_play(size_t n_args, const mp_obj_t *p mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofreeverb_freeverb_play(self, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofreeverb_freeverb_play_obj, 1, audiofreeverb_freeverb_obj_play); diff --git a/shared-bindings/audiomixer/Mixer.c b/shared-bindings/audiomixer/Mixer.c index 0944f58ddbe45..5e5bfdc1f3df9 100644 --- a/shared-bindings/audiomixer/Mixer.c +++ b/shared-bindings/audiomixer/Mixer.c @@ -189,7 +189,7 @@ static mp_obj_t audiomixer_mixer_obj_play(size_t n_args, const mp_obj_t *pos_arg mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiomixer_mixervoice_play(voice, sample, args[ARG_loop].u_bool); - return pos_args[0]; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixer_play_obj, 1, audiomixer_mixer_obj_play); From 734209078660bc06103cb48eee460b1625323546 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 16 Sep 2025 10:15:47 -0500 Subject: [PATCH 108/517] Add return type documentation --- shared-bindings/audiodelays/Chorus.c | 6 +++++- shared-bindings/audiodelays/Echo.c | 6 +++++- shared-bindings/audiodelays/MultiTapDelay.c | 6 +++++- shared-bindings/audiodelays/PitchShift.c | 6 +++++- shared-bindings/audiofilters/Distortion.c | 6 +++++- shared-bindings/audiofilters/Filter.c | 6 +++++- shared-bindings/audiofilters/Phaser.c | 6 +++++- shared-bindings/audiofreeverb/Freeverb.c | 6 +++++- shared-bindings/audiomixer/Mixer.c | 6 +++++- 9 files changed, 45 insertions(+), 9 deletions(-) diff --git a/shared-bindings/audiodelays/Chorus.c b/shared-bindings/audiodelays/Chorus.c index 94360ee558d5a..87922ff110b1b 100644 --- a/shared-bindings/audiodelays/Chorus.c +++ b/shared-bindings/audiodelays/Chorus.c @@ -210,7 +210,11 @@ MP_PROPERTY_GETTER(audiodelays_chorus_playing_obj, //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Chorus""" //| ... //| static mp_obj_t audiodelays_chorus_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/shared-bindings/audiodelays/Echo.c b/shared-bindings/audiodelays/Echo.c index 273d0e8e18dcb..dbb496c61b975 100644 --- a/shared-bindings/audiodelays/Echo.c +++ b/shared-bindings/audiodelays/Echo.c @@ -234,7 +234,11 @@ MP_PROPERTY_GETTER(audiodelays_echo_playing_obj, //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Echo""" //| ... //| static mp_obj_t audiodelays_echo_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/shared-bindings/audiodelays/MultiTapDelay.c b/shared-bindings/audiodelays/MultiTapDelay.c index 75b029600a2c0..2126dbf990435 100644 --- a/shared-bindings/audiodelays/MultiTapDelay.c +++ b/shared-bindings/audiodelays/MultiTapDelay.c @@ -237,7 +237,11 @@ MP_PROPERTY_GETTER(audiodelays_multi_tap_delay_playing_obj, //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: MultiTapDelay""" //| ... //| static mp_obj_t audiodelays_multi_tap_delay_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/shared-bindings/audiodelays/PitchShift.c b/shared-bindings/audiodelays/PitchShift.c index c8b4ea4b81c79..b94df7d9edd08 100644 --- a/shared-bindings/audiodelays/PitchShift.c +++ b/shared-bindings/audiodelays/PitchShift.c @@ -194,7 +194,11 @@ MP_PROPERTY_GETTER(audiodelays_pitch_shift_playing_obj, //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: PitchShift""" //| ... //| static mp_obj_t audiodelays_pitch_shift_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/shared-bindings/audiofilters/Distortion.c b/shared-bindings/audiofilters/Distortion.c index 956a14e97e345..8de4c1d1ea331 100644 --- a/shared-bindings/audiofilters/Distortion.c +++ b/shared-bindings/audiofilters/Distortion.c @@ -313,7 +313,11 @@ MP_PROPERTY_GETTER(audiofilters_distortion_playing_obj, //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Distortion""" //| ... //| static mp_obj_t audiofilters_distortion_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/shared-bindings/audiofilters/Filter.c b/shared-bindings/audiofilters/Filter.c index d82208d11ed81..426fff226168f 100644 --- a/shared-bindings/audiofilters/Filter.c +++ b/shared-bindings/audiofilters/Filter.c @@ -183,7 +183,11 @@ MP_PROPERTY_GETTER(audiofilters_filter_playing_obj, //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Filter""" //| ... //| static mp_obj_t audiofilters_filter_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/shared-bindings/audiofilters/Phaser.c b/shared-bindings/audiofilters/Phaser.c index 8472b01505cec..9f713afce5f72 100644 --- a/shared-bindings/audiofilters/Phaser.c +++ b/shared-bindings/audiofilters/Phaser.c @@ -218,7 +218,11 @@ MP_PROPERTY_GETTER(audiofilters_phaser_playing_obj, //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Phaser""" //| ... //| static mp_obj_t audiofilters_phaser_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/shared-bindings/audiofreeverb/Freeverb.c b/shared-bindings/audiofreeverb/Freeverb.c index de061734997a0..12eb7ef70ab79 100644 --- a/shared-bindings/audiofreeverb/Freeverb.c +++ b/shared-bindings/audiofreeverb/Freeverb.c @@ -200,7 +200,11 @@ MP_PROPERTY_GETTER(audiofreeverb_freeverb_playing_obj, //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Freeverb""" //| ... //| static mp_obj_t audiofreeverb_freeverb_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { diff --git a/shared-bindings/audiomixer/Mixer.c b/shared-bindings/audiomixer/Mixer.c index 5e5bfdc1f3df9..516d079cd2a09 100644 --- a/shared-bindings/audiomixer/Mixer.c +++ b/shared-bindings/audiomixer/Mixer.c @@ -166,7 +166,11 @@ MP_PROPERTY_GETTER(audiomixer_mixer_voice_obj, //| //| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. //| -//| The sample must match the Mixer's encoding settings given in the constructor.""" +//| The sample must match the Mixer's encoding settings given in the constructor. +//| +//| :return: The mixer object itself. Can be used for chaining, ie: +//| ``audio.play(mixer.play(sample))``. +//| :rtype: Chorus""" //| ... //| static mp_obj_t audiomixer_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { From 9f771b37ba6ca319828c98f4793618cebece32dc Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 19 Sep 2025 11:08:04 -0700 Subject: [PATCH 109/517] Free dma capable temporary buffer immediately This reduces memory use and prevents long term fragmentation. --- shared-module/usb/core/Device.c | 98 +++++++++++++++++++-------------- shared-module/usb/core/Device.h | 4 -- 2 files changed, 56 insertions(+), 46 deletions(-) diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index c4342e10d1d65..83def37de914e 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -43,30 +43,19 @@ static uint8_t *_ensure_dma_buffer(usb_core_device_obj_t *self, const uint8_t *b return (uint8_t *)buffer; // Already DMA-capable, use directly } - // Need to allocate/reallocate temporary buffer in DMA-capable memory - if (self->temp_buffer_size < len) { - self->temp_buffer = port_realloc(self->temp_buffer, len, true); // true = DMA capable - if (self->temp_buffer == NULL) { - self->temp_buffer_size = 0; - return NULL; // Allocation failed - } - self->temp_buffer_size = len; + uint8_t *dma_buffer = port_malloc(len, true); // true = DMA capable + if (dma_buffer == NULL) { + return NULL; // Allocation failed } // Copy data to DMA buffer if writing if (for_write && buffer != NULL) { - memcpy(self->temp_buffer, buffer, len); + memcpy(dma_buffer, buffer, len); } - return self->temp_buffer; + return dma_buffer; } -// Copy data back from DMA buffer to original buffer after read -static void _copy_from_dma_buffer(usb_core_device_obj_t *self, uint8_t *original_buffer, size_t len) { - if (!port_buffer_is_dma_capable(original_buffer) && self->temp_buffer != NULL) { - memcpy(original_buffer, self->temp_buffer, len); - } -} #endif bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_address) { if (!tuh_inited()) { @@ -81,10 +70,6 @@ bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t d } self->device_address = device_address; self->first_langid = 0; - #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE - self->temp_buffer = NULL; - self->temp_buffer_size = 0; - #endif _xfer_result = XFER_RESULT_INVALID; return true; } @@ -104,14 +89,6 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) { self->open_endpoints[i] = 0; } } - #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE - // Clean up temporary buffer - if (self->temp_buffer != NULL) { - port_free(self->temp_buffer); - self->temp_buffer = NULL; - self->temp_buffer_size = 0; - } - #endif self->device_address = 0; } @@ -180,7 +157,24 @@ static void _prepare_for_transfer(void) { _actual_len = 0; } -static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout) { +static void _abort_transfer(tuh_xfer_t *xfer) { + bool aborted = tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); + if (aborted) { + // If the transfer was aborted, then we can continue. + return; + } + uint32_t start_time = supervisor_ticks_ms32(); + // If not, we need to wait for it to finish, otherwise we may free memory out from under it. + // Limit the wait time to 10 milliseconds to avoid blocking indefinitely. + while (_xfer_result == XFER_RESULT_INVALID && (supervisor_ticks_ms32() - start_time < 10)) { + // The background tasks include TinyUSB which will call the function + // we provided above. In other words, the callback isn't in an interrupt. + RUN_BACKGROUND_TASKS; + } +} + +// Only frees the transfer buffer on error. +static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer) { if (xfer == NULL) { mp_raise_usb_core_USBError(NULL); return 0; @@ -195,12 +189,15 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout } if (mp_hal_is_interrupted()) { // Handle case of VM being interrupted by Ctrl-C or autoreload - tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); + _abort_transfer(xfer); return 0; } // Handle transfer result code from TinyUSB xfer_result_t result = _xfer_result; _xfer_result = XFER_RESULT_INVALID; + if (our_buffer && result != XFER_RESULT_SUCCESS && result != XFER_RESULT_INVALID) { + port_free(xfer->buffer); + } switch (result) { case XFER_RESULT_SUCCESS: return _actual_len; @@ -217,8 +214,11 @@ static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout break; case XFER_RESULT_INVALID: // This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB - // to stop the transfer - tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); + // to stop the transfer and then wait to free the buffer. + _abort_transfer(xfer); + if (our_buffer) { + port_free(xfer->buffer); + } mp_raise_usb_core_USBTimeoutError(); break; } @@ -382,14 +382,18 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m _wait_for_callback(); } -static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) { +// Raises an exception on failure. Returns the number of bytes transferred (maybe zero) on success. +static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer) { _prepare_for_transfer(); xfer->complete_cb = _transfer_done_cb; if (!tuh_edpt_xfer(xfer)) { + if (our_buffer) { + port_free(xfer->buffer); + } mp_raise_usb_core_USBError(NULL); return 0; } - return _handle_timed_transfer_callback(xfer, timeout); + return _handle_timed_transfer_callback(xfer, timeout, our_buffer); } static bool _open_endpoint(usb_core_device_obj_t *self, mp_int_t endpoint) { @@ -463,7 +467,13 @@ mp_int_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t xfer.ep_addr = endpoint; xfer.buffer = dma_buffer; xfer.buflen = len; - return _xfer(&xfer, timeout); + size_t result = _xfer(&xfer, timeout, dma_buffer != buffer); + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + if (dma_buffer != buffer) { + port_free(dma_buffer); + } + #endif + return result; } mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t endpoint, uint8_t *buffer, mp_int_t len, mp_int_t timeout) { @@ -488,12 +498,13 @@ mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t e xfer.ep_addr = endpoint; xfer.buffer = dma_buffer; xfer.buflen = len; - mp_int_t result = _xfer(&xfer, timeout); + mp_int_t result = _xfer(&xfer, timeout, dma_buffer != buffer); #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE // Copy data back to original buffer if needed - if (result > 0) { - _copy_from_dma_buffer(self, buffer, result); + if (dma_buffer != buffer) { + memcpy(buffer, dma_buffer, result); + port_free(dma_buffer); } #endif @@ -543,12 +554,15 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, mp_raise_usb_core_USBError(NULL); return 0; } - mp_int_t result = (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout); + mp_int_t result = (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout, dma_buffer != buffer); #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE - // Copy data back to original buffer if this was a read transfer and we got data - if ((bmRequestType & 0x80) != 0 && result > 0 && buffer != NULL) { // Read transfer (device-to-host) - _copy_from_dma_buffer(self, buffer, result); + if (dma_buffer != buffer) { + // Copy data back to original buffer if this was a read transfer + if (buffer != NULL && !is_write) { + memcpy(buffer, dma_buffer, result); + } + port_free(dma_buffer); } #endif diff --git a/shared-module/usb/core/Device.h b/shared-module/usb/core/Device.h index 8737eeaf86bf7..c7392a04ba7bc 100644 --- a/shared-module/usb/core/Device.h +++ b/shared-module/usb/core/Device.h @@ -15,8 +15,4 @@ typedef struct { uint8_t *configuration_descriptor; // Contains the length of the all descriptors. uint8_t open_endpoints[8]; uint16_t first_langid; - #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE - uint8_t *temp_buffer; // Temporary buffer for PSRAM data - size_t temp_buffer_size; // Size of temporary buffer - #endif } usb_core_device_obj_t; From dd0e10d6f45398683627f268eb6ad060044887d6 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 21 Sep 2025 11:49:58 -0400 Subject: [PATCH 110/517] turn on canio --- .../microchip_curiosity_circuitpython_nano/mpconfigboard.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk index 34c9d7bcaab28..4bb7b68550922 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk @@ -11,3 +11,4 @@ EXTERNAL_FLASH_DEVICES = "SST26VF016B,SST26VF032B,SST26VF064B" LONGINT_IMPL = MPZ CIRCUITPY__EVE = 1 +CIRCUITPY_CANIO = 1 From 9d006d42604eaffc2782389f34287eeea5f9ef13 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Mon, 22 Sep 2025 19:40:55 +0200 Subject: [PATCH 111/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 4 ++++ locale/el.po | 4 ++++ locale/hi.po | 4 ++++ locale/ko.po | 4 ++++ locale/ru.po | 4 ++++ locale/tr.po | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/locale/cs.po b/locale/cs.po index 67ece22664d76..eab928c2aaaf6 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -861,6 +861,10 @@ msgstr "Pole souřadnic mají různé délky" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" diff --git a/locale/el.po b/locale/el.po index 9e31051bcc03b..871ce82f6725c 100644 --- a/locale/el.po +++ b/locale/el.po @@ -867,6 +867,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index 7ab47ff0953b5..f8408f6c119a5 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -846,6 +846,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 30205cad930e0..4a74ec9003488 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -890,6 +890,10 @@ msgstr "좌표 배열의 길이가 다릅니다" msgid "Coordinate arrays types have different sizes" msgstr "좌표 배열 유형은 크기가 다릅니다" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" diff --git a/locale/ru.po b/locale/ru.po index 49fb1eccdedef..24b880972cb42 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -870,6 +870,10 @@ msgstr "Координатные массивы имеют разные длин msgid "Coordinate arrays types have different sizes" msgstr "Типы массивов координат имеют разные размеры" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" diff --git a/locale/tr.po b/locale/tr.po index 1bb304eaba6ca..f51ad3e8c12ee 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -860,6 +860,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" From 493f078739d1e5ce93dfc5ef9d0a52a29c63b4ba Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Thu, 26 Dec 2024 13:25:26 -0800 Subject: [PATCH 112/517] Setup files for busio --- ports/analog/common-hal/busio/I2C.c | 210 ++++++++++++++++++ ports/analog/common-hal/busio/I2C.h | 24 ++ ports/analog/common-hal/busio/SPI.c | 254 ++++++++++++++++++++++ ports/analog/common-hal/busio/SPI.h | 35 +++ ports/analog/common-hal/busio/UART.c | 76 +++++++ ports/analog/common-hal/busio/UART.h | 31 +++ ports/analog/common-hal/busio/__init__.c | 5 + ports/analog/peripherals/max32690/gpios.h | 2 + 8 files changed, 637 insertions(+) create mode 100644 ports/analog/common-hal/busio/I2C.c create mode 100644 ports/analog/common-hal/busio/I2C.h create mode 100644 ports/analog/common-hal/busio/SPI.c create mode 100644 ports/analog/common-hal/busio/SPI.h create mode 100644 ports/analog/common-hal/busio/UART.c create mode 100644 ports/analog/common-hal/busio/UART.h create mode 100644 ports/analog/common-hal/busio/__init__.c diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c new file mode 100644 index 0000000000000..ae18f561c502f --- /dev/null +++ b/ports/analog/common-hal/busio/I2C.c @@ -0,0 +1,210 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * 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 "shared-bindings/busio/I2C.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" + +static I2CSPM_Init_TypeDef i2cspm_init; +static bool in_used = false; + +// Construct I2C protocol, this function init i2c peripheral +void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, + const mcu_pin_obj_t *scl, + const mcu_pin_obj_t *sda, + uint32_t frequency, uint32_t timeout) { + + // Ensure the object starts in its deinit state. + common_hal_busio_i2c_mark_deinit(self); + + if ((scl != NULL) && (sda != NULL)) { + if (scl->function_list[ DEFAULT_I2C_PERIPHERAL == I2C1? + FN_I2C1_SCL : FN_I2C0_SCL] == 1 && + scl->function_list[DEFAULT_I2C_PERIPHERAL == I2C1? + FN_I2C1_SDA : FN_I2C0_SDA] == 1) { + self->scl = scl; + self->sda = sda; + self->has_lock = false; + i2cspm_init.sclPort = self->scl->port; + i2cspm_init.sclPin = self->scl->number; + i2cspm_init.sdaPort = self->sda->port; + i2cspm_init.sdaPin = self->sda->number; + i2cspm_init.port = DEFAULT_I2C_PERIPHERAL; + i2cspm_init.i2cRefFreq = 0; + i2cspm_init.i2cMaxFreq = I2C_FREQ_STANDARD_MAX; + i2cspm_init.i2cClhr = i2cClockHLRStandard; + + self->i2cspm = i2cspm_init.port; + I2CSPM_Init(&i2cspm_init); + common_hal_mcu_pin_claim(scl); + common_hal_mcu_pin_claim(sda); + in_used = true; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Hardware in use, try alternative pins")); + } + } else { + raise_ValueError_invalid_pins(); + } +} + +// Never reset I2C obj when reload +void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { + common_hal_never_reset_pin(self->sda); + common_hal_never_reset_pin(self->scl); +} + +// Check I2C status, deinited or not +bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { + return self->sda == NULL; +} + +// Deinit i2c obj, reset I2C pin +void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { + if (common_hal_busio_i2c_deinited(self)) { + return; + } + I2C_Reset(self->i2cspm); + common_hal_reset_pin(self->sda); + common_hal_reset_pin(self->scl); + self->i2cspm = NULL; + in_used = false; + common_hal_busio_i2c_mark_deinit(self); +} + +void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { + self->sda = NULL; +} + +// Probe device in I2C bus +bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { + I2C_TransferSeq_TypeDef seq; + I2C_TransferReturn_TypeDef ret; + uint8_t data = 0; + + seq.addr = addr << 1; + seq.flags = I2C_FLAG_READ; + + seq.buf[0].data = &data; + seq.buf[0].len = 1; + + ret = I2CSPM_Transfer(self->i2cspm, &seq); + if (ret != i2cTransferDone) { + return false; + } + return true; +} + +// Lock I2C bus +bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { + if (common_hal_busio_i2c_deinited(self)) { + return false; + } + bool grabbed_lock = false; + + if (!self->has_lock) { + grabbed_lock = true; + self->has_lock = true; + } + + return grabbed_lock; +} + +// Check I2C lock status +bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { + return self->has_lock; +} + +// Unlock I2C bus +void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { + self->has_lock = false; +} + +// Write data to the device selected by address +uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, + const uint8_t *data, size_t len) { + + I2C_TransferSeq_TypeDef seq; + I2C_TransferReturn_TypeDef ret; + + seq.addr = addr << 1; + seq.flags = I2C_FLAG_WRITE; + + seq.buf[0].data = (uint8_t *)data; + seq.buf[0].len = len; + + ret = I2CSPM_Transfer(self->i2cspm, &seq); + if (ret != i2cTransferDone) { + return MP_EIO; + } + return 0; +} + +// Read into buffer from the device selected by address +uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, + uint16_t addr, + uint8_t *data, size_t len) { + + I2C_TransferSeq_TypeDef seq; + I2C_TransferReturn_TypeDef ret; + + seq.addr = addr << 1; + seq.flags = I2C_FLAG_READ; + + seq.buf[0].data = data; + seq.buf[0].len = len; + + ret = I2CSPM_Transfer(self->i2cspm, &seq); + if (ret != i2cTransferDone) { + return MP_EIO; + } + return 0; +} + +// Write the bytes from out_data to the device selected by address, +uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, + uint8_t *out_data, size_t out_len, + uint8_t *in_data, size_t in_len) { + + I2C_TransferSeq_TypeDef seq; + I2C_TransferReturn_TypeDef ret; + + seq.addr = addr << 1; + seq.flags = I2C_FLAG_WRITE_READ; + // Select command to issue + seq.buf[0].data = out_data; + seq.buf[0].len = out_len; + // Select location/length of data to be read + seq.buf[1].data = in_data; + seq.buf[1].len = in_len; + + ret = I2CSPM_Transfer(self->i2cspm, &seq); + if (ret != i2cTransferDone) { + return MP_EIO; + } + return 0; +} diff --git a/ports/analog/common-hal/busio/I2C.h b/ports/analog/common-hal/busio/I2C.h new file mode 100644 index 0000000000000..c92283d6c53b6 --- /dev/null +++ b/ports/analog/common-hal/busio/I2C.h @@ -0,0 +1,24 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +// FIXME: Silabs includes "peripherals/periph.h. Figure out what's in this file. " +#include "py/obj.h" + +// HAL-specific +#include "i2c.h" +#include "gpio.h" + +// Define a struct for what BUSIO.I2C should carry +typedef struct { + mp_obj_base_t base; + mxc_i2c_regs_t* i2c_regs; + bool has_lock; + const mcu_pin_obj_t *scl; + const mcu_pin_obj_t *sda; +} busio_i2c_obj_t; diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c new file mode 100644 index 0000000000000..74cfc69bfb2a0 --- /dev/null +++ b/ports/analog/common-hal/busio/SPI.c @@ -0,0 +1,254 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * 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 "shared-bindings/busio/SPI.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "supervisor/board.h" +#include "shared-bindings/microcontroller/Pin.h" + +// Note that any bugs introduced in this file can cause crashes +// at startupfor chips using external SPI flash. + +static SPIDRV_HandleData_t spidrv_eusart_handle; +static SPIDRV_Init_t spidrv_eusart_init = SPIDRV_MASTER_EUSART1; +static bool in_used = false; +static bool never_reset = false; + +// Reset SPI when reload +void spi_reset(void) { + if (!never_reset && in_used) { + SPIDRV_DeInit(&spidrv_eusart_handle); + in_used = false; + } + return; +} + +// Construct SPI protocol, this function init SPI peripheral +void common_hal_busio_spi_construct(busio_spi_obj_t *self, + const mcu_pin_obj_t *sck, + const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *miso, + bool half_duplex) { + Ecode_t sc = ECODE_OK; + + if (half_duplex) { + mp_raise_NotImplementedError( + MP_ERROR_TEXT("Half duplex SPI is not implemented")); + } + + if ((sck != NULL) && (mosi != NULL) && (miso != NULL)) { + if (sck->function_list[FN_EUSART1_SCLK] == 1 + && miso->function_list[FN_EUSART1_RX] == 1 + && mosi->function_list[FN_EUSART1_TX] == 1) { + + self->sck = sck; + self->mosi = mosi; + self->miso = miso; + self->handle = &spidrv_eusart_handle; + self->polarity = 0; + self->phase = 0; + self->bits = 8; + + spidrv_eusart_init.portTx = mosi->port; + spidrv_eusart_init.portRx = miso->port; + spidrv_eusart_init.portClk = sck->port; + spidrv_eusart_init.pinTx = mosi->number; + spidrv_eusart_init.pinRx = miso->number; + spidrv_eusart_init.pinClk = sck->number; + spidrv_eusart_init.bitRate = 1000000; + spidrv_eusart_init.frameLength = 8; + spidrv_eusart_init.dummyTxValue = 0; + spidrv_eusart_init.type = spidrvMaster; + spidrv_eusart_init.bitOrder = spidrvBitOrderMsbFirst; + spidrv_eusart_init.clockMode = spidrvClockMode0; + spidrv_eusart_init.csControl = spidrvCsControlApplication; + spidrv_eusart_init.slaveStartMode = spidrvSlaveStartImmediate; + + sc = SPIDRV_Init(self->handle, &spidrv_eusart_init); + if (sc != ECODE_EMDRV_SPIDRV_OK) { + mp_raise_ValueError(MP_ERROR_TEXT("SPI init error")); + } + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Hardware in use, try alternative pins")); + } + } else { + raise_ValueError_invalid_pins(); + } + + in_used = true; + common_hal_mcu_pin_claim(sck); + common_hal_mcu_pin_claim(mosi); + common_hal_mcu_pin_claim(miso); +} + +// Never reset SPI when reload +void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { + never_reset = true; + common_hal_never_reset_pin(self->mosi); + common_hal_never_reset_pin(self->miso); + common_hal_never_reset_pin(self->sck); +} + +// Check SPI status, deinited or not +bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { + return self->sck == NULL; +} + +// Deinit SPI obj +void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { + + if (common_hal_busio_spi_deinited(self)) { + return; + } + + Ecode_t sc = SPIDRV_DeInit(self->handle); + if (sc != ECODE_EMDRV_SPIDRV_OK) { + mp_raise_RuntimeError(MP_ERROR_TEXT("SPI re-init")); + } + + in_used = false; + self->sck = NULL; + self->mosi = NULL; + self->miso = NULL; + self->handle = NULL; + common_hal_reset_pin(self->mosi); + common_hal_reset_pin(self->miso); + common_hal_reset_pin(self->sck); +} + +// Configures the SPI bus. The SPI object must be locked. +bool common_hal_busio_spi_configure(busio_spi_obj_t *self, + uint32_t baudrate, + uint8_t polarity, + uint8_t phase, + uint8_t bits) { + Ecode_t sc; + // This resets the SPI, so check before updating it redundantly + if (baudrate == self->baudrate && polarity == self->polarity + && phase == self->phase && bits == self->bits) { + return true; + } + + sc = SPIDRV_DeInit(self->handle); + if (sc != ECODE_EMDRV_SPIDRV_OK) { + mp_raise_RuntimeError(MP_ERROR_TEXT("SPI re-init")); + } + in_used = false; + self->baudrate = baudrate; + self->phase = phase; + self->bits = bits; + self->polarity = polarity; + + spidrv_eusart_init.bitRate = baudrate; + spidrv_eusart_init.frameLength = 8; + if (polarity == 0 && phase == 0) { + spidrv_eusart_init.clockMode = spidrvClockMode0; + } else if (polarity == 0 && phase == 1) { + spidrv_eusart_init.clockMode = spidrvClockMode1; + } else if (polarity == 1 && phase == 0) { + spidrv_eusart_init.clockMode = spidrvClockMode2; + } else if (polarity == 1 && phase == 1) { + spidrv_eusart_init.clockMode = spidrvClockMode3; + } + + sc = SPIDRV_Init(self->handle, &spidrv_eusart_init); + if (sc != ECODE_EMDRV_SPIDRV_OK) { + mp_raise_RuntimeError(MP_ERROR_TEXT("SPI re-init")); + } + in_used = true; + return true; +} + +// Lock SPI bus +bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + bool grabbed_lock = false; + if (!self->has_lock) { + grabbed_lock = true; + self->has_lock = true; + } + + return grabbed_lock; +} + +// Check SPI lock status +bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { + return self->has_lock; +} + +// Unlock SPI bus +void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { + self->has_lock = false; +} + +// Write the data contained in buffer +bool common_hal_busio_spi_write(busio_spi_obj_t *self, + const uint8_t *data, + size_t len) { + + Ecode_t result = SPIDRV_MTransmitB(self->handle, data, len); + return result == ECODE_EMDRV_SPIDRV_OK; +} + +// Read data into buffer +bool common_hal_busio_spi_read(busio_spi_obj_t *self, + uint8_t *data, size_t len, + uint8_t write_value) { + + self->handle->initData.dummyTxValue = write_value; + Ecode_t result = SPIDRV_MReceiveB(self->handle, data, len); + return result == ECODE_EMDRV_SPIDRV_OK; +} + +// Write out the data in data_out +// while simultaneously reading data into data_in +bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, + const uint8_t *data_out, + uint8_t *data_in, + size_t len) { + + Ecode_t result = SPIDRV_MTransferB(self->handle, data_out, data_in, len); + return result == ECODE_EMDRV_SPIDRV_OK; +} + +// Get SPI baudrate +uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) { + return self->baudrate; +} + +// Get SPI phase +uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { + return self->phase; +} + +// Get SPI polarity +uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { + return self->polarity; +} diff --git a/ports/analog/common-hal/busio/SPI.h b/ports/analog/common-hal/busio/SPI.h new file mode 100644 index 0000000000000..8c48147ff6c77 --- /dev/null +++ b/ports/analog/common-hal/busio/SPI.h @@ -0,0 +1,35 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H +#define MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H + +#include "common-hal/microcontroller/Pin.h" +// FIXME: Silabs includes "peripherals/periph.h. Figure out what's in this file. " +#include "py/obj.h" + +// HAL-specific +#include "spi.h" + +// Define a struct for what BUSIO.SPI should carry +typedef struct { + mp_obj_base_t base; + mxc_spi_regs_t* spi_regs; + bool has_lock; + const mcu_pin_obj_t *sck; + const mcu_pin_obj_t *mosi; + const mcu_pin_obj_t *miso; + const mcu_pin_obj_t *nss; + uint32_t baudrate; + uint16_t prescaler; + uint8_t polarity; + uint8_t phase; + uint8_t bits; +} busio_spi_obj_t; + +void spi_reset(void); + +#endif // MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c new file mode 100644 index 0000000000000..6d1507b78c696 --- /dev/null +++ b/ports/analog/common-hal/busio/UART.c @@ -0,0 +1,76 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * 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 "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/busio/UART.h" + +#include "mpconfigport.h" +#include "shared/readline/readline.h" +#include "shared/runtime/interrupt_char.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" + +#include "uart.h" +#include "UART.h" + +typedef enum { + BUSIO_UART_PARITY_NONE, + BUSIO_UART_PARITY_EVEN, + BUSIO_UART_PARITY_ODD +} busio_uart_parity_t; + +// Construct an underlying UART object. +extern void common_hal_busio_uart_construct(busio_uart_obj_t *self, + const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, + const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts, + const mcu_pin_obj_t *rs485_dir, bool rs485_invert, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, + mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer, + bool sigint_enabled); + +extern void common_hal_busio_uart_deinit(busio_uart_obj_t *self); +extern bool common_hal_busio_uart_deinited(busio_uart_obj_t *self); + +// Read characters. len is in characters NOT bytes! +extern size_t common_hal_busio_uart_read(busio_uart_obj_t *self, + uint8_t *data, size_t len, int *errcode); + +// Write characters. len is in characters NOT bytes! +extern size_t common_hal_busio_uart_write(busio_uart_obj_t *self, + const uint8_t *data, size_t len, int *errcode); + +extern uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self); +extern void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate); +extern mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self); +extern void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout); + +extern uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self); +extern void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self); +extern bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self); + +extern void common_hal_busio_uart_never_reset(busio_uart_obj_t *self); diff --git a/ports/analog/common-hal/busio/UART.h b/ports/analog/common-hal/busio/UART.h new file mode 100644 index 0000000000000..a8d294de222d3 --- /dev/null +++ b/ports/analog/common-hal/busio/UART.h @@ -0,0 +1,31 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_EFR32_COMMON_HAL_BUSIO_UART_H +#define MICROPY_INCLUDED_EFR32_COMMON_HAL_BUSIO_UART_H + +#include "common-hal/microcontroller/Pin.h" +#include "peripherals/periph.h" +#include "py/obj.h" +#include "py/ringbuf.h" + +#include "uart.h" + +// Define a struct for what BUSIO.UART should contain +typedef struct { + mp_obj_base_t base; + mxc_uart_regs_t* uart_regs; + const mcu_pin_obj_t *rx; + const mcu_pin_obj_t *tx; + uint32_t baudrate; + bool parity; + uint8_t stop_bits; + uint8_t bits; +} busio_uart_obj_t; + +void uart_reset(void); + +#endif // MICROPY_INCLUDED_EFR32_COMMON_HAL_BUSIO_UART_H diff --git a/ports/analog/common-hal/busio/__init__.c b/ports/analog/common-hal/busio/__init__.c new file mode 100644 index 0000000000000..ff05be051bb25 --- /dev/null +++ b/ports/analog/common-hal/busio/__init__.c @@ -0,0 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT diff --git a/ports/analog/peripherals/max32690/gpios.h b/ports/analog/peripherals/max32690/gpios.h index 4677bf8f33dbe..fe91227728291 100644 --- a/ports/analog/peripherals/max32690/gpios.h +++ b/ports/analog/peripherals/max32690/gpios.h @@ -4,6 +4,8 @@ // // SPDX-License-Identifier: MIT +#pragma once + #include "py/obj.h" #include "py/mphal.h" From f4c21716f5385b62a4400312c8de53abc195ab00 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Tue, 14 Jan 2025 15:19:04 -0800 Subject: [PATCH 113/517] Add default build for analog ports for apard32690 board. --- Makefile | 3 +++ ports/analog/Makefile | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f2fc070a6331d..c2aebc61f1c0b 100644 --- a/Makefile +++ b/Makefile @@ -299,6 +299,9 @@ update-frozen-libraries: one-of-each: samd21 litex mimxrt10xx nordic stm +analog: + $(MAKE) -C ports/analog/ BOARD=apard32690 + samd21: $(MAKE) -C ports/atmel-samd BOARD=trinket_m0 diff --git a/ports/analog/Makefile b/ports/analog/Makefile index ce9082c5e255a..96f9037aae924 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -6,12 +6,13 @@ # # SPDX-License-Identifier: MIT +BOARD ?= apard32690 +CROSS_COMPILE = arm-none-eabi- + # Includes mpconfigboard.mk & mpconfigport.mk, # along with numerous other shared environment makefiles. include ../../py/circuitpy_mkenv.mk -CROSS_COMPILE = arm-none-eabi- - # MCU_SERIES e.g. "max32" # MCU_VARIANT e.g. "max32690" # defined in mpconfigboard.mk @@ -20,6 +21,7 @@ MCU_SERIES_UPPER := $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]') MCU_VARIANT_LOWER := $(shell echo $(MCU_VARIANT) | tr '[:upper:]' '[:lower:]') MCU_VARIANT_UPPER := $(shell echo $(MCU_VARIANT) | tr '[:lower:]' '[:upper:]') + # ******************************************************************************* #### MSDK INCLUDES #### # Necessary for msdk makefiles @@ -59,6 +61,7 @@ DIE_TYPE=me18 endif PERIPH_SRC = $(ADI_PERIPH)/Source +PERIPH_INC = $(ADI_PERIPH)/Include/$(MCU_VARIANT_UPPER) INC += -I. INC += -I../.. @@ -75,7 +78,7 @@ INC += \ -I$(TOP)/lib/cmsis/inc \ -I$(CMSIS_ROOT)/Include \ -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Include \ - -I$(ADI_PERIPH)/Include/$(MCU_VARIANT_UPPER) \ + -I$(PERIPH_INC) \ -I$(PERIPH_SRC)/SYS \ -I$(PERIPH_SRC)/CTB \ -I$(PERIPH_SRC)/DMA \ @@ -126,7 +129,8 @@ SRC_C += $(SRC_MAX32) \ boards/$(BOARD)/board.c \ boards/$(BOARD)/pins.c \ peripherals/$(MCU_VARIANT_LOWER)/pins.c \ - peripherals/$(MCU_VARIANT_LOWER)/gpios.c + peripherals/$(MCU_VARIANT_LOWER)/gpios.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_uart.c # ******************************************************************************* ### Compiler & Linker Flags ### From 1380af79aae33182845c869e0a7c40829bf0d315 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Tue, 14 Jan 2025 15:22:10 -0800 Subject: [PATCH 114/517] Add preliminary busio.UART support - Enabled BUSIO in mpconfigport.mk - Stubs added for busio.I2C & busio.SPI - Implemented busio.UART to basic working functionality - Added MCU-specific UART data in peripherals//max32_uart.c & max32_uart.h - Still have a couple issues where UART generates garbage characters or does not respond to timeouts / generate asynchronous IRQs --- ports/analog/common-hal/busio/I2C.c | 116 +---- ports/analog/common-hal/busio/SPI.c | 147 +------ ports/analog/common-hal/busio/UART.c | 406 +++++++++++++++++- ports/analog/common-hal/busio/UART.h | 33 +- ports/analog/max32_port.h | 52 +-- ports/analog/mpconfigport.mk | 3 +- .../analog/peripherals/max32690/max32_uart.c | 47 ++ .../analog/peripherals/max32690/max32_uart.h | 16 + 8 files changed, 527 insertions(+), 293 deletions(-) create mode 100644 ports/analog/peripherals/max32690/max32_uart.c create mode 100644 ports/analog/peripherals/max32690/max32_uart.h diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index ae18f561c502f..a8080fa5bc10d 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -30,7 +30,6 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" -static I2CSPM_Init_TypeDef i2cspm_init; static bool in_used = false; // Construct I2C protocol, this function init i2c peripheral @@ -39,37 +38,7 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) { - // Ensure the object starts in its deinit state. - common_hal_busio_i2c_mark_deinit(self); - - if ((scl != NULL) && (sda != NULL)) { - if (scl->function_list[ DEFAULT_I2C_PERIPHERAL == I2C1? - FN_I2C1_SCL : FN_I2C0_SCL] == 1 && - scl->function_list[DEFAULT_I2C_PERIPHERAL == I2C1? - FN_I2C1_SDA : FN_I2C0_SDA] == 1) { - self->scl = scl; - self->sda = sda; - self->has_lock = false; - i2cspm_init.sclPort = self->scl->port; - i2cspm_init.sclPin = self->scl->number; - i2cspm_init.sdaPort = self->sda->port; - i2cspm_init.sdaPin = self->sda->number; - i2cspm_init.port = DEFAULT_I2C_PERIPHERAL; - i2cspm_init.i2cRefFreq = 0; - i2cspm_init.i2cMaxFreq = I2C_FREQ_STANDARD_MAX; - i2cspm_init.i2cClhr = i2cClockHLRStandard; - - self->i2cspm = i2cspm_init.port; - I2CSPM_Init(&i2cspm_init); - common_hal_mcu_pin_claim(scl); - common_hal_mcu_pin_claim(sda); - in_used = true; - } else { - mp_raise_ValueError(MP_ERROR_TEXT("Hardware in use, try alternative pins")); - } - } else { - raise_ValueError_invalid_pins(); - } + } // Never reset I2C obj when reload @@ -85,53 +54,26 @@ bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { // Deinit i2c obj, reset I2C pin void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { - if (common_hal_busio_i2c_deinited(self)) { - return; - } - I2C_Reset(self->i2cspm); - common_hal_reset_pin(self->sda); - common_hal_reset_pin(self->scl); - self->i2cspm = NULL; - in_used = false; - common_hal_busio_i2c_mark_deinit(self); + // FIXME: Implement } void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { - self->sda = NULL; + // FIXME: Implement } // Probe device in I2C bus bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { - I2C_TransferSeq_TypeDef seq; - I2C_TransferReturn_TypeDef ret; - uint8_t data = 0; - - seq.addr = addr << 1; - seq.flags = I2C_FLAG_READ; - - seq.buf[0].data = &data; - seq.buf[0].len = 1; + // FIXME: Implement - ret = I2CSPM_Transfer(self->i2cspm, &seq); - if (ret != i2cTransferDone) { - return false; - } return true; } // Lock I2C bus bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { - if (common_hal_busio_i2c_deinited(self)) { - return false; - } - bool grabbed_lock = false; - if (!self->has_lock) { - grabbed_lock = true; - self->has_lock = true; - } + // FIXME: Implement - return grabbed_lock; + return false; } // Check I2C lock status @@ -148,19 +90,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { - I2C_TransferSeq_TypeDef seq; - I2C_TransferReturn_TypeDef ret; - - seq.addr = addr << 1; - seq.flags = I2C_FLAG_WRITE; - - seq.buf[0].data = (uint8_t *)data; - seq.buf[0].len = len; - - ret = I2CSPM_Transfer(self->i2cspm, &seq); - if (ret != i2cTransferDone) { - return MP_EIO; - } + // FIXME: Implement return 0; } @@ -169,19 +99,7 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { - I2C_TransferSeq_TypeDef seq; - I2C_TransferReturn_TypeDef ret; - - seq.addr = addr << 1; - seq.flags = I2C_FLAG_READ; - - seq.buf[0].data = data; - seq.buf[0].len = len; - - ret = I2CSPM_Transfer(self->i2cspm, &seq); - if (ret != i2cTransferDone) { - return MP_EIO; - } + // FIXME: Implement return 0; } @@ -190,21 +108,7 @@ uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - I2C_TransferSeq_TypeDef seq; - I2C_TransferReturn_TypeDef ret; - - seq.addr = addr << 1; - seq.flags = I2C_FLAG_WRITE_READ; - // Select command to issue - seq.buf[0].data = out_data; - seq.buf[0].len = out_len; - // Select location/length of data to be read - seq.buf[1].data = in_data; - seq.buf[1].len = in_len; - - ret = I2CSPM_Transfer(self->i2cspm, &seq); - if (ret != i2cTransferDone) { - return MP_EIO; - } + // FIXME: Implement + return 0; } diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index 74cfc69bfb2a0..083403b12bbe3 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -34,17 +34,9 @@ // Note that any bugs introduced in this file can cause crashes // at startupfor chips using external SPI flash. -static SPIDRV_HandleData_t spidrv_eusart_handle; -static SPIDRV_Init_t spidrv_eusart_init = SPIDRV_MASTER_EUSART1; -static bool in_used = false; -static bool never_reset = false; - // Reset SPI when reload void spi_reset(void) { - if (!never_reset && in_used) { - SPIDRV_DeInit(&spidrv_eusart_handle); - in_used = false; - } + // FIXME: Implement return; } @@ -54,64 +46,14 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { - Ecode_t sc = ECODE_OK; - - if (half_duplex) { - mp_raise_NotImplementedError( - MP_ERROR_TEXT("Half duplex SPI is not implemented")); - } - - if ((sck != NULL) && (mosi != NULL) && (miso != NULL)) { - if (sck->function_list[FN_EUSART1_SCLK] == 1 - && miso->function_list[FN_EUSART1_RX] == 1 - && mosi->function_list[FN_EUSART1_TX] == 1) { - - self->sck = sck; - self->mosi = mosi; - self->miso = miso; - self->handle = &spidrv_eusart_handle; - self->polarity = 0; - self->phase = 0; - self->bits = 8; - - spidrv_eusart_init.portTx = mosi->port; - spidrv_eusart_init.portRx = miso->port; - spidrv_eusart_init.portClk = sck->port; - spidrv_eusart_init.pinTx = mosi->number; - spidrv_eusart_init.pinRx = miso->number; - spidrv_eusart_init.pinClk = sck->number; - spidrv_eusart_init.bitRate = 1000000; - spidrv_eusart_init.frameLength = 8; - spidrv_eusart_init.dummyTxValue = 0; - spidrv_eusart_init.type = spidrvMaster; - spidrv_eusart_init.bitOrder = spidrvBitOrderMsbFirst; - spidrv_eusart_init.clockMode = spidrvClockMode0; - spidrv_eusart_init.csControl = spidrvCsControlApplication; - spidrv_eusart_init.slaveStartMode = spidrvSlaveStartImmediate; - - sc = SPIDRV_Init(self->handle, &spidrv_eusart_init); - if (sc != ECODE_EMDRV_SPIDRV_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("SPI init error")); - } - } else { - mp_raise_ValueError(MP_ERROR_TEXT("Hardware in use, try alternative pins")); - } - } else { - raise_ValueError_invalid_pins(); - } - - in_used = true; - common_hal_mcu_pin_claim(sck); - common_hal_mcu_pin_claim(mosi); - common_hal_mcu_pin_claim(miso); + + + // FIXME: Implement } // Never reset SPI when reload void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset = true; - common_hal_never_reset_pin(self->mosi); - common_hal_never_reset_pin(self->miso); - common_hal_never_reset_pin(self->sck); + // FIXME: Implement } // Check SPI status, deinited or not @@ -122,23 +64,7 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { // Deinit SPI obj void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { - if (common_hal_busio_spi_deinited(self)) { - return; - } - - Ecode_t sc = SPIDRV_DeInit(self->handle); - if (sc != ECODE_EMDRV_SPIDRV_OK) { - mp_raise_RuntimeError(MP_ERROR_TEXT("SPI re-init")); - } - - in_used = false; - self->sck = NULL; - self->mosi = NULL; - self->miso = NULL; - self->handle = NULL; - common_hal_reset_pin(self->mosi); - common_hal_reset_pin(self->miso); - common_hal_reset_pin(self->sck); + // FIXME: Implement } // Configures the SPI bus. The SPI object must be locked. @@ -147,55 +73,15 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint8_t polarity, uint8_t phase, uint8_t bits) { - Ecode_t sc; - // This resets the SPI, so check before updating it redundantly - if (baudrate == self->baudrate && polarity == self->polarity - && phase == self->phase && bits == self->bits) { - return true; - } - - sc = SPIDRV_DeInit(self->handle); - if (sc != ECODE_EMDRV_SPIDRV_OK) { - mp_raise_RuntimeError(MP_ERROR_TEXT("SPI re-init")); - } - in_used = false; - self->baudrate = baudrate; - self->phase = phase; - self->bits = bits; - self->polarity = polarity; - - spidrv_eusart_init.bitRate = baudrate; - spidrv_eusart_init.frameLength = 8; - if (polarity == 0 && phase == 0) { - spidrv_eusart_init.clockMode = spidrvClockMode0; - } else if (polarity == 0 && phase == 1) { - spidrv_eusart_init.clockMode = spidrvClockMode1; - } else if (polarity == 1 && phase == 0) { - spidrv_eusart_init.clockMode = spidrvClockMode2; - } else if (polarity == 1 && phase == 1) { - spidrv_eusart_init.clockMode = spidrvClockMode3; - } - - sc = SPIDRV_Init(self->handle, &spidrv_eusart_init); - if (sc != ECODE_EMDRV_SPIDRV_OK) { - mp_raise_RuntimeError(MP_ERROR_TEXT("SPI re-init")); - } - in_used = true; + + // FIXME: Implement return true; } // Lock SPI bus bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { - if (common_hal_busio_spi_deinited(self)) { - return false; - } - bool grabbed_lock = false; - if (!self->has_lock) { - grabbed_lock = true; - self->has_lock = true; - } - - return grabbed_lock; + // FIXME: Implement + return false; } // Check SPI lock status @@ -213,8 +99,8 @@ bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) { - Ecode_t result = SPIDRV_MTransmitB(self->handle, data, len); - return result == ECODE_EMDRV_SPIDRV_OK; + // FIXME: Implement + return false; } // Read data into buffer @@ -222,9 +108,8 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) { - self->handle->initData.dummyTxValue = write_value; - Ecode_t result = SPIDRV_MReceiveB(self->handle, data, len); - return result == ECODE_EMDRV_SPIDRV_OK; + // FIXME: Implement + return false; } // Write out the data in data_out @@ -234,8 +119,8 @@ bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_in, size_t len) { - Ecode_t result = SPIDRV_MTransferB(self->handle, data_out, data_in, len); - return result == ECODE_EMDRV_SPIDRV_OK; + // FIXME: Implement + return false; } // Get SPI baudrate diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index 6d1507b78c696..ddffc8f55b9c3 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -26,8 +26,11 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" +#include "supervisor/shared/tick.h" #include "shared-bindings/busio/UART.h" +#include "shared-bindings/microcontroller/Pin.h" + #include "mpconfigport.h" #include "shared/readline/readline.h" #include "shared/runtime/interrupt_char.h" @@ -35,42 +38,403 @@ #include "py/mperrno.h" #include "py/runtime.h" -#include "uart.h" +#include "max32_port.h" #include "UART.h" +#include "nvic_table.h" + +// UART IRQ Priority +#define UART_PRIORITY 1 + +/** + * +// Define a struct for what BUSIO.UART should contain +typedef struct { + mp_obj_base_t base; + int uart_id; + mxc_uart_regs_t* uart_regs; + + const mcu_pin_obj_t *rx_pin; + const mcu_pin_obj_t *tx_pin; + const mcu_pin_obj_t *rts_pin; + const mcu_pin_obj_t *cts_pin; + + uint8_t bits; + uint32_t baudrate; + bool parity; + uint8_t stop_bits; + + uint32_t timeout_ms; + bool error; +} busio_uart_obj_t; + */ + +typedef enum { + UART_9600 = 9600, + UART_14400 = 14400, + UART_19200 = 19200, + UART_38400 = 38400, + UART_57600 = 57600, + UART_115200 = 115200, + UART_230400 = 230400, + UART_460800 = 460800, + UART_921600 = 921600, +} uart_valid_baudrates; + typedef enum { - BUSIO_UART_PARITY_NONE, - BUSIO_UART_PARITY_EVEN, - BUSIO_UART_PARITY_ODD -} busio_uart_parity_t; + UART_FREE = 0, + UART_BUSY, + UART_NEVER_RESET, +} uart_status_t; + +static uint32_t timeout_ms=0; + +// Set each bit to indicate an active UART +// will be checked by ISR Handler for which ones to call +static uint8_t uarts_active = 0; +static uart_status_t uart_status[NUM_UARTS]; +// static uint8_t uart_never_reset_mask = 0; + +static int isValidBaudrate(uint32_t baudrate) { + switch(baudrate) { + case UART_9600: + return 1; + break; + case UART_14400: + return 1; + break; + case UART_19200: + return 1; + break; + case UART_38400: + return 1; + break; + case UART_57600: + return 1; + break; + case UART_115200: + return 1; + break; + case UART_230400: + return 1; + break; + case UART_460800: + return 1; + break; + case UART_921600: + return 1; + break; + default: + return 0; + break; + } +} + +static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) +{ + switch(busio_parity) { + case BUSIO_UART_PARITY_NONE: + return MXC_UART_PARITY_DISABLE; + case BUSIO_UART_PARITY_EVEN: + return MXC_UART_PARITY_EVEN_0; + case BUSIO_UART_PARITY_ODD: + return MXC_UART_PARITY_ODD_0; + default: + mp_raise_ValueError(MP_ERROR_TEXT("Parity must be ODD, EVEN, or NONE\n")); + } +} + +// FIXME: Find a better way of doing t his without a for loop +void UartISR(void) { + for (int i=0; i< NUM_UARTS; i++) { + if (uarts_active & (1 << i) ) { + MXC_UART_AsyncHandler(MXC_UART_GET_UART(i)); + uart_status[i] = UART_FREE; + } + } +} + +void uartCallback(mxc_uart_req_t *req, int error) { + +} // Construct an underlying UART object. -extern void common_hal_busio_uart_construct(busio_uart_obj_t *self, +void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts, const mcu_pin_obj_t *rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer, - bool sigint_enabled); + bool sigint_enabled) +{ + int err, temp; + + // Check for NULL Pointers && valid UART settings + assert( self ); + + // Assign UART ID based on pins + temp = pinsToUart(tx, rx); + if (temp == -1) { + // Error will be indicated by pinsToUart(tx, rx) function + return; + } + else { + self->uart_id = temp; + self->uart_regs = MXC_UART_GET_UART(temp); + } + + assert( (self->uart_id >= 0) && (self->uart_id < NUM_UARTS) ); + + // Indicate RS485 not implemented + if ( (rs485_dir != NULL) || (rs485_invert) ) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("RS485")); + } + + if ((rx != NULL) && (tx != NULL)) { + err = MXC_UART_Init(self->uart_regs, baudrate, MXC_UART_IBRO_CLK); + if (err != E_NO_ERROR) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to initialize UART.\n")); + } + + // attach & configure pins + self->tx_pin = tx; + self->rx_pin = rx; + common_hal_mcu_pin_claim(self->tx_pin); + common_hal_mcu_pin_claim(self->rx_pin); + } + else if (tx != NULL) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); + } + else if (rx != NULL) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); + } + else { + // Should not get here, as shared-bindings API should not call this way + } + + if ((cts) && (rts)) { + MXC_UART_SetFlowCtrl(self->uart_regs, MXC_UART_FLOW_EN, 8); + self->cts_pin = cts; + self->rts_pin = rts; + common_hal_mcu_pin_claim(self->cts_pin); + common_hal_mcu_pin_claim(self->rts_pin); + } + else if (cts || rts) { + mp_raise_ValueError(MP_ERROR_TEXT("Flow Ctrl needs both CTS & RTS")); + } + + // Set stop bits & data size + assert( (stop == 1) || (stop == 2) ); + mp_arg_validate_int(bits, 8, MP_QSTR_bits); + MXC_UART_SetDataSize(self->uart_regs, bits); + MXC_UART_SetStopBits(self->uart_regs, stop); + + // Set parity + MXC_UART_SetParity(self->uart_regs, convertParity(parity)); + + // attach UART parameters + self->stop_bits = stop; // must be 1 or 2 + self->bits = bits; + self->parity = parity; + self->baudrate = baudrate; + self->error = E_NO_ERROR; -extern void common_hal_busio_uart_deinit(busio_uart_obj_t *self); -extern bool common_hal_busio_uart_deinited(busio_uart_obj_t *self); + // Initialize ringbuffer for receiving data + if (self->rx_pin) { + // if given a ringbuff, use that + if (receiver_buffer) { + ringbuf_init(&self->ringbuf, receiver_buffer, receiver_buffer_size); + } + // else create one and attach it + else { + if (!ringbuf_alloc(&self->ringbuf, receiver_buffer_size)) { + m_malloc_fail(receiver_buffer_size); + } + } + } + // Indicate to this module that the UART is active + uarts_active |= (1 << self->uart_id); + + MXC_UART_ClearFlags(self->uart_regs, self->uart_regs->int_fl); + + /* Enable UART interrupt */ + NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(self->uart_id)); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + NVIC_SetPriority(MXC_UART_GET_IRQ(self->uart_id), UART_PRIORITY); + NVIC_SetVector(MXC_UART_GET_IRQ(self->uart_id), (uint32_t)UartISR); + + // FIXME: UART ISRs are NOT WORKING! + // MXC_UART_EnableInt(self->uart_regs, ???) + // NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + + return; +} + +void common_hal_busio_uart_deinit(busio_uart_obj_t *self) +{ + assert(self); + + if (!common_hal_busio_uart_deinited(self)) { + // First disable the ISR to avoid pre-emption + NVIC_DisableIRQ(UART0_IRQn); + + MXC_UART_Shutdown(self->uart_regs); + self->error = E_UNINITIALIZED; + + assert(self->rx_pin && self->tx_pin); + reset_pin_number(self->rx_pin->port, self->rx_pin->mask); + reset_pin_number(self->tx_pin->port, self->tx_pin->mask); + + if (self->cts_pin && self->rts_pin) { + reset_pin_number(self->cts_pin->port, self->cts_pin->mask); + reset_pin_number(self->rts_pin->port, self->rts_pin->mask); + } + + self->tx_pin = NULL; + self->rx_pin = NULL; + self->cts_pin = NULL; + self->rts_pin = NULL; + + // Indicate to this module that the UART is not active + uarts_active &= ~(1 << self->uart_id); + } +} + +bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) +{ + if (uarts_active & (1 << self->uart_id)) { + return false; + } + else { + return true; + }; +} + +// todo: test // Read characters. len is in characters NOT bytes! -extern size_t common_hal_busio_uart_read(busio_uart_obj_t *self, - uint8_t *data, size_t len, int *errcode); +size_t common_hal_busio_uart_read(busio_uart_obj_t *self, + uint8_t *data, size_t len, int *errcode) +{ + int err; + static size_t bytes_remaining; + + bytes_remaining = len * 4; + while(bytes_remaining > 0) { + mxc_uart_req_t uart_wr_req; + uart_wr_req.rxCnt = 0; + uart_wr_req.txCnt = 0; + uart_wr_req.rxData = data; + uart_wr_req.txData = NULL; + uart_wr_req.rxLen = (uint32_t)( (bytes_remaining >= 255) ? 255 : bytes_remaining ); + uart_wr_req.txLen = 0; + uart_wr_req.uart = self->uart_regs; + + err = MXC_UART_Transaction(&uart_wr_req); + if (err != E_NO_ERROR) { + *errcode = err; + return ((len * 4) - bytes_remaining); + } + bytes_remaining -= uart_wr_req.rxLen; + } + return len; +} + +//todo: test // Write characters. len is in characters NOT bytes! -extern size_t common_hal_busio_uart_write(busio_uart_obj_t *self, - const uint8_t *data, size_t len, int *errcode); + size_t common_hal_busio_uart_write(busio_uart_obj_t *self, + const uint8_t *data, size_t len, int *errcode) +{ + int err; + uint32_t start_time=0; + static size_t bytes_remaining; + + bytes_remaining = len * 4; + + while(bytes_remaining > 0) { + mxc_uart_req_t uart_wr_req; + uart_wr_req.rxCnt = 0; + uart_wr_req.txCnt = 0; + uart_wr_req.rxData = NULL; + uart_wr_req.txData = data; + uart_wr_req.txLen = (uint32_t)( (bytes_remaining >= 255) ? 255 : bytes_remaining ); + uart_wr_req.rxLen = 0; + uart_wr_req.uart = self->uart_regs; + + uart_status[self->uart_id] = UART_BUSY; + start_time = supervisor_ticks_ms64(); + err = MXC_UART_Transaction(&uart_wr_req); + if (err != E_NO_ERROR) { + *errcode = err; + return ((len * 4) - bytes_remaining); + } + + // FIXME: NOT ENTERING ISR, NOT HANDLING TIMEOUT + // Wait for transaction completion + // FIXME: Test timeout & status flags + while ( (supervisor_ticks_ms64() - start_time < 500) + && (uart_status[self->uart_id] != UART_FREE) + ) {}; + + bytes_remaining -= uart_wr_req.txLen; + } + return len; +} + + uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) +{ + return self->baudrate; +} + +// Validate baudrate +void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) +{ + if ( isValidBaudrate(baudrate) ) { + self->baudrate = baudrate; + } + else { + mp_raise_ValueError(MP_ERROR_TEXT("Baudrate invalid. Must be a standard UART baudrate.\n")); + } +} + +mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) +{ + return self->timeout; +} + +void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) +{ + if (timeout > 100.0) { + mp_raise_ValueError(MP_ERROR_TEXT("Timeout must be < 100 seconds")); + } + + timeout_ms = 1000 * (uint32_t)timeout; + self->timeout = (uint32_t)timeout; + + return; +} + +uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) +{ + return MXC_UART_GetRXFIFOAvailable(self->uart_regs); +} -extern uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self); -extern void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate); -extern mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self); -extern void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout); +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) +{ + MXC_UART_ClearRXFIFO(self->uart_regs); +} -extern uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self); -extern void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self); -extern bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self); +bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) +{ + return !(MXC_UART_GetStatus(self->uart_regs) & (MXC_F_UART_STATUS_TX_BUSY)); +} -extern void common_hal_busio_uart_never_reset(busio_uart_obj_t *self); +void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) +{ + common_hal_never_reset_pin(self->tx_pin); + common_hal_never_reset_pin(self->rx_pin); + common_hal_never_reset_pin(self->cts_pin); + common_hal_never_reset_pin(self->rts_pin); + // uart_never_reset_mask |= ( 1 << (self->uart_id) ); +} diff --git a/ports/analog/common-hal/busio/UART.h b/ports/analog/common-hal/busio/UART.h index a8d294de222d3..3c76efc720e78 100644 --- a/ports/analog/common-hal/busio/UART.h +++ b/ports/analog/common-hal/busio/UART.h @@ -4,28 +4,43 @@ // // SPDX-License-Identifier: MIT -#ifndef MICROPY_INCLUDED_EFR32_COMMON_HAL_BUSIO_UART_H -#define MICROPY_INCLUDED_EFR32_COMMON_HAL_BUSIO_UART_H +#ifndef MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H +#define MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H #include "common-hal/microcontroller/Pin.h" -#include "peripherals/periph.h" #include "py/obj.h" #include "py/ringbuf.h" -#include "uart.h" +// #include "uart.h" +// #include "uart_regs.h" +// #include "mxc_sys.h" + +#include "max32_port.h" // Define a struct for what BUSIO.UART should contain typedef struct { mp_obj_base_t base; + + int uart_id; + int uart_map; mxc_uart_regs_t* uart_regs; - const mcu_pin_obj_t *rx; - const mcu_pin_obj_t *tx; - uint32_t baudrate; + bool parity; - uint8_t stop_bits; uint8_t bits; + uint8_t stop_bits; + uint32_t baudrate; + + int error; + float timeout; + + ringbuf_t ringbuf; + + const mcu_pin_obj_t *rx_pin; + const mcu_pin_obj_t *tx_pin; + const mcu_pin_obj_t *rts_pin; + const mcu_pin_obj_t *cts_pin; } busio_uart_obj_t; void uart_reset(void); -#endif // MICROPY_INCLUDED_EFR32_COMMON_HAL_BUSIO_UART_H +#endif // MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H diff --git a/ports/analog/max32_port.h b/ports/analog/max32_port.h index 5d92fcfe6d3ee..5c3811b651554 100644 --- a/ports/analog/max32_port.h +++ b/ports/analog/max32_port.h @@ -22,31 +22,35 @@ #include "system_max32690.h" #include "max32690.h" +// UART Ports & pins +#include "peripherals/max32690/max32_uart.h" +#define NUM_UARTS 4 + /** START: GPIO4 Handling specific to MAX32690 */ -#define GPIO4_PIN_MASK 0x00000003 -#define GPIO4_RESET_MASK 0xFFFFFF77 -#define GPIO4_OUTEN_MASK(mask) \ - (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_OE_POS) | \ - ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_OE_POS - 1))) -#define GPIO4_PULLDIS_MASK(mask) \ - (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_PE_POS) | \ - ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_PE_POS - 1))) -#define GPIO4_DATAOUT_MASK(mask) \ - (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ - ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) -#define GPIO4_DATAOUT_GET_MASK(mask) \ - ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_DO) >> MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ - ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_DO) >> \ - (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) & \ - mask) -#define GPIO4_DATAIN_MASK(mask) \ - ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_IN) >> MXC_F_MCR_GPIO4_CTRL_P40_IN_POS) | \ - ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_IN) >> \ - (MXC_F_MCR_GPIO4_CTRL_P41_IN_POS - 1))) & \ - mask) -#define GPIO4_AFEN_MASK(mask) \ - (((mask & (1 << 0)) << MXC_F_MCR_OUTEN_PDOWN_OUT_EN_POS) | \ - ((mask & (1 << 1)) >> (MXC_F_MCR_OUTEN_SQWOUT_EN_POS + 1))) + #define GPIO4_PIN_MASK 0x00000003 + #define GPIO4_RESET_MASK 0xFFFFFF77 + #define GPIO4_OUTEN_MASK(mask) \ + (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_OE_POS) | \ + ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_OE_POS - 1))) + #define GPIO4_PULLDIS_MASK(mask) \ + (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_PE_POS) | \ + ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_PE_POS - 1))) + #define GPIO4_DATAOUT_MASK(mask) \ + (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ + ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) + #define GPIO4_DATAOUT_GET_MASK(mask) \ + ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_DO) >> MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ + ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_DO) >> \ + (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) & \ + mask) + #define GPIO4_DATAIN_MASK(mask) \ + ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_IN) >> MXC_F_MCR_GPIO4_CTRL_P40_IN_POS) | \ + ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_IN) >> \ + (MXC_F_MCR_GPIO4_CTRL_P41_IN_POS - 1))) & \ + mask) + #define GPIO4_AFEN_MASK(mask) \ + (((mask & (1 << 0)) << MXC_F_MCR_OUTEN_PDOWN_OUT_EN_POS) | \ + ((mask & (1 << 1)) >> (MXC_F_MCR_OUTEN_SQWOUT_EN_POS + 1))) /** END: GPIO4 Handling specific to MAX32690 */ #endif diff --git a/ports/analog/mpconfigport.mk b/ports/analog/mpconfigport.mk index 97ead8efdfb0f..f1cd0bb2901e9 100644 --- a/ports/analog/mpconfigport.mk +++ b/ports/analog/mpconfigport.mk @@ -22,8 +22,7 @@ INTERNAL_FLASH_FILESYSTEM = 1 #################################################################################### # These modules are implemented in ports//common-hal: -# Plan to implement -CIRCUITPY_BUSIO ?= 0 +CIRCUITPY_BUSIO ?= 1 CIRCUITPY_RTC ?= 1 # Other modules (may or may not implement): diff --git a/ports/analog/peripherals/max32690/max32_uart.c b/ports/analog/peripherals/max32690/max32_uart.c new file mode 100644 index 0000000000000..f8c53dc63218e --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_uart.c @@ -0,0 +1,47 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/UART.h" +#include "max32_uart.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + +// FIXME: Remove upon test! +// mxc_uart_regs_t max32_uarts[NUM_UARTS] = { +// MXC_UART0, +// MXC_UART1, +// MXC_UART2, +// MXC_UART3, +// }; + +const mxc_gpio_cfg_t uart_maps[NUM_UARTS] = { + { MXC_GPIO2, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO2, (MXC_GPIO_PIN_14 | MXC_GPIO_PIN_16), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO1, (MXC_GPIO_PIN_9 | MXC_GPIO_PIN_10), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_WEAK_PULL_UP, + MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO3, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1), + MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_WEAK_PULL_UP, + MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 } +}; + +int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx) { + for (int i = 0; i < NUM_UARTS; i++) { + if ( (uart_maps[i].port == (MXC_GPIO_GET_GPIO(tx->port))) + && (uart_maps[i].mask == (( tx->mask) | (rx->mask))) ) { + return i; + } + } + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("ERR: Unable to find a uart matching pins...\nTX: port %d mask %d\nRX: port %d mask %d\n"), + tx->port, tx->mask, rx->port, rx->mask); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_uart.h b/ports/analog/peripherals/max32690/max32_uart.h new file mode 100644 index 0000000000000..f03b500a3c507 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_uart.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "uart_regs.h" +#include "mxc_sys.h" +#include "uart.h" +#include "peripherals/pins.h" + +#define NUM_UARTS 4 + +int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx); From 451e152d5b3d785b7e81a2fb575f4d22c33217c3 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Tue, 14 Jan 2025 15:26:49 -0800 Subject: [PATCH 115/517] Add tools files for debugging - Add debug-dap.sh to spawn OpenOCD process & gdb-multiarch process for debugging. Requires MaximSDK install & path settings inside this script. Also depends on gdb-multiarch and connected CMSIS-DAP probe over USB. - debug-dap.gdb simply holds the gdb commands for this script + small style fix on common-hal/microcontroller/Pin.c --- ports/analog/common-hal/microcontroller/Pin.c | 2 +- ports/analog/tools/debug-dap.gdb | 3 +++ ports/analog/tools/debug-dap.sh | 23 +++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 ports/analog/tools/debug-dap.gdb create mode 100644 ports/analog/tools/debug-dap.sh diff --git a/ports/analog/common-hal/microcontroller/Pin.c b/ports/analog/common-hal/microcontroller/Pin.c index 4545aa039c2fa..6816054153e72 100644 --- a/ports/analog/common-hal/microcontroller/Pin.c +++ b/ports/analog/common-hal/microcontroller/Pin.c @@ -37,7 +37,7 @@ void reset_all_pins(void) { } void reset_pin_number(uint8_t pin_port, uint8_t pin_pad) { - if (pin_port == INVALID_PIN || pin_port > NUM_GPIO_PORTS) { + if ( (pin_port == INVALID_PIN) || (pin_port > NUM_GPIO_PORTS) ) { return; } diff --git a/ports/analog/tools/debug-dap.gdb b/ports/analog/tools/debug-dap.gdb new file mode 100644 index 0000000000000..d0aa7e2b13fa0 --- /dev/null +++ b/ports/analog/tools/debug-dap.gdb @@ -0,0 +1,3 @@ +target remote :3333 +break main +continue diff --git a/ports/analog/tools/debug-dap.sh b/ports/analog/tools/debug-dap.sh new file mode 100644 index 0000000000000..df2964fcb122a --- /dev/null +++ b/ports/analog/tools/debug-dap.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Primer on taking cmd line args in Linux shell scripts +# $0 is the script itself +# $1 $2 $3 are arg1, arg2, arg3, etc + +# Export OCD Path +### USER ACTION: Replace with your path to OpenOCD ### +OCD_PATH="~/MaximSDK/Tools/OpenOCD" + +# Call openocd to setup the debug server, storing the PID for OpenOCD +sh -c "openocd -s $OCD_PATH/scripts -f interface/cmsis-dap.cfg -f target/$1.cfg -c \"init; reset halt\" " & + +# Allow enough time for OCD server to set up +# + wait for the sleep to finish +sleep 3 & +wait $! + +# spawn the gdb process and store the gdb_pid +gdb-multiarch build-apard32690/firmware.elf -x "tools/debug-dap.gdb" + +# when gdb exits, kill all openocd processes +killall openocd From 13fb8f3d8f6d2d523f760c036a0d7cadcac2b312 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Fri, 24 Jan 2025 13:42:42 -0800 Subject: [PATCH 116/517] Fixed most UART write issues. - Graceful recovery from timeouts added - Remaining issue still exists where a timeout occurs after a while --- ports/analog/common-hal/busio/UART.c | 86 +++++++++++++++++----------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index ddffc8f55b9c3..4b0c8bfba6a81 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#if CIRCUITPY_BUSIO_UART + #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "supervisor/shared/tick.h" @@ -144,18 +146,21 @@ static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) } } -// FIXME: Find a better way of doing t his without a for loop -void UartISR(void) { - for (int i=0; i< NUM_UARTS; i++) { +// FIXME: Find a better way of doing this without a for loop +// FIXME: Fixed @ UART0 for TESTING BUGFIXES!!! +void UART0_IRQHandler(void) { + // MXC_UART_AsyncHandler(MXC_UART_GET_UART(0)); + for (int i=0; i < NUM_UARTS; i++) { if (uarts_active & (1 << i) ) { MXC_UART_AsyncHandler(MXC_UART_GET_UART(i)); - uart_status[i] = UART_FREE; } } } -void uartCallback(mxc_uart_req_t *req, int error) { - +// Callback gets called when AsyncRequest is COMPLETE +// (e.g. txLen == txCnt) +static volatile void uartCallback(mxc_uart_req_t *req, int error) { + uart_status[MXC_UART_GET_IDX(req->uart)] = UART_FREE; } // Construct an underlying UART object. @@ -256,17 +261,12 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // Indicate to this module that the UART is active uarts_active |= (1 << self->uart_id); - MXC_UART_ClearFlags(self->uart_regs, self->uart_regs->int_fl); /* Enable UART interrupt */ NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_SetPriority(MXC_UART_GET_IRQ(self->uart_id), UART_PRIORITY); - NVIC_SetVector(MXC_UART_GET_IRQ(self->uart_id), (uint32_t)UartISR); - - // FIXME: UART ISRs are NOT WORKING! - // MXC_UART_EnableInt(self->uart_regs, ???) - // NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + NVIC_SetVector(MXC_UART_GET_IRQ(self->uart_id), (uint32_t)&UART0_IRQHandler); return; } @@ -321,23 +321,30 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, bytes_remaining = len * 4; - while(bytes_remaining > 0) { - mxc_uart_req_t uart_wr_req; - uart_wr_req.rxCnt = 0; - uart_wr_req.txCnt = 0; - uart_wr_req.rxData = data; - uart_wr_req.txData = NULL; - uart_wr_req.rxLen = (uint32_t)( (bytes_remaining >= 255) ? 255 : bytes_remaining ); - uart_wr_req.txLen = 0; - uart_wr_req.uart = self->uart_regs; + MXC_UART_ClearFlags(self->uart_regs, 0xFFFFFFFF); + NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - err = MXC_UART_Transaction(&uart_wr_req); + while(bytes_remaining > 0) { + mxc_uart_req_t uart_rd_req; + uart_rd_req.rxCnt = 0; + uart_rd_req.txCnt = 0; + uart_rd_req.rxData = data; + uart_rd_req.txData = NULL; + uart_rd_req.rxLen = (uint32_t)( (bytes_remaining >= 255) ? 255 : bytes_remaining ); + uart_rd_req.txLen = 0; + uart_rd_req.uart = self->uart_regs; + uart_rd_req.callback = (void *)uartCallback; + + err = MXC_UART_TransactionAsync(&uart_rd_req); if (err != E_NO_ERROR) { *errcode = err; + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); return ((len * 4) - bytes_remaining); } - bytes_remaining -= uart_wr_req.rxLen; + bytes_remaining -= uart_rd_req.rxLen; } + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + return len; } @@ -350,7 +357,10 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint32_t start_time=0; static size_t bytes_remaining; - bytes_remaining = len * 4; + bytes_remaining = len; + + MXC_UART_ClearFlags(self->uart_regs, 0xFFFFFFFF); + NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); while(bytes_remaining > 0) { mxc_uart_req_t uart_wr_req; @@ -358,28 +368,38 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uart_wr_req.txCnt = 0; uart_wr_req.rxData = NULL; uart_wr_req.txData = data; - uart_wr_req.txLen = (uint32_t)( (bytes_remaining >= 255) ? 255 : bytes_remaining ); + uart_wr_req.txLen = bytes_remaining; uart_wr_req.rxLen = 0; uart_wr_req.uart = self->uart_regs; + uart_wr_req.callback = (void *)uartCallback; uart_status[self->uart_id] = UART_BUSY; start_time = supervisor_ticks_ms64(); - err = MXC_UART_Transaction(&uart_wr_req); + err = MXC_UART_TransactionAsync(&uart_wr_req); if (err != E_NO_ERROR) { *errcode = err; - return ((len * 4) - bytes_remaining); + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Requested bus is busy\n")); } - // FIXME: NOT ENTERING ISR, NOT HANDLING TIMEOUT // Wait for transaction completion - // FIXME: Test timeout & status flags - while ( (supervisor_ticks_ms64() - start_time < 500) - && (uart_status[self->uart_id] != UART_FREE) + while ( (uart_status[self->uart_id] != UART_FREE) && + (supervisor_ticks_ms64() - start_time < (self->timeout * 1000)) ) {}; - bytes_remaining -= uart_wr_req.txLen; + // If the timeout gets hit, abort and error out + if (uart_status[self->uart_id] != UART_FREE) { + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Uart transaction timed out.\n")); + } + + bytes_remaining -= uart_wr_req.txCnt; } + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); return len; + } uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) @@ -438,3 +458,5 @@ void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) common_hal_never_reset_pin(self->rts_pin); // uart_never_reset_mask |= ( 1 << (self->uart_id) ); } + +#endif // CIRCUITPY_BUSIO_UART From 0f1d915f7c3521d877ce589c01e465c4b48be781 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Fri, 24 Jan 2025 17:08:32 -0800 Subject: [PATCH 117/517] Fix issues with UART reads and unreliable UART writes. - No ISR for UART writes, but uses timeouts - Reads use ISRs, but also incorporate timeouts --- ports/analog/common-hal/busio/UART.c | 162 ++++++++++++++++----------- 1 file changed, 98 insertions(+), 64 deletions(-) diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index 4b0c8bfba6a81..ad5425d62ea31 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -95,6 +95,7 @@ static uint32_t timeout_ms=0; // will be checked by ISR Handler for which ones to call static uint8_t uarts_active = 0; static uart_status_t uart_status[NUM_UARTS]; +static volatile int uart_err; // static uint8_t uart_never_reset_mask = 0; static int isValidBaudrate(uint32_t baudrate) { @@ -146,12 +147,9 @@ static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) } } -// FIXME: Find a better way of doing this without a for loop -// FIXME: Fixed @ UART0 for TESTING BUGFIXES!!! void UART0_IRQHandler(void) { - // MXC_UART_AsyncHandler(MXC_UART_GET_UART(0)); - for (int i=0; i < NUM_UARTS; i++) { - if (uarts_active & (1 << i) ) { + for (int i = 0; i < NUM_UARTS; i++) { + if (uarts_active & (1 << i)) { MXC_UART_AsyncHandler(MXC_UART_GET_UART(i)); } } @@ -161,6 +159,7 @@ void UART0_IRQHandler(void) { // (e.g. txLen == txCnt) static volatile void uartCallback(mxc_uart_req_t *req, int error) { uart_status[MXC_UART_GET_IDX(req->uart)] = UART_FREE; + uart_err = error; } // Construct an underlying UART object. @@ -262,7 +261,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, uarts_active |= (1 << self->uart_id); - /* Enable UART interrupt */ + /* Setup UART interrupt */ NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_SetPriority(MXC_UART_GET_IRQ(self->uart_id), UART_PRIORITY); @@ -311,45 +310,67 @@ bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) }; } -// todo: test // Read characters. len is in characters NOT bytes! size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) { int err; + uint32_t start_time=0; static size_t bytes_remaining; - bytes_remaining = len * 4; + // Setup globals & status tracking + uart_err = E_NO_ERROR; + uarts_active |= (1 << self->uart_id); + uart_status[self->uart_id] = UART_BUSY; + bytes_remaining = len; - MXC_UART_ClearFlags(self->uart_regs, 0xFFFFFFFF); NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - while(bytes_remaining > 0) { - mxc_uart_req_t uart_rd_req; - uart_rd_req.rxCnt = 0; - uart_rd_req.txCnt = 0; - uart_rd_req.rxData = data; - uart_rd_req.txData = NULL; - uart_rd_req.rxLen = (uint32_t)( (bytes_remaining >= 255) ? 255 : bytes_remaining ); - uart_rd_req.txLen = 0; - uart_rd_req.uart = self->uart_regs; - uart_rd_req.callback = (void *)uartCallback; - - err = MXC_UART_TransactionAsync(&uart_rd_req); - if (err != E_NO_ERROR) { - *errcode = err; - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - return ((len * 4) - bytes_remaining); - } - bytes_remaining -= uart_rd_req.rxLen; + mxc_uart_req_t uart_rd_req; + uart_rd_req.rxCnt = 0; + uart_rd_req.txCnt = 0; + uart_rd_req.rxData = data; + uart_rd_req.txData = NULL; + uart_rd_req.rxLen = bytes_remaining; + uart_rd_req.txLen = 0; + uart_rd_req.uart = self->uart_regs; + uart_rd_req.callback = (void *)uartCallback; + + // Initiate the read transaction + start_time = supervisor_ticks_ms64(); + err = MXC_UART_TransactionAsync(&uart_rd_req); + if (err != E_NO_ERROR) { + *errcode = err; + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("\nERR: Error starting trasaction: %d\n"), err); } - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + // Wait for transaction completion or timeout + while ( (uart_status[self->uart_id] != UART_FREE) && + (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { + } + + // If the timeout gets hit, abort and error out + if (uart_status[self->uart_id] != UART_FREE) { + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_RuntimeError(MP_ERROR_TEXT("\nERR: Uart transaction timed out.\n")); + } + + // Check for errors from the callback + else if (uart_err != E_NO_ERROR) { + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("MAX32 ERR: %d\n"), uart_err); + } + + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); return len; } -//todo: test // Write characters. len is in characters NOT bytes! +// This function blocks until the timeout finishes size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { @@ -357,49 +378,62 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint32_t start_time=0; static size_t bytes_remaining; + // Setup globals & status tracking + uart_err = E_NO_ERROR; + uarts_active |= (1 << self->uart_id); + uart_status[self->uart_id] = UART_BUSY; bytes_remaining = len; - MXC_UART_ClearFlags(self->uart_regs, 0xFFFFFFFF); - NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - - while(bytes_remaining > 0) { - mxc_uart_req_t uart_wr_req; - uart_wr_req.rxCnt = 0; - uart_wr_req.txCnt = 0; - uart_wr_req.rxData = NULL; - uart_wr_req.txData = data; - uart_wr_req.txLen = bytes_remaining; - uart_wr_req.rxLen = 0; - uart_wr_req.uart = self->uart_regs; - uart_wr_req.callback = (void *)uartCallback; - - uart_status[self->uart_id] = UART_BUSY; - start_time = supervisor_ticks_ms64(); - err = MXC_UART_TransactionAsync(&uart_wr_req); - if (err != E_NO_ERROR) { - *errcode = err; - MXC_UART_AbortAsync(self->uart_regs); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Requested bus is busy\n")); - } + mxc_uart_req_t uart_wr_req = {}; + + // Setup transaction + uart_wr_req.rxCnt = 0; + uart_wr_req.txCnt = 0; + uart_wr_req.rxData = NULL; + uart_wr_req.txData = data; + uart_wr_req.txLen = bytes_remaining; + uart_wr_req.rxLen = 0; + uart_wr_req.uart = self->uart_regs; + uart_wr_req.callback = (void *)uartCallback; + + // Start the transaction + start_time = supervisor_ticks_ms64(); + err = MXC_UART_TransactionAsync(&uart_wr_req); + if (err != E_NO_ERROR) { + *errcode = err; + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Requested bus is busy\n")); + } - // Wait for transaction completion - while ( (uart_status[self->uart_id] != UART_FREE) && - (supervisor_ticks_ms64() - start_time < (self->timeout * 1000)) - ) {}; + // Wait for transaction completion or timeout + while ( (uart_status[self->uart_id] != UART_FREE) && + (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { - // If the timeout gets hit, abort and error out - if (uart_status[self->uart_id] != UART_FREE) { - MXC_UART_AbortAsync(self->uart_regs); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Uart transaction timed out.\n")); + // Call the handler and abort if errors + uart_err = MXC_UART_AsyncHandler(MXC_UART_GET_UART(0)); + if (uart_err != E_NO_ERROR) { + MXC_UART_AbortAsync(MXC_UART_GET_UART(0)); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(0)); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("MAX32 ERR: %d\n"), uart_err); } + } - bytes_remaining -= uart_wr_req.txCnt; + // If the timeout gets hit, abort and error out + if (uart_status[self->uart_id] != UART_FREE) { + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Uart transaction timed out.\n")); } - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - return len; + // Check for errors from the callback + else if (uart_err != E_NO_ERROR) { + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("MAX32 ERR: %d\n"), uart_err); + } + + return len; } uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) From 72c4d438e19b9b76f4e68b06194dd8cc92ae4f88 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Mon, 27 Jan 2025 13:50:52 -0800 Subject: [PATCH 118/517] Add pre-commit changes to UART - Formatting & codespell fixes from pre-commit --- ports/analog/common-hal/busio/I2C.h | 2 +- ports/analog/common-hal/busio/SPI.h | 2 +- ports/analog/common-hal/busio/UART.c | 115 +++++++----------- ports/analog/common-hal/busio/UART.h | 2 +- ports/analog/common-hal/microcontroller/Pin.c | 2 +- ports/analog/max32_port.h | 32 ++--- .../analog/peripherals/max32690/max32_uart.c | 16 +-- 7 files changed, 74 insertions(+), 97 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.h b/ports/analog/common-hal/busio/I2C.h index c92283d6c53b6..9571a8ab694f4 100644 --- a/ports/analog/common-hal/busio/I2C.h +++ b/ports/analog/common-hal/busio/I2C.h @@ -17,7 +17,7 @@ // Define a struct for what BUSIO.I2C should carry typedef struct { mp_obj_base_t base; - mxc_i2c_regs_t* i2c_regs; + mxc_i2c_regs_t *i2c_regs; bool has_lock; const mcu_pin_obj_t *scl; const mcu_pin_obj_t *sda; diff --git a/ports/analog/common-hal/busio/SPI.h b/ports/analog/common-hal/busio/SPI.h index 8c48147ff6c77..d90f55da111a0 100644 --- a/ports/analog/common-hal/busio/SPI.h +++ b/ports/analog/common-hal/busio/SPI.h @@ -17,7 +17,7 @@ // Define a struct for what BUSIO.SPI should carry typedef struct { mp_obj_base_t base; - mxc_spi_regs_t* spi_regs; + mxc_spi_regs_t *spi_regs; bool has_lock; const mcu_pin_obj_t *sck; const mcu_pin_obj_t *mosi; diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index ad5425d62ea31..b4b17126e60e9 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -89,7 +89,7 @@ typedef enum { UART_NEVER_RESET, } uart_status_t; -static uint32_t timeout_ms=0; +static uint32_t timeout_ms = 0; // Set each bit to indicate an active UART // will be checked by ISR Handler for which ones to call @@ -99,7 +99,7 @@ static volatile int uart_err; // static uint8_t uart_never_reset_mask = 0; static int isValidBaudrate(uint32_t baudrate) { - switch(baudrate) { + switch (baudrate) { case UART_9600: return 1; break; @@ -133,17 +133,16 @@ static int isValidBaudrate(uint32_t baudrate) { } } -static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) -{ - switch(busio_parity) { - case BUSIO_UART_PARITY_NONE: - return MXC_UART_PARITY_DISABLE; - case BUSIO_UART_PARITY_EVEN: - return MXC_UART_PARITY_EVEN_0; - case BUSIO_UART_PARITY_ODD: - return MXC_UART_PARITY_ODD_0; - default: - mp_raise_ValueError(MP_ERROR_TEXT("Parity must be ODD, EVEN, or NONE\n")); +static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) { + switch (busio_parity) { + case BUSIO_UART_PARITY_NONE: + return MXC_UART_PARITY_DISABLE; + case BUSIO_UART_PARITY_EVEN: + return MXC_UART_PARITY_EVEN_0; + case BUSIO_UART_PARITY_ODD: + return MXC_UART_PARITY_ODD_0; + default: + mp_raise_ValueError(MP_ERROR_TEXT("Parity must be ODD, EVEN, or NONE\n")); } } @@ -169,33 +168,31 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t *rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer, - bool sigint_enabled) -{ + bool sigint_enabled) { int err, temp; // Check for NULL Pointers && valid UART settings - assert( self ); + assert(self); // Assign UART ID based on pins temp = pinsToUart(tx, rx); if (temp == -1) { // Error will be indicated by pinsToUart(tx, rx) function return; - } - else { + } else { self->uart_id = temp; self->uart_regs = MXC_UART_GET_UART(temp); } - assert( (self->uart_id >= 0) && (self->uart_id < NUM_UARTS) ); + assert((self->uart_id >= 0) && (self->uart_id < NUM_UARTS)); // Indicate RS485 not implemented - if ( (rs485_dir != NULL) || (rs485_invert) ) { + if ((rs485_dir != NULL) || (rs485_invert)) { mp_raise_NotImplementedError(MP_ERROR_TEXT("RS485")); } if ((rx != NULL) && (tx != NULL)) { - err = MXC_UART_Init(self->uart_regs, baudrate, MXC_UART_IBRO_CLK); + err = MXC_UART_Init(self->uart_regs, baudrate, MXC_UART_IBRO_CLK); if (err != E_NO_ERROR) { mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to initialize UART.\n")); } @@ -205,14 +202,11 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->rx_pin = rx; common_hal_mcu_pin_claim(self->tx_pin); common_hal_mcu_pin_claim(self->rx_pin); - } - else if (tx != NULL) { + } else if (tx != NULL) { mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); - } - else if (rx != NULL) { + } else if (rx != NULL) { mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); - } - else { + } else { // Should not get here, as shared-bindings API should not call this way } @@ -222,13 +216,12 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->rts_pin = rts; common_hal_mcu_pin_claim(self->cts_pin); common_hal_mcu_pin_claim(self->rts_pin); - } - else if (cts || rts) { + } else if (cts || rts) { mp_raise_ValueError(MP_ERROR_TEXT("Flow Ctrl needs both CTS & RTS")); } // Set stop bits & data size - assert( (stop == 1) || (stop == 2) ); + assert((stop == 1) || (stop == 2)); mp_arg_validate_int(bits, 8, MP_QSTR_bits); MXC_UART_SetDataSize(self->uart_regs, bits); MXC_UART_SetStopBits(self->uart_regs, stop); @@ -270,8 +263,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, return; } -void common_hal_busio_uart_deinit(busio_uart_obj_t *self) -{ +void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { assert(self); if (!common_hal_busio_uart_deinited(self)) { @@ -300,22 +292,19 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) } } -bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) -{ +bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { if (uarts_active & (1 << self->uart_id)) { return false; - } - else { + } else { return true; }; } // Read characters. len is in characters NOT bytes! size_t common_hal_busio_uart_read(busio_uart_obj_t *self, - uint8_t *data, size_t len, int *errcode) -{ + uint8_t *data, size_t len, int *errcode) { int err; - uint32_t start_time=0; + uint32_t start_time = 0; static size_t bytes_remaining; // Setup globals & status tracking @@ -343,12 +332,12 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, *errcode = err; MXC_UART_AbortAsync(self->uart_regs); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("\nERR: Error starting trasaction: %d\n"), err); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("\nERR: Error starting transaction: %d\n"), err); } // Wait for transaction completion or timeout - while ( (uart_status[self->uart_id] != UART_FREE) && - (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { + while ((uart_status[self->uart_id] != UART_FREE) && + (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { } // If the timeout gets hit, abort and error out @@ -357,7 +346,6 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); mp_raise_RuntimeError(MP_ERROR_TEXT("\nERR: Uart transaction timed out.\n")); } - // Check for errors from the callback else if (uart_err != E_NO_ERROR) { MXC_UART_AbortAsync(self->uart_regs); @@ -371,11 +359,10 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, // Write characters. len is in characters NOT bytes! // This function blocks until the timeout finishes - size_t common_hal_busio_uart_write(busio_uart_obj_t *self, - const uint8_t *data, size_t len, int *errcode) -{ +size_t common_hal_busio_uart_write(busio_uart_obj_t *self, + const uint8_t *data, size_t len, int *errcode) { int err; - uint32_t start_time=0; + uint32_t start_time = 0; static size_t bytes_remaining; // Setup globals & status tracking @@ -407,8 +394,8 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, } // Wait for transaction completion or timeout - while ( (uart_status[self->uart_id] != UART_FREE) && - (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { + while ((uart_status[self->uart_id] != UART_FREE) && + (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { // Call the handler and abort if errors uart_err = MXC_UART_AsyncHandler(MXC_UART_GET_UART(0)); @@ -425,7 +412,6 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Uart transaction timed out.\n")); } - // Check for errors from the callback else if (uart_err != E_NO_ERROR) { MXC_UART_AbortAsync(self->uart_regs); @@ -436,29 +422,24 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, return len; } - uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) -{ +uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { return self->baudrate; } // Validate baudrate -void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) -{ - if ( isValidBaudrate(baudrate) ) { +void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { + if (isValidBaudrate(baudrate)) { self->baudrate = baudrate; - } - else { + } else { mp_raise_ValueError(MP_ERROR_TEXT("Baudrate invalid. Must be a standard UART baudrate.\n")); } } -mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) -{ +mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) { return self->timeout; } -void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) -{ +void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) { if (timeout > 100.0) { mp_raise_ValueError(MP_ERROR_TEXT("Timeout must be < 100 seconds")); } @@ -469,23 +450,19 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou return; } -uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) -{ +uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { return MXC_UART_GetRXFIFOAvailable(self->uart_regs); } -void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) -{ +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { MXC_UART_ClearRXFIFO(self->uart_regs); } -bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) -{ +bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { return !(MXC_UART_GetStatus(self->uart_regs) & (MXC_F_UART_STATUS_TX_BUSY)); } -void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) -{ +void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { common_hal_never_reset_pin(self->tx_pin); common_hal_never_reset_pin(self->rx_pin); common_hal_never_reset_pin(self->cts_pin); diff --git a/ports/analog/common-hal/busio/UART.h b/ports/analog/common-hal/busio/UART.h index 3c76efc720e78..f24f310fe3a52 100644 --- a/ports/analog/common-hal/busio/UART.h +++ b/ports/analog/common-hal/busio/UART.h @@ -23,7 +23,7 @@ typedef struct { int uart_id; int uart_map; - mxc_uart_regs_t* uart_regs; + mxc_uart_regs_t *uart_regs; bool parity; uint8_t bits; diff --git a/ports/analog/common-hal/microcontroller/Pin.c b/ports/analog/common-hal/microcontroller/Pin.c index 6816054153e72..83e2f3b9c3a76 100644 --- a/ports/analog/common-hal/microcontroller/Pin.c +++ b/ports/analog/common-hal/microcontroller/Pin.c @@ -37,7 +37,7 @@ void reset_all_pins(void) { } void reset_pin_number(uint8_t pin_port, uint8_t pin_pad) { - if ( (pin_port == INVALID_PIN) || (pin_port > NUM_GPIO_PORTS) ) { + if ((pin_port == INVALID_PIN) || (pin_port > NUM_GPIO_PORTS)) { return; } diff --git a/ports/analog/max32_port.h b/ports/analog/max32_port.h index 5c3811b651554..3b4fe7bc87d90 100644 --- a/ports/analog/max32_port.h +++ b/ports/analog/max32_port.h @@ -30,27 +30,27 @@ #define GPIO4_PIN_MASK 0x00000003 #define GPIO4_RESET_MASK 0xFFFFFF77 #define GPIO4_OUTEN_MASK(mask) \ - (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_OE_POS) | \ - ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_OE_POS - 1))) + (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_OE_POS) | \ + ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_OE_POS - 1))) #define GPIO4_PULLDIS_MASK(mask) \ - (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_PE_POS) | \ - ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_PE_POS - 1))) + (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_PE_POS) | \ + ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_PE_POS - 1))) #define GPIO4_DATAOUT_MASK(mask) \ - (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ - ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) + (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ + ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) #define GPIO4_DATAOUT_GET_MASK(mask) \ - ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_DO) >> MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ - ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_DO) >> \ - (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) & \ - mask) + ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_DO) >> MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ + ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_DO) >> \ + (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) & \ + mask) #define GPIO4_DATAIN_MASK(mask) \ - ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_IN) >> MXC_F_MCR_GPIO4_CTRL_P40_IN_POS) | \ - ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_IN) >> \ - (MXC_F_MCR_GPIO4_CTRL_P41_IN_POS - 1))) & \ - mask) + ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_IN) >> MXC_F_MCR_GPIO4_CTRL_P40_IN_POS) | \ + ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_IN) >> \ + (MXC_F_MCR_GPIO4_CTRL_P41_IN_POS - 1))) & \ + mask) #define GPIO4_AFEN_MASK(mask) \ - (((mask & (1 << 0)) << MXC_F_MCR_OUTEN_PDOWN_OUT_EN_POS) | \ - ((mask & (1 << 1)) >> (MXC_F_MCR_OUTEN_SQWOUT_EN_POS + 1))) + (((mask & (1 << 0)) << MXC_F_MCR_OUTEN_PDOWN_OUT_EN_POS) | \ + ((mask & (1 << 1)) >> (MXC_F_MCR_OUTEN_SQWOUT_EN_POS + 1))) /** END: GPIO4 Handling specific to MAX32690 */ #endif diff --git a/ports/analog/peripherals/max32690/max32_uart.c b/ports/analog/peripherals/max32690/max32_uart.c index f8c53dc63218e..3ccb930d85c14 100644 --- a/ports/analog/peripherals/max32690/max32_uart.c +++ b/ports/analog/peripherals/max32690/max32_uart.c @@ -23,21 +23,21 @@ const mxc_gpio_cfg_t uart_maps[NUM_UARTS] = { { MXC_GPIO2, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, - MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, { MXC_GPIO2, (MXC_GPIO_PIN_14 | MXC_GPIO_PIN_16), MXC_GPIO_FUNC_ALT1, - MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, { MXC_GPIO1, (MXC_GPIO_PIN_9 | MXC_GPIO_PIN_10), - MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_WEAK_PULL_UP, - MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_WEAK_PULL_UP, + MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, { MXC_GPIO3, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1), - MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_WEAK_PULL_UP, - MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 } + MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_WEAK_PULL_UP, + MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 } }; int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx) { for (int i = 0; i < NUM_UARTS; i++) { - if ( (uart_maps[i].port == (MXC_GPIO_GET_GPIO(tx->port))) - && (uart_maps[i].mask == (( tx->mask) | (rx->mask))) ) { + if ((uart_maps[i].port == (MXC_GPIO_GET_GPIO(tx->port))) + && (uart_maps[i].mask == ((tx->mask) | (rx->mask)))) { return i; } } From b741b3a7519011d6f4619a85b18b433bea511292 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Fri, 31 Jan 2025 16:45:13 -0800 Subject: [PATCH 119/517] Resolve UART ISR naming issues - Call NVIC_SetRAM in supervisor/port.c to move NVIC table to RAM, enabling ISR RAM remapping. - Rename UART ISR to be generic for any UART IRQn. - Remove background led writes from background.c. --- ports/analog/background.c | 10 ++-------- ports/analog/common-hal/busio/UART.c | 11 +++++++++-- ports/analog/supervisor/port.c | 8 ++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/ports/analog/background.c b/ports/analog/background.c index ffad007ffa51c..ad840c6dcdabc 100644 --- a/ports/analog/background.c +++ b/ports/analog/background.c @@ -19,19 +19,13 @@ extern const mxc_gpio_cfg_t led_pin[]; extern const int num_leds; /** NOTE: ALL "ticks" refer to a 1/1024 s period */ -static int status_led_ticks = 0; +static int status_ticks = 0; // This function is where port-specific background // tasks should be performed // Execute port specific actions during background tick. Only if ticks are enabled. void port_background_tick(void) { - status_led_ticks++; - - // Set an LED approx. 1/s - if (status_led_ticks > 1024) { - MXC_GPIO_OutToggle(led_pin[2].port, led_pin[2].mask); - status_led_ticks = 0; - } + status_ticks++; } // Execute port specific actions during background tasks. This is before the diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index b4b17126e60e9..310965e34609b 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -146,7 +146,7 @@ static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) { } } -void UART0_IRQHandler(void) { +void uart_isr(void) { for (int i = 0; i < NUM_UARTS; i++) { if (uarts_active & (1 << i)) { MXC_UART_AsyncHandler(MXC_UART_GET_UART(i)); @@ -237,6 +237,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->error = E_NO_ERROR; // Initialize ringbuffer for receiving data + // FIXME: Either the use the ringbuf or get rid of it! if (self->rx_pin) { // if given a ringbuff, use that if (receiver_buffer) { @@ -253,12 +254,18 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // Indicate to this module that the UART is active uarts_active |= (1 << self->uart_id); + // Set the timeout to a default value + if (((timeout < 0.0) || (timeout > 100.0))) { + self->timeout = 1.0; + } else { + self->timeout = timeout; + } /* Setup UART interrupt */ NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_SetPriority(MXC_UART_GET_IRQ(self->uart_id), UART_PRIORITY); - NVIC_SetVector(MXC_UART_GET_IRQ(self->uart_id), (uint32_t)&UART0_IRQHandler); + NVIC_SetVector(MXC_UART_GET_IRQ(self->uart_id), (uint32_t)uart_isr); return; } diff --git a/ports/analog/supervisor/port.c b/ports/analog/supervisor/port.c index ff05ff2d28709..de83b011bdf0d 100644 --- a/ports/analog/supervisor/port.c +++ b/ports/analog/supervisor/port.c @@ -39,6 +39,7 @@ // Sys includes #include "max32_port.h" +#include "nvic_table.h" // Timers #include "mxc_delay.h" @@ -78,6 +79,13 @@ void SysTick_Handler(void) { safe_mode_t port_init(void) { int err = E_NO_ERROR; + // Set Vector Table to RAM & configure ARM core to use RAM-based ISRs + // This allows definition of ISRs with custom names + // + // Useful for mapping ISRs with names not related to a specific IRQn. + // Source: https://arm-software.github.io/CMSIS_5/Core/html/using_VTOR_pg.html + NVIC_SetRAM(); + // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); NVIC_EnableIRQ(SysTick_IRQn); From aaeee4db835e9575ef5fc683f73d6a82c4552a02 Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Thu, 6 Mar 2025 23:57:59 -0800 Subject: [PATCH 120/517] Add BUSIO.I2C and MAX32690 I2C structure - Data structure and constructor for I2C (tested) - Lock and probing functions complete and tested TODO: Implement I2C Write, Read, Wr/Rd Signed-off-by: Brandon-Hurst --- ports/analog/Makefile | 15 +- ports/analog/common-hal/busio/I2C.c | 131 ++++++++++++++++-- ports/analog/common-hal/busio/I2C.h | 7 +- ports/analog/max32_port.h | 2 +- ports/analog/peripherals/max32690/max32_i2c.c | 35 +++++ ports/analog/peripherals/max32690/max32_i2c.h | 16 +++ 6 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 ports/analog/peripherals/max32690/max32_i2c.c create mode 100644 ports/analog/peripherals/max32690/max32_i2c.h diff --git a/ports/analog/Makefile b/ports/analog/Makefile index 96f9037aae924..cba5608feaad6 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -88,7 +88,11 @@ INC += \ -I$(PERIPH_SRC)/TMR \ -I$(PERIPH_SRC)/RTC \ -I$(PERIPH_SRC)/UART \ +<<<<<<< HEAD -I$(PERIPH_SRC)/TRNG +======= + -I$(PERIPH_SRC)/I2C +>>>>>>> edf069d0e7 (Add BUSIO.I2C and MAX32690 I2C structure) INC += -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/GCC @@ -124,13 +128,17 @@ SRC_MAX32 += \ $(PERIPH_SRC)/UART/uart_revb.c \ $(PERIPH_SRC)/TRNG/trng_revb.c \ $(PERIPH_SRC)/TRNG/trng_$(DIE_TYPE).c + $(PERIPH_SRC)/I2C/i2c_$(DIE_TYPE).c \ + $(PERIPH_SRC)/I2C/i2c_reva.c SRC_C += $(SRC_MAX32) \ boards/$(BOARD)/board.c \ boards/$(BOARD)/pins.c \ peripherals/$(MCU_VARIANT_LOWER)/pins.c \ peripherals/$(MCU_VARIANT_LOWER)/gpios.c \ - peripherals/$(MCU_VARIANT_LOWER)/max32_uart.c + peripherals/$(MCU_VARIANT_LOWER)/max32_uart.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_i2c.c + # ******************************************************************************* ### Compiler & Linker Flags ### @@ -271,6 +279,11 @@ flash-msdk: -f interface/cmsis-dap.cfg -f target/$(MCU_VARIANT_LOWER).cfg \ -c "program $(BUILD)/firmware.elf verify; init; reset; exit" +flash-openocd-jlink: + $(OPENOCD) -s $(OPENOCD_SCRIPTS) \ + -f interface/jlink.cfg -f target/$(MCU_VARIANT_LOWER).cfg \ + -c "program $(BUILD)/firmware.elf verify; init; reset; exit" + # flash target using JLink JLINK_DEVICE = $(MCU_VARIANT_LOWER) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index a8080fa5bc10d..306b4bc8f4ab1 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -30,7 +30,38 @@ #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" -static bool in_used = false; +#include "max32_port.h" + +#define I2C_PRIORITY 1 + +typedef enum { + I2C_FREE = 0, + I2C_BUSY, + I2C_NEVER_RESET, +} i2c_status_t; + +// Set each bit to indicate an active UART +// will be checked by ISR Handler for which ones to call +static uint8_t i2c_active = 0; +static i2c_status_t i2c_status[NUM_I2C]; +static volatile int i2c_err; + +// I2C Interrupt Handler +void i2c_isr(void) { + for (int i = 0; i < NUM_I2C; i++) { + if (i2c_active & (1 << i)) { + // NOTE: I2C_GET_TMR actually returns the I2C registers + MXC_I2C_AsyncHandler(MXC_I2C_GET_I2C(i)); + } + } +} + +// Callback gets called when AsyncRequest is COMPLETE +// (e.g. txLen == txCnt) +// static volatile void i2cCallback(mxc_i2c_req_t *req, int error) { +// i2c_status[MXC_I2C_GET_IDX(req->i2c)] = I2C_FREE; +// i2c_err = error; +// } // Construct I2C protocol, this function init i2c peripheral void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, @@ -38,7 +69,64 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) { - + int temp, err = 0; + + // Check for NULL Pointers && valid I2C settings + assert(self); + + // Assign I2C ID based on pins + temp = pinsToI2c(sda, scl); + if (temp == -1) { + // Error will be indicated by pinsToUart(tx, rx) function + return; + } else { + self->i2c_id = temp; + self->i2c_regs = MXC_I2C_GET_I2C(temp); + } + + assert((self->i2c_id >= 0) && (self->i2c_id < NUM_I2C)); + + // Init I2C as main / controller node (0x00 is ignored) + if ((scl != NULL) && (sda != NULL)) { + err = MXC_I2C_Init(self->i2c_regs, 1, 0x00); + if (err) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to init I2C.\n")); + } + err = MXC_I2C_SetFrequency(self->i2c_regs, frequency); + if (err < 0) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to set I2C frequency\n")); + } + } else if (scl != NULL) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("I2C needs SDA & SCL")); + } else if (sda != NULL) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("I2C needs SDA & SCL")); + } else { + // Should not get here, as shared-bindings API should not call this way + } + + // Attach I2C pins + self->sda = sda; + self->scl = scl; + common_hal_mcu_pin_claim(self->sda); + common_hal_mcu_pin_claim(self->scl); + + // Indicate to this module that the I2C is active + i2c_active |= (1 << self->i2c_id); + + // Set the timeout to a default value + if (((timeout < 0.0) || (timeout > 100.0))) { + self->timeout = 1.0; + } else { + self->timeout = timeout; + } + + /* Setup I2C interrupt */ + NVIC_ClearPendingIRQ(MXC_I2C_GET_IRQ(self->i2c_id)); + NVIC_DisableIRQ(MXC_I2C_GET_IRQ(self->i2c_id)); + NVIC_SetPriority(MXC_I2C_GET_IRQ(self->i2c_id), I2C_PRIORITY); + NVIC_SetVector(MXC_I2C_GET_IRQ(self->i2c_id), (uint32_t)i2c_isr); + + return; } // Never reset I2C obj when reload @@ -54,7 +142,13 @@ bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { // Deinit i2c obj, reset I2C pin void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { - // FIXME: Implement + MXC_I2C_Shutdown(self->i2c_regs); + + common_hal_reset_pin(self->sda); + common_hal_reset_pin(self->scl); + + self->sda = NULL; + self->scl = NULL; } void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { @@ -63,17 +157,36 @@ void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { // Probe device in I2C bus bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { - // FIXME: Implement - - return true; + int nack = 0; + + mxc_i2c_req_t addr_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_len = 0, + .tx_buf = NULL, + .rx_len = 0, + .rx_buf = NULL, + .callback = NULL + }; + + // Probe the address + nack = MXC_I2C_MasterTransaction(&addr_req); + if (nack) { + return false; + } else { + return true; + } } // Lock I2C bus bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { - // FIXME: Implement - - return false; + if (self->i2c_regs->status & MXC_F_I2C_STATUS_BUSY) { + return false; + } else { + self->has_lock = true; + return true; + } } // Check I2C lock status diff --git a/ports/analog/common-hal/busio/I2C.h b/ports/analog/common-hal/busio/I2C.h index 9571a8ab694f4..2c7ffec20785e 100644 --- a/ports/analog/common-hal/busio/I2C.h +++ b/ports/analog/common-hal/busio/I2C.h @@ -17,8 +17,13 @@ // Define a struct for what BUSIO.I2C should carry typedef struct { mp_obj_base_t base; + + int i2c_id; mxc_i2c_regs_t *i2c_regs; - bool has_lock; const mcu_pin_obj_t *scl; const mcu_pin_obj_t *sda; + const int frequency; + + uint32_t timeout; + bool has_lock; } busio_i2c_obj_t; diff --git a/ports/analog/max32_port.h b/ports/analog/max32_port.h index 3b4fe7bc87d90..fcab5b5976238 100644 --- a/ports/analog/max32_port.h +++ b/ports/analog/max32_port.h @@ -24,7 +24,7 @@ // UART Ports & pins #include "peripherals/max32690/max32_uart.h" -#define NUM_UARTS 4 +#include "peripherals/max32690/max32_i2c.h" /** START: GPIO4 Handling specific to MAX32690 */ #define GPIO4_PIN_MASK 0x00000003 diff --git a/ports/analog/peripherals/max32690/max32_i2c.c b/ports/analog/peripherals/max32690/max32_i2c.c new file mode 100644 index 0000000000000..8a13cf7a9a5a3 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_i2c.c @@ -0,0 +1,35 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/I2C.h" +#include "max32_i2c.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + +const mxc_gpio_cfg_t i2c_maps[NUM_I2C] = { + // I2C0A + { MXC_GPIO0, (MXC_GPIO_PIN_30 | MXC_GPIO_PIN_31), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // I2C1A + { MXC_GPIO2, (MXC_GPIO_PIN_17 | MXC_GPIO_PIN_18), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, +}; + +int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl) { + for (int i = 0; i < NUM_I2C; i++) { + if ((i2c_maps[i].port == (MXC_GPIO_GET_GPIO(sda->port))) + && (i2c_maps[i].mask == ((sda->mask) | (scl->mask)))) { + return i; + } + } + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("ERR: Unable to find an I2C matching pins...\nSCL: port %d mask %d\nSDA: port %d mask %d\n"), + sda->port, sda->mask, scl->port, scl->mask); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_i2c.h b/ports/analog/peripherals/max32690/max32_i2c.h new file mode 100644 index 0000000000000..3e554da5abcde --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_i2c.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "i2c_regs.h" +#include "mxc_sys.h" +#include "i2c.h" +#include "peripherals/pins.h" + +#define NUM_I2C 2 + +int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl); From 00c53ea87d3da15e53843ba1177f140bab4bed2b Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Fri, 7 Mar 2025 00:37:46 -0800 Subject: [PATCH 121/517] Add hardware mapping for BUSIO.SPI for MAX32690 Currently coupled a bit to APARD32690-SL board. Will refactor after testing BUSIO.SPI Signed-off-by: Brandon-Hurst --- ports/analog/Makefile | 16 +++--- ports/analog/peripherals/max32690/max32_spi.c | 49 +++++++++++++++++++ ports/analog/peripherals/max32690/max32_spi.h | 17 +++++++ 3 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 ports/analog/peripherals/max32690/max32_spi.c create mode 100644 ports/analog/peripherals/max32690/max32_spi.h diff --git a/ports/analog/Makefile b/ports/analog/Makefile index cba5608feaad6..bd26cdd80f295 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -88,11 +88,9 @@ INC += \ -I$(PERIPH_SRC)/TMR \ -I$(PERIPH_SRC)/RTC \ -I$(PERIPH_SRC)/UART \ -<<<<<<< HEAD - -I$(PERIPH_SRC)/TRNG -======= - -I$(PERIPH_SRC)/I2C ->>>>>>> edf069d0e7 (Add BUSIO.I2C and MAX32690 I2C structure) + -I$(PERIPH_SRC)/TRNG \ + -I$(PERIPH_SRC)/I2C \ + -I$(PERIPH_SRC)/SPI INC += -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/GCC @@ -129,7 +127,9 @@ SRC_MAX32 += \ $(PERIPH_SRC)/TRNG/trng_revb.c \ $(PERIPH_SRC)/TRNG/trng_$(DIE_TYPE).c $(PERIPH_SRC)/I2C/i2c_$(DIE_TYPE).c \ - $(PERIPH_SRC)/I2C/i2c_reva.c + $(PERIPH_SRC)/I2C/i2c_reva.c \ + $(PERIPH_SRC)/SPI/spi_$(DIE_TYPE).c \ + $(PERIPH_SRC)/SPI/spi_reva1.c SRC_C += $(SRC_MAX32) \ boards/$(BOARD)/board.c \ @@ -137,8 +137,8 @@ SRC_C += $(SRC_MAX32) \ peripherals/$(MCU_VARIANT_LOWER)/pins.c \ peripherals/$(MCU_VARIANT_LOWER)/gpios.c \ peripherals/$(MCU_VARIANT_LOWER)/max32_uart.c \ - peripherals/$(MCU_VARIANT_LOWER)/max32_i2c.c - + peripherals/$(MCU_VARIANT_LOWER)/max32_i2c.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_spi.c # ******************************************************************************* ### Compiler & Linker Flags ### diff --git a/ports/analog/peripherals/max32690/max32_spi.c b/ports/analog/peripherals/max32690/max32_spi.c new file mode 100644 index 0000000000000..587349b7e2140 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_spi.c @@ -0,0 +1,49 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/SPI.h" +#include "max32_spi.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + +// TODO Decouple from APARD board +const mxc_gpio_cfg_t spi_maps[NUM_SPI] = { + // DUMMY entry for SPI0 (not on APARD board) + { MXC_GPIO0, 0xFFFFFFFF, 0, 0, 0, 0}, + + // SPI1A + { MXC_GPIO1, (MXC_GPIO_PIN_23 | MXC_GPIO_PIN_26 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI2A + { MXC_GPIO2, (MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3 | MXC_GPIO_PIN_4 | MXC_GPIO_PIN_5), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI3A + { MXC_GPIO0, (MXC_GPIO_PIN_16 | MXC_GPIO_PIN_19 | MXC_GPIO_PIN_20 | MXC_GPIO_PIN_21), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI4A + { MXC_GPIO1, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1 | MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, +}; + +int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, + const mcu_pin_obj_t *sck, const mcu_pin_obj_t *cs) { + for (int i = 0; i < NUM_SPI; i++) { + if ((spi_maps[i].port == (MXC_GPIO_GET_GPIO(mosi->port))) + && (spi_maps[i].mask == ((cs->mask) | (mosi->mask) | (miso->mask) | (sck->mask)))) { + return i; + } + } + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("ERR: Unable to find an SPI matching pins... \ + \nMOSI: port %d mask %d\nMISO: port %d mask %d\n \ + \nSCK: port %d mask %d\nCS: port %d mask%d\n"), + mosi->port, mosi->mask, miso->port, miso->mask, + sck->port, sck->mask, cs->port, cs->mask); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_spi.h b/ports/analog/peripherals/max32690/max32_spi.h new file mode 100644 index 0000000000000..d0b9c83201c2b --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_spi.h @@ -0,0 +1,17 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "spi_regs.h" +#include "mxc_sys.h" +#include "spi.h" +#include "peripherals/pins.h" + +#define NUM_SPI 5 + +int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, + const mcu_pin_obj_t *sck, const mcu_pin_obj_t *cs); From 1d30ce794d1499168c08930f37d73e3e95e33596 Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Fri, 7 Mar 2025 02:16:40 -0800 Subject: [PATCH 122/517] Add complete BUSIO driver structure for SPI & I2C. TODO: - Figure out assigning CS Pins for SPI - Test & Debug SPI / I2C Signed-off-by: Brandon-Hurst --- ports/analog/common-hal/busio/I2C.c | 50 +++- ports/analog/common-hal/busio/SPI.c | 214 +++++++++++++++++- ports/analog/common-hal/busio/SPI.h | 9 +- ports/analog/max32_port.h | 1 + ports/analog/peripherals/max32690/max32_spi.c | 17 +- ports/analog/peripherals/max32690/max32_spi.h | 2 +- 6 files changed, 267 insertions(+), 26 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index 306b4bc8f4ab1..1834db0e4a3d4 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -200,28 +200,72 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } // Write data to the device selected by address +// todo test uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { - // FIXME: Implement + int ret; + mxc_i2c_req_t wr_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = (uint8_t *)data, + .tx_len = len, + .rx_buf = NULL, + .rx_len = 0, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&wr_req); + if (ret) { + mp_raise_RuntimeError(MP_ERROR_TEXT("ERROR during I2C Transaction\n")); + } + return 0; } // Read into buffer from the device selected by address +// todo test uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { - // FIXME: Implement + int ret; + mxc_i2c_req_t rd_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = NULL, + .tx_len = 0, + .rx_buf = data, + .rx_len = len, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&rd_req); + if (ret) { + mp_raise_RuntimeError(MP_ERROR_TEXT("ERROR during I2C Transaction\n")); + } + return 0; } // Write the bytes from out_data to the device selected by address, +// todo test uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - // FIXME: Implement + int ret; + mxc_i2c_req_t wr_rd_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = out_data, + .tx_len = out_len, + .rx_buf = in_data, + .rx_len = in_len, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&wr_rd_req); + if (ret) { + mp_raise_RuntimeError(MP_ERROR_TEXT("ERROR during I2C Transaction\n")); + } return 0; } diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index 083403b12bbe3..0c882d3ecc991 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -31,9 +31,35 @@ #include "supervisor/board.h" #include "shared-bindings/microcontroller/Pin.h" +#include "max32_port.h" +#include "spi_reva1.h" + // Note that any bugs introduced in this file can cause crashes // at startupfor chips using external SPI flash. +#define SPI_PRIORITY 1 + +typedef enum { + SPI_FREE = 0, + SPI_BUSY, + SPI_NEVER_RESET, +} spi_status_t; + +// Set each bit to indicate an active SPI +// will be checked by ISR Handler for which ones to call +static uint8_t spi_active = 0; +static spi_status_t spi_status[NUM_SPI]; +static volatile int spi_err; + +// SPI Interrupt Handler +void spi_isr(void) { + for (int i = 0; i < NUM_SPI; i++) { + if (spi_active & (1 << i)) { + MXC_SPI_AsyncHandler(MXC_SPI_GET_SPI(i)); + } + } +} + // Reset SPI when reload void spi_reset(void) { // FIXME: Implement @@ -41,19 +67,69 @@ void spi_reset(void) { } // Construct SPI protocol, this function init SPI peripheral +// todo: figure out Chip select behavior void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *sck, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { + int temp, err = 0; + // Check for NULL Pointers && valid I2C settings + assert(self); - // FIXME: Implement + // Assign I2C ID based on pins + temp = pinsToSpi(mosi, miso, sck); + if (temp == -1) { + // Error will be indicated by pinsToUart(tx, rx) function + return; + } else { + self->spi_id = temp; + self->spi_regs = MXC_SPI_GET_SPI(temp); + } + + assert((self->spi_id >= 0) && (self->spi_id < NUM_SPI)); + + // Init I2C as main / controller node (0x00 is ignored) + // FIXME: MUST map the SPI pins to a spi_pins_t struct + if ((mosi != NULL) && (miso != NULL) && (sck != NULL)) { + // spi, mastermode, quadModeUsed, numSubs, ssPolarity, frequency + err = MXC_SPI_RevA1_Init((mxc_spi_reva_regs_t *)self->spi_regs, 1, 0, 1, 0x00, 1000000); + if (err) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to init SPI.\n")); + } + } else { + mp_raise_NotImplementedError(MP_ERROR_TEXT("SPI needs MOSI, MISO, and SCK")); + } + + // Attach SPI pins + self->mosi = mosi; + self->miso = miso; + self->sck = sck; + common_hal_mcu_pin_claim(self->mosi); + common_hal_mcu_pin_claim(self->miso); + common_hal_mcu_pin_claim(self->sck); + + // Indicate to this module that the SPI is active + spi_active |= (1 << self->spi_id); + + /* Setup I2C interrupt */ + NVIC_ClearPendingIRQ(MXC_SPI_GET_IRQ(self->spi_id)); + NVIC_DisableIRQ(MXC_SPI_GET_IRQ(self->spi_id)); + NVIC_SetPriority(MXC_SPI_GET_IRQ(self->spi_id), SPI_PRIORITY); + NVIC_SetVector(MXC_SPI_GET_IRQ(self->spi_id), (uint32_t)spi_isr); + + return; } // Never reset SPI when reload void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - // FIXME: Implement + common_hal_never_reset_pin(self->mosi); + common_hal_never_reset_pin(self->miso); + common_hal_never_reset_pin(self->sck); + common_hal_never_reset_pin(self->nss); + + spi_status[self->spi_id] = SPI_NEVER_RESET; } // Check SPI status, deinited or not @@ -64,7 +140,16 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { // Deinit SPI obj void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { - // FIXME: Implement + MXC_SPI_Shutdown(self->spi_regs); + common_hal_reset_pin(self->mosi); + common_hal_reset_pin(self->miso); + common_hal_reset_pin(self->sck); + common_hal_reset_pin(self->nss); + + self->mosi = NULL; + self->miso = NULL; + self->sck = NULL; + self->nss = NULL; } // Configures the SPI bus. The SPI object must be locked. @@ -74,14 +159,58 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint8_t phase, uint8_t bits) { - // FIXME: Implement + mxc_spi_clkmode_t clk_mode; + int ret; + + self->baudrate = baudrate; + self->polarity = polarity; + self->phase = phase; + self->bits = bits; + + switch ((polarity << 1) | (phase)) { + case 0b00: + clk_mode = MXC_SPI_CLKMODE_0; + break; + case 0b01: + clk_mode = MXC_SPI_CLKMODE_1; + break; + case 0b10: + clk_mode = MXC_SPI_CLKMODE_2; + break; + case 0b11: + clk_mode = MXC_SPI_CLKMODE_3; + break; + default: + mp_raise_ValueError(MP_ERROR_TEXT("CPOL / CPHA must both be either 0 or 1\n")); + return false; + } + + ret = MXC_SPI_SetFrequency(self->spi_regs, baudrate); + if (ret) { + mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Frequency\n")); + return false; + } + ret = MXC_SPI_SetDataSize(self->spi_regs, bits); + if (ret) { + mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Frame Size\n")); + return false; + } + ret = MXC_SPI_SetMode(self->spi_regs, clk_mode); + if (ret) { + mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Clock Mode\n")); + return false; + } return true; } // Lock SPI bus bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { - // FIXME: Implement - return false; + if (spi_status[self->spi_id] != SPI_BUSY) { + self->has_lock = true; + return true; + } else { + return false; + } } // Check SPI lock status @@ -98,9 +227,27 @@ void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) { + int ret = 0; - // FIXME: Implement - return false; + mxc_spi_req_t wr_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = (uint8_t *)data, + .txLen = len, + .rxData = NULL, + .rxLen = 0, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = 0xFF, + }; + ret = MXC_SPI_MasterTransaction(&wr_req); + if (ret) { + return false; + } else { + return true; + } } // Read data into buffer @@ -108,8 +255,32 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) { - // FIXME: Implement - return false; + int ret = 0; + // uint8_t tx_buffer[len] = {0x0}; + + // for (int i = 0; i < len; i++) { + // tx_buffer[i] = write_value; + // } + + mxc_spi_req_t rd_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = NULL, + .txLen = len, + .rxData = data, + .rxLen = len, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = write_value, + }; + ret = MXC_SPI_MasterTransaction(&rd_req); + if (ret) { + return false; + } else { + return true; + } } // Write out the data in data_out @@ -119,8 +290,27 @@ bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, uint8_t *data_in, size_t len) { - // FIXME: Implement - return false; + int ret = 0; + + mxc_spi_req_t rd_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = (uint8_t *)data_out, + .txLen = len, + .rxData = data_in, + .rxLen = len, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = 0xFF, + }; + ret = MXC_SPI_MasterTransaction(&rd_req); + if (ret) { + return false; + } else { + return true; + } } // Get SPI baudrate diff --git a/ports/analog/common-hal/busio/SPI.h b/ports/analog/common-hal/busio/SPI.h index d90f55da111a0..e70d4234d9407 100644 --- a/ports/analog/common-hal/busio/SPI.h +++ b/ports/analog/common-hal/busio/SPI.h @@ -17,17 +17,24 @@ // Define a struct for what BUSIO.SPI should carry typedef struct { mp_obj_base_t base; + + // Spi regs & pins + int spi_id; mxc_spi_regs_t *spi_regs; - bool has_lock; const mcu_pin_obj_t *sck; const mcu_pin_obj_t *mosi; const mcu_pin_obj_t *miso; const mcu_pin_obj_t *nss; + + // Configuration uint32_t baudrate; uint16_t prescaler; uint8_t polarity; uint8_t phase; uint8_t bits; + + // Synch data + bool has_lock; } busio_spi_obj_t; void spi_reset(void); diff --git a/ports/analog/max32_port.h b/ports/analog/max32_port.h index fcab5b5976238..89830e96b0a49 100644 --- a/ports/analog/max32_port.h +++ b/ports/analog/max32_port.h @@ -25,6 +25,7 @@ // UART Ports & pins #include "peripherals/max32690/max32_uart.h" #include "peripherals/max32690/max32_i2c.h" +#include "peripherals/max32690/max32_spi.h" /** START: GPIO4 Handling specific to MAX32690 */ #define GPIO4_PIN_MASK 0x00000003 diff --git a/ports/analog/peripherals/max32690/max32_spi.c b/ports/analog/peripherals/max32690/max32_spi.c index 587349b7e2140..5a1dc8a9dc2f1 100644 --- a/ports/analog/peripherals/max32690/max32_spi.c +++ b/ports/analog/peripherals/max32690/max32_spi.c @@ -19,31 +19,30 @@ const mxc_gpio_cfg_t spi_maps[NUM_SPI] = { { MXC_GPIO0, 0xFFFFFFFF, 0, 0, 0, 0}, // SPI1A - { MXC_GPIO1, (MXC_GPIO_PIN_23 | MXC_GPIO_PIN_26 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), + { MXC_GPIO1, (MXC_GPIO_PIN_26 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, // SPI2A - { MXC_GPIO2, (MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3 | MXC_GPIO_PIN_4 | MXC_GPIO_PIN_5), + { MXC_GPIO2, (MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3 | MXC_GPIO_PIN_4), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, // SPI3A - { MXC_GPIO0, (MXC_GPIO_PIN_16 | MXC_GPIO_PIN_19 | MXC_GPIO_PIN_20 | MXC_GPIO_PIN_21), + { MXC_GPIO0, (MXC_GPIO_PIN_16 | MXC_GPIO_PIN_20 | MXC_GPIO_PIN_21), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, // SPI4A - { MXC_GPIO1, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1 | MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3), + { MXC_GPIO1, (MXC_GPIO_PIN_1 | MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, }; int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, - const mcu_pin_obj_t *sck, const mcu_pin_obj_t *cs) { + const mcu_pin_obj_t *sck) { for (int i = 0; i < NUM_SPI; i++) { if ((spi_maps[i].port == (MXC_GPIO_GET_GPIO(mosi->port))) - && (spi_maps[i].mask == ((cs->mask) | (mosi->mask) | (miso->mask) | (sck->mask)))) { + && (spi_maps[i].mask == ((mosi->mask) | (miso->mask) | (sck->mask)))) { return i; } } mp_raise_RuntimeError_varg(MP_ERROR_TEXT("ERR: Unable to find an SPI matching pins... \ - \nMOSI: port %d mask %d\nMISO: port %d mask %d\n \ - \nSCK: port %d mask %d\nCS: port %d mask%d\n"), + \nMOSI: port %d mask %d\nMISO: port %d mask %d\n"), mosi->port, mosi->mask, miso->port, miso->mask, - sck->port, sck->mask, cs->port, cs->mask); + sck->port, sck->mask); return -1; } diff --git a/ports/analog/peripherals/max32690/max32_spi.h b/ports/analog/peripherals/max32690/max32_spi.h index d0b9c83201c2b..76bb48a59a7e5 100644 --- a/ports/analog/peripherals/max32690/max32_spi.h +++ b/ports/analog/peripherals/max32690/max32_spi.h @@ -14,4 +14,4 @@ #define NUM_SPI 5 int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, - const mcu_pin_obj_t *sck, const mcu_pin_obj_t *cs); + const mcu_pin_obj_t *sck); From 7cccb66c2a9ea426eeff6d468f9ecd2b10da337f Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Fri, 7 Mar 2025 13:28:36 -0800 Subject: [PATCH 123/517] Add call to MXC_DelayHandler in SysTick ISR Signed-off-by: Brandon-Hurst --- ports/analog/supervisor/port.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/analog/supervisor/port.c b/ports/analog/supervisor/port.c index de83b011bdf0d..40e8e1d605038 100644 --- a/ports/analog/supervisor/port.c +++ b/ports/analog/supervisor/port.c @@ -73,6 +73,8 @@ volatile uint32_t system_ticks = 0; void SysTick_Handler(void) { system_ticks++; + + MXC_DelayHandler(); } From ebfbd01ce103d56655052efd5d6b3b71f5d49c85 Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Fri, 7 Mar 2025 14:35:50 -0800 Subject: [PATCH 124/517] Corrected I2C & SPI. Tested with example scripts; works with real peripherals. Signed-off-by: Brandon-Hurst --- ports/analog/Makefile | 2 +- ports/analog/common-hal/busio/I2C.c | 8 ++++++-- ports/analog/common-hal/busio/SPI.c | 20 ++++++++++++++++++- ports/analog/peripherals/max32690/max32_i2c.c | 4 ++-- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/ports/analog/Makefile b/ports/analog/Makefile index bd26cdd80f295..4a758eff9c46d 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -288,7 +288,7 @@ flash-openocd-jlink: JLINK_DEVICE = $(MCU_VARIANT_LOWER) JLINKEXE ?= JLink.exe -JLINKEXE += -if SWD -device ${JLINK_DEVICE} -speed 10000 +JLINKEXE += -if SWD -device ${JLINK_DEVICE} -speed 4000 COMMAND_FILE := tools/flash_max32.jlink flash-jlink: $(BUILD)/firmware.bin diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index 1834db0e4a3d4..cba8fca94f50e 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -46,6 +46,9 @@ static uint8_t i2c_active = 0; static i2c_status_t i2c_status[NUM_I2C]; static volatile int i2c_err; +// I2C struct for configuring GPIO pins +extern const mxc_gpio_cfg_t i2c_maps[NUM_I2C]; + // I2C Interrupt Handler void i2c_isr(void) { for (int i = 0; i < NUM_I2C; i++) { @@ -88,13 +91,14 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, // Init I2C as main / controller node (0x00 is ignored) if ((scl != NULL) && (sda != NULL)) { + MXC_GPIO_Config(&i2c_maps[self->i2c_id]); err = MXC_I2C_Init(self->i2c_regs, 1, 0x00); if (err) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to init I2C.\n")); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to init I2C. ERR: %d\n"), err); } err = MXC_I2C_SetFrequency(self->i2c_regs, frequency); if (err < 0) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to set I2C frequency\n")); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to set I2C frequency. ERR: %d\n"), err); } } else if (scl != NULL) { mp_raise_NotImplementedError(MP_ERROR_TEXT("I2C needs SDA & SCL")); diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index 0c882d3ecc991..7f19ebd1f945c 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -88,13 +88,28 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, self->spi_regs = MXC_SPI_GET_SPI(temp); } + // Other pins default to true + mxc_spi_pins_t spi_pins = { + .clock = TRUE, + .mosi = TRUE, + .miso = TRUE, + .ss0 = FALSE, + .ss1 = FALSE, + .ss2 = FALSE, + .vddioh = true, + .drvstr = MXC_GPIO_DRVSTR_0 + }; + assert((self->spi_id >= 0) && (self->spi_id < NUM_SPI)); // Init I2C as main / controller node (0x00 is ignored) // FIXME: MUST map the SPI pins to a spi_pins_t struct if ((mosi != NULL) && (miso != NULL) && (sck != NULL)) { // spi, mastermode, quadModeUsed, numSubs, ssPolarity, frequency - err = MXC_SPI_RevA1_Init((mxc_spi_reva_regs_t *)self->spi_regs, 1, 0, 1, 0x00, 1000000); + // err = MXC_SPI_Init((mxc_spi_reva_regs_t *)self->spi_regs, 1, 0, 1, 0x00, 1000000, &spi_pins); + err = MXC_SPI_Init(self->spi_regs, MXC_SPI_TYPE_CONTROLLER, MXC_SPI_INTERFACE_STANDARD, + 1, 0x01, 1000000, spi_pins); + MXC_GPIO_SetVSSEL(MXC_GPIO_GET_GPIO(sck->port), MXC_GPIO_VSSEL_VDDIOH, (sck->mask | miso->mask | mosi->mask | MXC_GPIO_PIN_0)); if (err) { mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to init SPI.\n")); } @@ -102,6 +117,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_NotImplementedError(MP_ERROR_TEXT("SPI needs MOSI, MISO, and SCK")); } + // FIXME: Debugging + + // Attach SPI pins self->mosi = mosi; self->miso = miso; diff --git a/ports/analog/peripherals/max32690/max32_i2c.c b/ports/analog/peripherals/max32690/max32_i2c.c index 8a13cf7a9a5a3..2dabc9b16a768 100644 --- a/ports/analog/peripherals/max32690/max32_i2c.c +++ b/ports/analog/peripherals/max32690/max32_i2c.c @@ -16,10 +16,10 @@ const mxc_gpio_cfg_t i2c_maps[NUM_I2C] = { // I2C0A { MXC_GPIO0, (MXC_GPIO_PIN_30 | MXC_GPIO_PIN_31), MXC_GPIO_FUNC_ALT1, - MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, // I2C1A { MXC_GPIO2, (MXC_GPIO_PIN_17 | MXC_GPIO_PIN_18), MXC_GPIO_FUNC_ALT1, - MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, }; int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl) { From 558eb93a590229c2eeff5c93b30500d8a8b90990 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Mon, 17 Mar 2025 17:14:08 -0700 Subject: [PATCH 125/517] Add extra I2C options for MAX32690 non-TQFN packages Also reformatted pinmaps for SPI/I2C/UART on MAX32690 Signed-off-by: Brandon Hurst --- ports/analog/peripherals/max32690/max32_i2c.c | 41 +++++++++++++++++++ ports/analog/peripherals/max32690/max32_spi.c | 15 ++++--- .../analog/peripherals/max32690/max32_uart.c | 20 +++------ 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/ports/analog/peripherals/max32690/max32_i2c.c b/ports/analog/peripherals/max32690/max32_i2c.c index 2dabc9b16a768..d5c8da0743129 100644 --- a/ports/analog/peripherals/max32690/max32_i2c.c +++ b/ports/analog/peripherals/max32690/max32_i2c.c @@ -13,14 +13,44 @@ #include "py/runtime.h" #include "py/mperrno.h" + +/* Note: The MAX32690 assigns the same alternate function to multiple sets + * of pins. The drivers will enable both sets so that either can be used. + * Users should ensure the unused set is left unconnected. + * + * See MAX32690 Rev A2 Errata #16: + * https://www.analog.com/media/en/technical-documentation/data-sheets/max32690_a2_errata_rev2.pdf + * + * Additionally, note that the TQFN package does not expose some of the duplicate pins. For this package, + * enabling the un-routed GPIOs has been shown to cause initialization issues with the I2C block. + * To work around this, "MAX32690GTK_PACKAGE_TQFN" can be defined by the build system. The recommend place + * to do it is in the "board.mk" file of the BSP. This will prevent the inaccessible pins from being configured. + */ + const mxc_gpio_cfg_t i2c_maps[NUM_I2C] = { + // I2C0 + { MXC_GPIO2, (MXC_GPIO_PIN_7 | MXC_GPIO_PIN_8), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }; + // I2C1 + { MXC_GPIO0, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }; + // I2C2 + { MXC_GPIO1, (MXC_GPIO_PIN_7 | MXC_GPIO_PIN_8), MXC_GPIO_FUNC_ALT3, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 };, +}; +#ifndef MAX32690GTK_PACKAGE_TQFN +const mxc_gpio_cfg_t i2c_maps_extra[NUM_I2C] = { // I2C0A { MXC_GPIO0, (MXC_GPIO_PIN_30 | MXC_GPIO_PIN_31), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, // I2C1A { MXC_GPIO2, (MXC_GPIO_PIN_17 | MXC_GPIO_PIN_18), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, + // I2C2C + { MXC_GPIO0, (MXC_GPIO_PIN_13 | MXC_GPIO_PIN_14), MXC_GPIO_FUNC_ALT3, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 };, }; +#endif int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl) { for (int i = 0; i < NUM_I2C; i++) { @@ -29,6 +59,17 @@ int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl) { return i; } } + + // Additional for loop to cover alternate potential I2C maps + #ifndef MAX32690GTK_PACKAGE_TQFN + for (int i = 0; i < NUM_I2C; i++) { + if ((i2c_maps_extra[i].port == (MXC_GPIO_GET_GPIO(sda->port))) + && (i2c_maps_extra[i].mask == ((sda->mask) | (scl->mask)))) { + return i; + } + } + #endif + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("ERR: Unable to find an I2C matching pins...\nSCL: port %d mask %d\nSDA: port %d mask %d\n"), sda->port, sda->mask, scl->port, scl->mask); return -1; diff --git a/ports/analog/peripherals/max32690/max32_spi.c b/ports/analog/peripherals/max32690/max32_spi.c index 5a1dc8a9dc2f1..ecd197036a49d 100644 --- a/ports/analog/peripherals/max32690/max32_spi.c +++ b/ports/analog/peripherals/max32690/max32_spi.c @@ -13,21 +13,20 @@ #include "py/runtime.h" #include "py/mperrno.h" -// TODO Decouple from APARD board const mxc_gpio_cfg_t spi_maps[NUM_SPI] = { - // DUMMY entry for SPI0 (not on APARD board) - { MXC_GPIO0, 0xFFFFFFFF, 0, 0, 0, 0}, - - // SPI1A + // SPI0 + { MXC_GPIO2, (MXC_GPIO_PIN_27 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), + MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }; + // SPI1 { MXC_GPIO1, (MXC_GPIO_PIN_26 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, - // SPI2A + // SPI2 { MXC_GPIO2, (MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3 | MXC_GPIO_PIN_4), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, - // SPI3A + // SPI3 { MXC_GPIO0, (MXC_GPIO_PIN_16 | MXC_GPIO_PIN_20 | MXC_GPIO_PIN_21), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, - // SPI4A + // SPI4 { MXC_GPIO1, (MXC_GPIO_PIN_1 | MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, }; diff --git a/ports/analog/peripherals/max32690/max32_uart.c b/ports/analog/peripherals/max32690/max32_uart.c index 3ccb930d85c14..3cfcc75ca0dc0 100644 --- a/ports/analog/peripherals/max32690/max32_uart.c +++ b/ports/analog/peripherals/max32690/max32_uart.c @@ -13,29 +13,19 @@ #include "py/runtime.h" #include "py/mperrno.h" -// FIXME: Remove upon test! -// mxc_uart_regs_t max32_uarts[NUM_UARTS] = { -// MXC_UART0, -// MXC_UART1, -// MXC_UART2, -// MXC_UART3, -// }; - const mxc_gpio_cfg_t uart_maps[NUM_UARTS] = { { MXC_GPIO2, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, { MXC_GPIO2, (MXC_GPIO_PIN_14 | MXC_GPIO_PIN_16), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, - { MXC_GPIO1, (MXC_GPIO_PIN_9 | MXC_GPIO_PIN_10), - MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_WEAK_PULL_UP, - MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, - { MXC_GPIO3, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1), - MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_WEAK_PULL_UP, - MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 } + { MXC_GPIO1, (MXC_GPIO_PIN_9 | MXC_GPIO_PIN_10), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO3, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1), MXC_GPIO_FUNC_ALT2, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 } }; int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx) { - for (int i = 0; i < NUM_UARTS; i++) { + for (int i = 0; i < uart_entries; i++) { if ((uart_maps[i].port == (MXC_GPIO_GET_GPIO(tx->port))) && (uart_maps[i].mask == ((tx->mask) | (rx->mask)))) { return i; From 479e0e181122c8888acd0c79151ce0e2c88bf28f Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Thu, 1 May 2025 17:23:33 -0700 Subject: [PATCH 126/517] Clean up MAX32 BUSIO drivers - Fixed errors with peripheral bus hardware descriptions in MAX32690 "peripherals/" - Resolved all FIXME comments - Removed unused ringbuf API from common-hal/busio/UART.c Signed-off-by: Brandon Hurst --- ports/analog/common-hal/busio/I2C.c | 4 --- ports/analog/common-hal/busio/SPI.c | 13 +--------- ports/analog/common-hal/busio/UART.c | 25 ++++--------------- ports/analog/common-hal/busio/UART.h | 2 -- ports/analog/peripherals/max32690/max32_i2c.c | 10 ++++---- ports/analog/peripherals/max32690/max32_i2c.h | 2 +- ports/analog/peripherals/max32690/max32_spi.c | 2 +- .../analog/peripherals/max32690/max32_uart.c | 6 ++--- 8 files changed, 16 insertions(+), 48 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index cba8fca94f50e..f8a5a8bd20971 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -155,10 +155,6 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { self->scl = NULL; } -void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { - // FIXME: Implement -} - // Probe device in I2C bus bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { int nack = 0; diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index 7f19ebd1f945c..d158f09673189 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -60,12 +60,6 @@ void spi_isr(void) { } } -// Reset SPI when reload -void spi_reset(void) { - // FIXME: Implement - return; -} - // Construct SPI protocol, this function init SPI peripheral // todo: figure out Chip select behavior void common_hal_busio_spi_construct(busio_spi_obj_t *self, @@ -102,11 +96,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, assert((self->spi_id >= 0) && (self->spi_id < NUM_SPI)); - // Init I2C as main / controller node (0x00 is ignored) - // FIXME: MUST map the SPI pins to a spi_pins_t struct + // Init SPI controller if ((mosi != NULL) && (miso != NULL) && (sck != NULL)) { // spi, mastermode, quadModeUsed, numSubs, ssPolarity, frequency - // err = MXC_SPI_Init((mxc_spi_reva_regs_t *)self->spi_regs, 1, 0, 1, 0x00, 1000000, &spi_pins); err = MXC_SPI_Init(self->spi_regs, MXC_SPI_TYPE_CONTROLLER, MXC_SPI_INTERFACE_STANDARD, 1, 0x01, 1000000, spi_pins); MXC_GPIO_SetVSSEL(MXC_GPIO_GET_GPIO(sck->port), MXC_GPIO_VSSEL_VDDIOH, (sck->mask | miso->mask | mosi->mask | MXC_GPIO_PIN_0)); @@ -117,9 +109,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_NotImplementedError(MP_ERROR_TEXT("SPI needs MOSI, MISO, and SCK")); } - // FIXME: Debugging - - // Attach SPI pins self->mosi = mosi; self->miso = miso; diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index 310965e34609b..408ae8452352e 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -96,7 +96,7 @@ static uint32_t timeout_ms = 0; static uint8_t uarts_active = 0; static uart_status_t uart_status[NUM_UARTS]; static volatile int uart_err; -// static uint8_t uart_never_reset_mask = 0; +static uint8_t uart_never_reset_mask = 0; static int isValidBaudrate(uint32_t baudrate) { switch (baudrate) { @@ -167,8 +167,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts, const mcu_pin_obj_t *rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, - mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer, - bool sigint_enabled) { + mp_float_t timeout, bool sigint_enabled) { int err, temp; // Check for NULL Pointers && valid UART settings @@ -236,21 +235,6 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->baudrate = baudrate; self->error = E_NO_ERROR; - // Initialize ringbuffer for receiving data - // FIXME: Either the use the ringbuf or get rid of it! - if (self->rx_pin) { - // if given a ringbuff, use that - if (receiver_buffer) { - ringbuf_init(&self->ringbuf, receiver_buffer, receiver_buffer_size); - } - // else create one and attach it - else { - if (!ringbuf_alloc(&self->ringbuf, receiver_buffer_size)) { - m_malloc_fail(receiver_buffer_size); - } - } - } - // Indicate to this module that the UART is active uarts_active |= (1 << self->uart_id); @@ -277,6 +261,7 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { // First disable the ISR to avoid pre-emption NVIC_DisableIRQ(UART0_IRQn); + // Shutdown the UART Controller MXC_UART_Shutdown(self->uart_regs); self->error = E_UNINITIALIZED; @@ -307,7 +292,7 @@ bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { }; } -// Read characters. len is in characters NOT bytes! +// Read characters. len is in characters. size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) { int err; @@ -474,7 +459,7 @@ void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { common_hal_never_reset_pin(self->rx_pin); common_hal_never_reset_pin(self->cts_pin); common_hal_never_reset_pin(self->rts_pin); - // uart_never_reset_mask |= ( 1 << (self->uart_id) ); + uart_never_reset_mask |= (1 << (self->uart_id)); } #endif // CIRCUITPY_BUSIO_UART diff --git a/ports/analog/common-hal/busio/UART.h b/ports/analog/common-hal/busio/UART.h index f24f310fe3a52..c2c2d837ca8a5 100644 --- a/ports/analog/common-hal/busio/UART.h +++ b/ports/analog/common-hal/busio/UART.h @@ -33,8 +33,6 @@ typedef struct { int error; float timeout; - ringbuf_t ringbuf; - const mcu_pin_obj_t *rx_pin; const mcu_pin_obj_t *tx_pin; const mcu_pin_obj_t *rts_pin; diff --git a/ports/analog/peripherals/max32690/max32_i2c.c b/ports/analog/peripherals/max32690/max32_i2c.c index d5c8da0743129..daf665bbee741 100644 --- a/ports/analog/peripherals/max32690/max32_i2c.c +++ b/ports/analog/peripherals/max32690/max32_i2c.c @@ -20,7 +20,7 @@ * * See MAX32690 Rev A2 Errata #16: * https://www.analog.com/media/en/technical-documentation/data-sheets/max32690_a2_errata_rev2.pdf - * + * * Additionally, note that the TQFN package does not expose some of the duplicate pins. For this package, * enabling the un-routed GPIOs has been shown to cause initialization issues with the I2C block. * To work around this, "MAX32690GTK_PACKAGE_TQFN" can be defined by the build system. The recommend place @@ -30,13 +30,13 @@ const mxc_gpio_cfg_t i2c_maps[NUM_I2C] = { // I2C0 { MXC_GPIO2, (MXC_GPIO_PIN_7 | MXC_GPIO_PIN_8), MXC_GPIO_FUNC_ALT1, - MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }; + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, // I2C1 { MXC_GPIO0, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, - MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }; + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, // I2C2 { MXC_GPIO1, (MXC_GPIO_PIN_7 | MXC_GPIO_PIN_8), MXC_GPIO_FUNC_ALT3, - MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 };, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, }; #ifndef MAX32690GTK_PACKAGE_TQFN const mxc_gpio_cfg_t i2c_maps_extra[NUM_I2C] = { @@ -48,7 +48,7 @@ const mxc_gpio_cfg_t i2c_maps_extra[NUM_I2C] = { MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, // I2C2C { MXC_GPIO0, (MXC_GPIO_PIN_13 | MXC_GPIO_PIN_14), MXC_GPIO_FUNC_ALT3, - MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 };, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, }; #endif diff --git a/ports/analog/peripherals/max32690/max32_i2c.h b/ports/analog/peripherals/max32690/max32_i2c.h index 3e554da5abcde..b64cfd308dcf5 100644 --- a/ports/analog/peripherals/max32690/max32_i2c.h +++ b/ports/analog/peripherals/max32690/max32_i2c.h @@ -11,6 +11,6 @@ #include "i2c.h" #include "peripherals/pins.h" -#define NUM_I2C 2 +#define NUM_I2C 3 int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl); diff --git a/ports/analog/peripherals/max32690/max32_spi.c b/ports/analog/peripherals/max32690/max32_spi.c index ecd197036a49d..f2594787598b2 100644 --- a/ports/analog/peripherals/max32690/max32_spi.c +++ b/ports/analog/peripherals/max32690/max32_spi.c @@ -16,7 +16,7 @@ const mxc_gpio_cfg_t spi_maps[NUM_SPI] = { // SPI0 { MXC_GPIO2, (MXC_GPIO_PIN_27 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), - MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }; + MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, // SPI1 { MXC_GPIO1, (MXC_GPIO_PIN_26 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, diff --git a/ports/analog/peripherals/max32690/max32_uart.c b/ports/analog/peripherals/max32690/max32_uart.c index 3cfcc75ca0dc0..5f7a8568fb426 100644 --- a/ports/analog/peripherals/max32690/max32_uart.c +++ b/ports/analog/peripherals/max32690/max32_uart.c @@ -18,14 +18,14 @@ const mxc_gpio_cfg_t uart_maps[NUM_UARTS] = { MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, { MXC_GPIO2, (MXC_GPIO_PIN_14 | MXC_GPIO_PIN_16), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, - { MXC_GPIO1, (MXC_GPIO_PIN_9 | MXC_GPIO_PIN_10), MXC_GPIO_FUNC_ALT1, + { MXC_GPIO1, (MXC_GPIO_PIN_9 | MXC_GPIO_PIN_10), MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, - { MXC_GPIO3, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1), MXC_GPIO_FUNC_ALT2, + { MXC_GPIO3, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1), MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 } }; int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx) { - for (int i = 0; i < uart_entries; i++) { + for (int i = 0; i < NUM_UARTS; i++) { if ((uart_maps[i].port == (MXC_GPIO_GET_GPIO(tx->port))) && (uart_maps[i].mask == ((tx->mask) | (rx->mask)))) { return i; From 3a8e56d0c8c5dacd664a0483217b85b189c5b0a9 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Thu, 1 May 2025 17:39:22 -0700 Subject: [PATCH 127/517] Cleanup copy-paste comments in MAX32 busio files Signed-off-by: Brandon Hurst --- ports/analog/common-hal/busio/I2C.c | 27 +-------------------------- ports/analog/common-hal/busio/I2C.h | 1 - ports/analog/common-hal/busio/SPI.c | 22 ++-------------------- ports/analog/common-hal/busio/SPI.h | 1 - ports/analog/common-hal/busio/UART.c | 3 ++- ports/analog/common-hal/busio/UART.h | 5 ----- 6 files changed, 5 insertions(+), 54 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index f8a5a8bd20971..9fe903428f5b9 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -40,8 +40,7 @@ typedef enum { I2C_NEVER_RESET, } i2c_status_t; -// Set each bit to indicate an active UART -// will be checked by ISR Handler for which ones to call +// Set each bit to indicate an active I2c static uint8_t i2c_active = 0; static i2c_status_t i2c_status[NUM_I2C]; static volatile int i2c_err; @@ -49,23 +48,6 @@ static volatile int i2c_err; // I2C struct for configuring GPIO pins extern const mxc_gpio_cfg_t i2c_maps[NUM_I2C]; -// I2C Interrupt Handler -void i2c_isr(void) { - for (int i = 0; i < NUM_I2C; i++) { - if (i2c_active & (1 << i)) { - // NOTE: I2C_GET_TMR actually returns the I2C registers - MXC_I2C_AsyncHandler(MXC_I2C_GET_I2C(i)); - } - } -} - -// Callback gets called when AsyncRequest is COMPLETE -// (e.g. txLen == txCnt) -// static volatile void i2cCallback(mxc_i2c_req_t *req, int error) { -// i2c_status[MXC_I2C_GET_IDX(req->i2c)] = I2C_FREE; -// i2c_err = error; -// } - // Construct I2C protocol, this function init i2c peripheral void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *scl, @@ -80,7 +62,6 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, // Assign I2C ID based on pins temp = pinsToI2c(sda, scl); if (temp == -1) { - // Error will be indicated by pinsToUart(tx, rx) function return; } else { self->i2c_id = temp; @@ -124,12 +105,6 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, self->timeout = timeout; } - /* Setup I2C interrupt */ - NVIC_ClearPendingIRQ(MXC_I2C_GET_IRQ(self->i2c_id)); - NVIC_DisableIRQ(MXC_I2C_GET_IRQ(self->i2c_id)); - NVIC_SetPriority(MXC_I2C_GET_IRQ(self->i2c_id), I2C_PRIORITY); - NVIC_SetVector(MXC_I2C_GET_IRQ(self->i2c_id), (uint32_t)i2c_isr); - return; } diff --git a/ports/analog/common-hal/busio/I2C.h b/ports/analog/common-hal/busio/I2C.h index 2c7ffec20785e..55c230ee2f97f 100644 --- a/ports/analog/common-hal/busio/I2C.h +++ b/ports/analog/common-hal/busio/I2C.h @@ -7,7 +7,6 @@ #pragma once #include "common-hal/microcontroller/Pin.h" -// FIXME: Silabs includes "peripherals/periph.h. Figure out what's in this file. " #include "py/obj.h" // HAL-specific diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index d158f09673189..999eb8490b0eb 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -46,22 +46,11 @@ typedef enum { } spi_status_t; // Set each bit to indicate an active SPI -// will be checked by ISR Handler for which ones to call static uint8_t spi_active = 0; static spi_status_t spi_status[NUM_SPI]; static volatile int spi_err; -// SPI Interrupt Handler -void spi_isr(void) { - for (int i = 0; i < NUM_SPI; i++) { - if (spi_active & (1 << i)) { - MXC_SPI_AsyncHandler(MXC_SPI_GET_SPI(i)); - } - } -} - // Construct SPI protocol, this function init SPI peripheral -// todo: figure out Chip select behavior void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *sck, const mcu_pin_obj_t *mosi, @@ -69,13 +58,12 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, bool half_duplex) { int temp, err = 0; - // Check for NULL Pointers && valid I2C settings + // Check for NULL Pointer assert(self); - // Assign I2C ID based on pins + // Assign SPI ID based on pins temp = pinsToSpi(mosi, miso, sck); if (temp == -1) { - // Error will be indicated by pinsToUart(tx, rx) function return; } else { self->spi_id = temp; @@ -120,12 +108,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, // Indicate to this module that the SPI is active spi_active |= (1 << self->spi_id); - /* Setup I2C interrupt */ - NVIC_ClearPendingIRQ(MXC_SPI_GET_IRQ(self->spi_id)); - NVIC_DisableIRQ(MXC_SPI_GET_IRQ(self->spi_id)); - NVIC_SetPriority(MXC_SPI_GET_IRQ(self->spi_id), SPI_PRIORITY); - NVIC_SetVector(MXC_SPI_GET_IRQ(self->spi_id), (uint32_t)spi_isr); - return; } diff --git a/ports/analog/common-hal/busio/SPI.h b/ports/analog/common-hal/busio/SPI.h index e70d4234d9407..d10601876fb5e 100644 --- a/ports/analog/common-hal/busio/SPI.h +++ b/ports/analog/common-hal/busio/SPI.h @@ -8,7 +8,6 @@ #define MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H #include "common-hal/microcontroller/Pin.h" -// FIXME: Silabs includes "peripherals/periph.h. Figure out what's in this file. " #include "py/obj.h" // HAL-specific diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index 408ae8452352e..3b159c7b29026 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -167,7 +167,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts, const mcu_pin_obj_t *rs485_dir, bool rs485_invert, uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, - mp_float_t timeout, bool sigint_enabled) { + mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer, + bool sigint_enabled) { int err, temp; // Check for NULL Pointers && valid UART settings diff --git a/ports/analog/common-hal/busio/UART.h b/ports/analog/common-hal/busio/UART.h index c2c2d837ca8a5..a727116f7b882 100644 --- a/ports/analog/common-hal/busio/UART.h +++ b/ports/analog/common-hal/busio/UART.h @@ -9,11 +9,6 @@ #include "common-hal/microcontroller/Pin.h" #include "py/obj.h" -#include "py/ringbuf.h" - -// #include "uart.h" -// #include "uart_regs.h" -// #include "mxc_sys.h" #include "max32_port.h" From ba301159140b562debb10504af8eaba087a8e60a Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Mon, 5 May 2025 10:31:25 -0700 Subject: [PATCH 128/517] Correct ringbuffer code for ports/analog BUSIO - Add correct ringbuffer handling code to ports/analog/common-hal/UART.c Signed-off-by: Brandon Hurst --- ports/analog/common-hal/busio/UART.c | 103 +++++++++++++++------------ ports/analog/common-hal/busio/UART.h | 8 +-- 2 files changed, 63 insertions(+), 48 deletions(-) diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index 3b159c7b29026..f781da8705545 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -24,20 +24,27 @@ * THE SOFTWARE. */ +/** TODO: + * - Fix readline issue + * +*/ + #if CIRCUITPY_BUSIO_UART +#include "mpconfigport.h" +#include "supervisor/shared/tick.h" + #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/tick.h" #include "shared-bindings/busio/UART.h" - #include "shared-bindings/microcontroller/Pin.h" - -#include "mpconfigport.h" #include "shared/readline/readline.h" #include "shared/runtime/interrupt_char.h" + #include "py/gc.h" +#include "py/ringbuf.h" #include "py/mperrno.h" +#include "py/mpprint.h" #include "py/runtime.h" #include "max32_port.h" @@ -47,29 +54,6 @@ // UART IRQ Priority #define UART_PRIORITY 1 -/** - * -// Define a struct for what BUSIO.UART should contain -typedef struct { - mp_obj_base_t base; - int uart_id; - mxc_uart_regs_t* uart_regs; - - const mcu_pin_obj_t *rx_pin; - const mcu_pin_obj_t *tx_pin; - const mcu_pin_obj_t *rts_pin; - const mcu_pin_obj_t *cts_pin; - - uint8_t bits; - uint32_t baudrate; - bool parity; - uint8_t stop_bits; - - uint32_t timeout_ms; - bool error; -} busio_uart_obj_t; - */ - typedef enum { UART_9600 = 9600, UART_14400 = 14400, @@ -82,7 +66,6 @@ typedef enum { UART_921600 = 921600, } uart_valid_baudrates; - typedef enum { UART_FREE = 0, UART_BUSY, @@ -97,6 +80,7 @@ static uint8_t uarts_active = 0; static uart_status_t uart_status[NUM_UARTS]; static volatile int uart_err; static uint8_t uart_never_reset_mask = 0; +static busio_uart_obj_t *context; static int isValidBaudrate(uint32_t baudrate) { switch (baudrate) { @@ -159,6 +143,10 @@ void uart_isr(void) { static volatile void uartCallback(mxc_uart_req_t *req, int error) { uart_status[MXC_UART_GET_IDX(req->uart)] = UART_FREE; uart_err = error; + + MXC_SYS_Crit_Enter(); + ringbuf_put_n(context->ringbuf, req->rxData, req->rxLen); + MXC_SYS_Crit_Exit(); } // Construct an underlying UART object. @@ -183,9 +171,15 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->uart_id = temp; self->uart_regs = MXC_UART_GET_UART(temp); } - assert((self->uart_id >= 0) && (self->uart_id < NUM_UARTS)); + // Check for size of ringbuffer, desire powers of 2 + // At least use 1 byte if no size is given + assert((receiver_buffer_size & (receiver_buffer_size - 1)) == 0); + if (receiver_buffer_size == 0) { + receiver_buffer_size += 1; + } + // Indicate RS485 not implemented if ((rs485_dir != NULL) || (rs485_invert)) { mp_raise_NotImplementedError(MP_ERROR_TEXT("RS485")); @@ -246,12 +240,31 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->timeout = timeout; } - /* Setup UART interrupt */ + // Initialize ringbuffer + if (receiver_buffer == NULL) { + self->ringbuf = gc_alloc(receiver_buffer_size, false); + if (!ringbuf_alloc(self->ringbuf, receiver_buffer_size)) { + m_malloc_fail(receiver_buffer_size); + mp_raise_RuntimeError(MP_ERROR_TEXT("ERR: Could not init ringbuffer\n")); + } + } else { + if (!(ringbuf_init(self->ringbuf, receiver_buffer, receiver_buffer_size))) { + mp_raise_RuntimeError(MP_ERROR_TEXT("ERR: Could not init ringbuffer\n")); + } + ; + } + + context = self; + + // Setup UART interrupt + NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_SetPriority(MXC_UART_GET_IRQ(self->uart_id), UART_PRIORITY); NVIC_SetVector(MXC_UART_GET_IRQ(self->uart_id), (uint32_t)uart_isr); + NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + return; } @@ -260,7 +273,7 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { if (!common_hal_busio_uart_deinited(self)) { // First disable the ISR to avoid pre-emption - NVIC_DisableIRQ(UART0_IRQn); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); // Shutdown the UART Controller MXC_UART_Shutdown(self->uart_regs); @@ -280,6 +293,8 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { self->cts_pin = NULL; self->rts_pin = NULL; + ringbuf_deinit(self->ringbuf); + // Indicate to this module that the UART is not active uarts_active &= ~(1 << self->uart_id); } @@ -306,8 +321,6 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uart_status[self->uart_id] = UART_BUSY; bytes_remaining = len; - NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mxc_uart_req_t uart_rd_req; uart_rd_req.rxCnt = 0; uart_rd_req.txCnt = 0; @@ -341,13 +354,16 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, } // Check for errors from the callback else if (uart_err != E_NO_ERROR) { + mp_printf(&mp_sys_stdout_print, "MAX32 ERR: %d\n", uart_err); MXC_UART_AbortAsync(self->uart_regs); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("MAX32 ERR: %d\n"), uart_err); } - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - return len; + // Copy the data from the ringbuf (or return error) + MXC_SYS_Crit_Enter(); + err = ringbuf_get_n(context->ringbuf, data, len); + MXC_SYS_Crit_Exit(); + + return err; } // Write characters. len is in characters NOT bytes! @@ -391,11 +407,10 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { // Call the handler and abort if errors - uart_err = MXC_UART_AsyncHandler(MXC_UART_GET_UART(0)); + uart_err = MXC_UART_AsyncHandler(self->uart_regs); if (uart_err != E_NO_ERROR) { - MXC_UART_AbortAsync(MXC_UART_GET_UART(0)); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(0)); - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("MAX32 ERR: %d\n"), uart_err); + mp_printf(&mp_sys_stdout_print, "MAX32 ERR: %d\n", uart_err); + MXC_UART_AbortAsync(self->uart_regs); } } @@ -407,9 +422,8 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, } // Check for errors from the callback else if (uart_err != E_NO_ERROR) { + mp_printf(&mp_sys_stdout_print, "MAX32 ERR: %d\n", uart_err); MXC_UART_AbortAsync(self->uart_regs); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("MAX32 ERR: %d\n"), uart_err); } return len; @@ -444,11 +458,12 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou } uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { - return MXC_UART_GetRXFIFOAvailable(self->uart_regs); + return ringbuf_num_filled(self->ringbuf); } void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { MXC_UART_ClearRXFIFO(self->uart_regs); + ringbuf_clear(self->ringbuf); } bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { diff --git a/ports/analog/common-hal/busio/UART.h b/ports/analog/common-hal/busio/UART.h index a727116f7b882..296c738772e68 100644 --- a/ports/analog/common-hal/busio/UART.h +++ b/ports/analog/common-hal/busio/UART.h @@ -9,25 +9,25 @@ #include "common-hal/microcontroller/Pin.h" #include "py/obj.h" +#include "py/ringbuf.h" #include "max32_port.h" // Define a struct for what BUSIO.UART should contain typedef struct { mp_obj_base_t base; + int error; + float timeout; int uart_id; int uart_map; mxc_uart_regs_t *uart_regs; - + ringbuf_t *ringbuf; bool parity; uint8_t bits; uint8_t stop_bits; uint32_t baudrate; - int error; - float timeout; - const mcu_pin_obj_t *rx_pin; const mcu_pin_obj_t *tx_pin; const mcu_pin_obj_t *rts_pin; From b9dd6a043941063c50ad2d2313366ed3f53b21d1 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Mon, 5 May 2025 10:34:22 -0700 Subject: [PATCH 129/517] Update jlink script for ports/analog Signed-off-by: Brandon Hurst --- ports/analog/tools/flash_max32.jlink | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/analog/tools/flash_max32.jlink b/ports/analog/tools/flash_max32.jlink index 4c9cbacb96fee..8e3647cb0afc7 100644 --- a/ports/analog/tools/flash_max32.jlink +++ b/ports/analog/tools/flash_max32.jlink @@ -1,6 +1,7 @@ si 1 erase -loadbin build-APARD/firmware.bin 0x10000000 -r -g +loadbin build-apard32690/firmware.bin 0x10000000 +reset +halt +go exit From 0ffcc782969f0eb4cde6eaba45a1d8676e515325 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Tue, 6 May 2025 09:19:43 -0700 Subject: [PATCH 130/517] Refactor ports/analog/common-hal/busio i2c probe function. - Used direct hardware values to rely on common HW implementation - Accounted for TX Lockout conditions in Transmit FIFO causing errors Signed-off-by: Brandon Hurst --- ports/analog/common-hal/busio/I2C.c | 54 +++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index 9fe903428f5b9..ca114d337ede0 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -132,25 +132,49 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { // Probe device in I2C bus bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { - int nack = 0; + int32_t flags = 0x0; + bool ret = false; + + MXC_I2C_ClearFlags(self->i2c_regs, 0xFF, 0xFF); + MXC_I2C_EnableInt(self->i2c_regs, 0xFF, 0xFF); + + // Pre-load target address into transmit FIFO + addr = (addr << 1); + self->i2c_regs->fifo = addr; + + // Set start bit & wait for it to clear + self->i2c_regs->mstctrl |= MXC_F_I2C_MSTCTRL_START; + + // wait for ACK/NACK + // if tx_lockout occurs, clear the error and re-load the target address + while (!(self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_ADDR_ACK) && + !(self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_ADDR_NACK_ERR)) { + if (self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_TX_LOCKOUT) { + self->i2c_regs->intfl0 |= MXC_F_I2C_INTFL0_TX_LOCKOUT; + self->i2c_regs->fifo = addr; + } + } + flags = self->i2c_regs->intfl0; + self->i2c_regs->intfl0 = MXC_F_I2C_INTFL0_ADDR_ACK | MXC_F_I2C_INTFL0_ADDR_NACK_ERR - mxc_i2c_req_t addr_req = { - .addr = addr, - .i2c = self->i2c_regs, - .tx_len = 0, - .tx_buf = NULL, - .rx_len = 0, - .rx_buf = NULL, - .callback = NULL - }; + // Set / Wait for stop + self->i2c_regs->mstctrl |= MXC_F_I2C_MSTCTRL_STOP; + while ((self->i2c_regs->mstctrl & MXC_F_I2C_MSTCTRL_STOP)) { + ; + } - // Probe the address - nack = MXC_I2C_MasterTransaction(&addr_req); - if (nack) { - return false; + // Wait for controller not busy, then clear flags + while (self->i2c_regs->status & MXC_F_I2C_STATUS_BUSY) { + ; + } + + if (flags & MXC_F_I2C_INTFL0_ADDR_ACK) { + ret = true; } else { - return true; + ret = false; } + MXC_I2C_ClearFlags(self->i2c_regs, 0xff, 0xff); + return ret; } // Lock I2C bus From 94eafa5d0ce45ada21c30b6f270c0895f04c1e2d Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Tue, 6 May 2025 09:21:14 -0700 Subject: [PATCH 131/517] Expand command in ports/analog/tools/flash_max32.jlink for clarity. Signed-off-by: Brandon Hurst --- ports/analog/tools/flash_max32.jlink | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/analog/tools/flash_max32.jlink b/ports/analog/tools/flash_max32.jlink index 8e3647cb0afc7..0453f4cb077fd 100644 --- a/ports/analog/tools/flash_max32.jlink +++ b/ports/analog/tools/flash_max32.jlink @@ -1,4 +1,4 @@ -si 1 +selectinterface swd erase loadbin build-apard32690/firmware.bin 0x10000000 reset From 4cb58b64f75aa9e6df9bbf74ce53d573106c2a9a Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Tue, 6 May 2025 14:08:22 -0700 Subject: [PATCH 132/517] Fix i2c scan bug with ports/analog/ BUSIO. - Probe function caused infinite loops / unreliable ACK values - Replaced with probe-function with low-level routines Signed-off-by: Brandon Hurst --- ports/analog/common-hal/busio/I2C.c | 42 +++++++++++------------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index ca114d337ede0..c1b1d9efe1b17 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -34,15 +34,8 @@ #define I2C_PRIORITY 1 -typedef enum { - I2C_FREE = 0, - I2C_BUSY, - I2C_NEVER_RESET, -} i2c_status_t; - // Set each bit to indicate an active I2c static uint8_t i2c_active = 0; -static i2c_status_t i2c_status[NUM_I2C]; static volatile int i2c_err; // I2C struct for configuring GPIO pins @@ -68,7 +61,9 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, self->i2c_regs = MXC_I2C_GET_I2C(temp); } + // Check for valid I2C controller assert((self->i2c_id >= 0) && (self->i2c_id < NUM_I2C)); + assert(!(i2c_active & (1 << self->i2c_id))); // Init I2C as main / controller node (0x00 is ignored) if ((scl != NULL) && (sda != NULL)) { @@ -132,48 +127,43 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { // Probe device in I2C bus bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { - int32_t flags = 0x0; - bool ret = false; + uint32_t int_fl0, int_fl1; + bool ret = 0; - MXC_I2C_ClearFlags(self->i2c_regs, 0xFF, 0xFF); - MXC_I2C_EnableInt(self->i2c_regs, 0xFF, 0xFF); + // Clear FIFOs & all interrupt flags + MXC_I2C_ClearRXFIFO(self->i2c_regs); + MXC_I2C_ClearTXFIFO(self->i2c_regs); + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); // Pre-load target address into transmit FIFO addr = (addr << 1); self->i2c_regs->fifo = addr; // Set start bit & wait for it to clear - self->i2c_regs->mstctrl |= MXC_F_I2C_MSTCTRL_START; + MXC_I2C_Start(self->i2c_regs); // wait for ACK/NACK - // if tx_lockout occurs, clear the error and re-load the target address while (!(self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_ADDR_ACK) && !(self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_ADDR_NACK_ERR)) { - if (self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_TX_LOCKOUT) { - self->i2c_regs->intfl0 |= MXC_F_I2C_INTFL0_TX_LOCKOUT; - self->i2c_regs->fifo = addr; - } } - flags = self->i2c_regs->intfl0; - self->i2c_regs->intfl0 = MXC_F_I2C_INTFL0_ADDR_ACK | MXC_F_I2C_INTFL0_ADDR_NACK_ERR - // Set / Wait for stop - self->i2c_regs->mstctrl |= MXC_F_I2C_MSTCTRL_STOP; - while ((self->i2c_regs->mstctrl & MXC_F_I2C_MSTCTRL_STOP)) { - ; - } + // Save interrupt flags for ACK/NACK checking + int_fl0 = self->i2c_regs->intfl0; + + // Set / Wait for stop + MXC_I2C_Stop(self->i2c_regs); // Wait for controller not busy, then clear flags while (self->i2c_regs->status & MXC_F_I2C_STATUS_BUSY) { ; } + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); - if (flags & MXC_F_I2C_INTFL0_ADDR_ACK) { + if (int_fl0 & MXC_F_I2C_INTFL0_ADDR_ACK) { ret = true; } else { ret = false; } - MXC_I2C_ClearFlags(self->i2c_regs, 0xff, 0xff); return ret; } From 814047d60fcd4fad5334cd204f6346fff5504a56 Mon Sep 17 00:00:00 2001 From: Brandon Hurst Date: Tue, 6 May 2025 14:41:26 -0700 Subject: [PATCH 133/517] Add translations for ports/analog/busio Signed-off-by: Brandon Hurst --- locale/circuitpython.pot | 114 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 98fc54741eb39..5b9e9afed76bf 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -28,6 +28,25 @@ msgid "" "Code stopped by auto-reload. Reloading soon.\n" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#, c-format +msgid "" +"\n" +"ERR: Error starting transaction: %d\n" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "" +"\n" +"ERR: Requested bus is busy\n" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "" +"\n" +"ERR: Uart transaction timed out.\n" +msgstr "" + #: supervisor/shared/safe_mode.c msgid "" "\n" @@ -628,6 +647,10 @@ msgid "" "disable.\n" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Baudrate invalid. Must be a standard UART baudrate.\n" +msgstr "" + #: ports/espressif/common-hal/canio/CAN.c msgid "Baudrate not supported by peripheral" msgstr "" @@ -717,6 +740,10 @@ msgstr "" msgid "CIRCUITPY drive could not be found or created." msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "CPOL / CPHA must both be either 0 or 1\n" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c msgid "CRC or checksum was invalid" msgstr "" @@ -943,6 +970,38 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "ERR: Could not init ringbuffer\n" +msgstr "" + +#: ports/analog/peripherals/max32690/max32_uart.c +#, c-format +msgid "" +"ERR: Unable to find a uart matching pins...\n" +"TX: port %d mask %d\n" +"RX: port %d mask %d\n" +msgstr "" + +#: ports/analog/peripherals/max32690/max32_i2c.c +#, c-format +msgid "" +"ERR: Unable to find an I2C matching pins...\n" +"SCL: port %d mask %d\n" +"SDA: port %d mask %d\n" +msgstr "" + +#: ports/analog/peripherals/max32690/max32_spi.c +#, c-format +msgid "" +"ERR: Unable to find an SPI matching pins... \n" +"MOSI: port %d mask %d\n" +"MISO: port %d mask %d\n" +msgstr "" + +#: ports/analog/common-hal/busio/I2C.c +msgid "ERROR during I2C Transaction\n" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c msgid "ESP-IDF memory allocation failed" @@ -1036,6 +1095,19 @@ msgstr "" msgid "Failed to enable continuous" msgstr "" +#: ports/analog/common-hal/busio/I2C.c +#, c-format +msgid "Failed to init I2C. ERR: %d\n" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to init SPI.\n" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "Failed to initialize UART.\n" +msgstr "" + #: shared-module/audiomp3/MP3Decoder.c msgid "Failed to parse MP3 file" msgstr "" @@ -1049,6 +1121,23 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/I2C.c +#, c-format +msgid "Failed to set I2C frequency. ERR: %d\n" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode\n" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Frame Size\n" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Frequency\n" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1089,6 +1178,10 @@ msgstr "" msgid "Firmware is too big" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Flow Ctrl needs both CTS & RTS" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "For L8 colorspace, input bitmap must have 8 bits per pixel" msgstr "" @@ -1148,6 +1241,10 @@ msgstr "" msgid "I2C init error" msgstr "" +#: ports/analog/common-hal/busio/I2C.c +msgid "I2C needs SDA & SCL" +msgstr "" + #: ports/raspberrypi/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c msgid "I2C peripheral in use" @@ -1765,6 +1862,10 @@ msgstr "" msgid "Parameter error" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Parity must be ODD, EVEN, or NONE\n" +msgstr "" + #: ports/espressif/common-hal/audiobusio/__init__.c msgid "Peripheral in use" msgstr "" @@ -1902,6 +2003,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -1994,6 +2096,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2125,6 +2231,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2168,6 +2278,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" From b19a86111cd736c5a65a5bb5354bfba9e4e277b7 Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Sat, 5 Jul 2025 12:32:20 -0700 Subject: [PATCH 134/517] Fix error reporting on ports/analog BUSIO for PR 10413 - Use existing error messages from locale/circuitpython.pot for most error messages - Replace ConnectionError (networking specific) calls with others (usually RuntimeError) - Remove '\n' from mp error calls (since they add it already) - Remove some redundant validation code that repeats checks done by shared-bindings/common-hal/busio code. e.g. pin validation Signed-off-by: Brandon-Hurst --- ports/analog/common-hal/busio/I2C.c | 35 +++++---------- ports/analog/common-hal/busio/SPI.c | 5 ++- ports/analog/common-hal/busio/UART.c | 45 ++++++++++--------- ports/analog/peripherals/max32690/max32_i2c.c | 3 +- ports/analog/peripherals/max32690/max32_spi.c | 5 +-- .../analog/peripherals/max32690/max32_uart.c | 3 +- 6 files changed, 39 insertions(+), 57 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index c1b1d9efe1b17..b75d916c9faf0 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -52,6 +52,11 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, // Check for NULL Pointers && valid I2C settings assert(self); + /* NOTE: The validate_obj_is_free_pin() calls in shared-bindings/busio/I2C.c + will ensure that scl and sda are both pins and cannot be null + ref: https://github.com/adafruit/circuitpython/pull/10413 + */ + // Assign I2C ID based on pins temp = pinsToI2c(sda, scl); if (temp == -1) { @@ -65,25 +70,6 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, assert((self->i2c_id >= 0) && (self->i2c_id < NUM_I2C)); assert(!(i2c_active & (1 << self->i2c_id))); - // Init I2C as main / controller node (0x00 is ignored) - if ((scl != NULL) && (sda != NULL)) { - MXC_GPIO_Config(&i2c_maps[self->i2c_id]); - err = MXC_I2C_Init(self->i2c_regs, 1, 0x00); - if (err) { - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to init I2C. ERR: %d\n"), err); - } - err = MXC_I2C_SetFrequency(self->i2c_regs, frequency); - if (err < 0) { - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to set I2C frequency. ERR: %d\n"), err); - } - } else if (scl != NULL) { - mp_raise_NotImplementedError(MP_ERROR_TEXT("I2C needs SDA & SCL")); - } else if (sda != NULL) { - mp_raise_NotImplementedError(MP_ERROR_TEXT("I2C needs SDA & SCL")); - } else { - // Should not get here, as shared-bindings API should not call this way - } - // Attach I2C pins self->sda = sda; self->scl = scl; @@ -94,8 +80,8 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, i2c_active |= (1 << self->i2c_id); // Set the timeout to a default value - if (((timeout < 0.0) || (timeout > 100.0))) { - self->timeout = 1.0; + if (timeout > 100) { + self->timeout = 1; } else { self->timeout = timeout; } @@ -212,7 +198,6 @@ uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, } // Read into buffer from the device selected by address -// todo test uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { @@ -229,14 +214,14 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, }; ret = MXC_I2C_MasterTransaction(&rd_req); if (ret) { - mp_raise_RuntimeError(MP_ERROR_TEXT("ERROR during I2C Transaction\n")); + // Return I/O error + return MP_EIO; } return 0; } // Write the bytes from out_data to the device selected by address, -// todo test uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { @@ -253,7 +238,7 @@ uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, }; ret = MXC_I2C_MasterTransaction(&wr_rd_req); if (ret) { - mp_raise_RuntimeError(MP_ERROR_TEXT("ERROR during I2C Transaction\n")); + return MP_EIO; } return 0; diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index 999eb8490b0eb..95ad1851a59ed 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -91,7 +91,8 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, 1, 0x01, 1000000, spi_pins); MXC_GPIO_SetVSSEL(MXC_GPIO_GET_GPIO(sck->port), MXC_GPIO_VSSEL_VDDIOH, (sck->mask | miso->mask | mosi->mask | MXC_GPIO_PIN_0)); if (err) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to init SPI.\n")); + // NOTE: Reuse existing messages from locales/circuitpython.pot to save space + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_SPI); } } else { mp_raise_NotImplementedError(MP_ERROR_TEXT("SPI needs MOSI, MISO, and SCK")); @@ -170,7 +171,7 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self, clk_mode = MXC_SPI_CLKMODE_3; break; default: - mp_raise_ValueError(MP_ERROR_TEXT("CPOL / CPHA must both be either 0 or 1\n")); + // should not be reachable; validated in shared-bindings/busio/SPI.c return false; } diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index f781da8705545..80feebfb8f4b6 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -82,37 +82,37 @@ static volatile int uart_err; static uint8_t uart_never_reset_mask = 0; static busio_uart_obj_t *context; -static int isValidBaudrate(uint32_t baudrate) { +static bool isValidBaudrate(uint32_t baudrate) { switch (baudrate) { case UART_9600: - return 1; + return true; break; case UART_14400: - return 1; + return true; break; case UART_19200: - return 1; + return true; break; case UART_38400: - return 1; + return true; break; case UART_57600: - return 1; + return true; break; case UART_115200: - return 1; + return true; break; case UART_230400: - return 1; + return true; break; case UART_460800: - return 1; + return true; break; case UART_921600: - return 1; + return true; break; default: - return 0; + return false; break; } } @@ -126,7 +126,8 @@ static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) { case BUSIO_UART_PARITY_ODD: return MXC_UART_PARITY_ODD_0; default: - mp_raise_ValueError(MP_ERROR_TEXT("Parity must be ODD, EVEN, or NONE\n")); + // not reachable due to validation in shared-bindings/busio/SPI.c + return MXC_UART_PARITY_DISABLE; } } @@ -188,7 +189,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, if ((rx != NULL) && (tx != NULL)) { err = MXC_UART_Init(self->uart_regs, baudrate, MXC_UART_IBRO_CLK); if (err != E_NO_ERROR) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Failed to initialize UART.\n")); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_UART); } // attach & configure pins @@ -211,7 +212,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, common_hal_mcu_pin_claim(self->cts_pin); common_hal_mcu_pin_claim(self->rts_pin); } else if (cts || rts) { - mp_raise_ValueError(MP_ERROR_TEXT("Flow Ctrl needs both CTS & RTS")); + mp_raise_ValueError(MP_ERROR_TEXT("Both RX and TX required for flow control")); } // Set stop bits & data size @@ -338,7 +339,7 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, *errcode = err; MXC_UART_AbortAsync(self->uart_regs); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("\nERR: Error starting transaction: %d\n"), err); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("UART read error")); } // Wait for transaction completion or timeout @@ -350,11 +351,11 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, if (uart_status[self->uart_id] != UART_FREE) { MXC_UART_AbortAsync(self->uart_regs); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_RuntimeError(MP_ERROR_TEXT("\nERR: Uart transaction timed out.\n")); + mp_raise_RuntimeError(MP_ERROR_TEXT("UART transaction timeout")); } // Check for errors from the callback else if (uart_err != E_NO_ERROR) { - mp_printf(&mp_sys_stdout_print, "MAX32 ERR: %d\n", uart_err); + // todo: indicate error? MXC_UART_AbortAsync(self->uart_regs); } @@ -399,7 +400,7 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, *errcode = err; MXC_UART_AbortAsync(self->uart_regs); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Requested bus is busy\n")); + mp_raise_ValueError(MP_ERROR_TEXT("All UART peripherals are in use")); } // Wait for transaction completion or timeout @@ -409,7 +410,7 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, // Call the handler and abort if errors uart_err = MXC_UART_AsyncHandler(self->uart_regs); if (uart_err != E_NO_ERROR) { - mp_printf(&mp_sys_stdout_print, "MAX32 ERR: %d\n", uart_err); + // todo: indicate error? MXC_UART_AbortAsync(self->uart_regs); } } @@ -418,11 +419,11 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, if (uart_status[self->uart_id] != UART_FREE) { MXC_UART_AbortAsync(self->uart_regs); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_ConnectionError(MP_ERROR_TEXT("\nERR: Uart transaction timed out.\n")); + mp_raise_RuntimeError(MP_ERROR_TEXT("Uart transaction timed out.")); } // Check for errors from the callback else if (uart_err != E_NO_ERROR) { - mp_printf(&mp_sys_stdout_print, "MAX32 ERR: %d\n", uart_err); + // todo: indicate error? MXC_UART_AbortAsync(self->uart_regs); } @@ -438,7 +439,7 @@ void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrat if (isValidBaudrate(baudrate)) { self->baudrate = baudrate; } else { - mp_raise_ValueError(MP_ERROR_TEXT("Baudrate invalid. Must be a standard UART baudrate.\n")); + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_baudrate); } } diff --git a/ports/analog/peripherals/max32690/max32_i2c.c b/ports/analog/peripherals/max32690/max32_i2c.c index daf665bbee741..4b9ec8b4db264 100644 --- a/ports/analog/peripherals/max32690/max32_i2c.c +++ b/ports/analog/peripherals/max32690/max32_i2c.c @@ -70,7 +70,6 @@ int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl) { } #endif - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("ERR: Unable to find an I2C matching pins...\nSCL: port %d mask %d\nSDA: port %d mask %d\n"), - sda->port, sda->mask, scl->port, scl->mask); + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); return -1; } diff --git a/ports/analog/peripherals/max32690/max32_spi.c b/ports/analog/peripherals/max32690/max32_spi.c index f2594787598b2..c78fd64dbd705 100644 --- a/ports/analog/peripherals/max32690/max32_spi.c +++ b/ports/analog/peripherals/max32690/max32_spi.c @@ -39,9 +39,6 @@ int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, return i; } } - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("ERR: Unable to find an SPI matching pins... \ - \nMOSI: port %d mask %d\nMISO: port %d mask %d\n"), - mosi->port, mosi->mask, miso->port, miso->mask, - sck->port, sck->mask); + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); return -1; } diff --git a/ports/analog/peripherals/max32690/max32_uart.c b/ports/analog/peripherals/max32690/max32_uart.c index 5f7a8568fb426..7d37708797415 100644 --- a/ports/analog/peripherals/max32690/max32_uart.c +++ b/ports/analog/peripherals/max32690/max32_uart.c @@ -31,7 +31,6 @@ int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx) { return i; } } - mp_raise_RuntimeError_varg(MP_ERROR_TEXT("ERR: Unable to find a uart matching pins...\nTX: port %d mask %d\nRX: port %d mask %d\n"), - tx->port, tx->mask, rx->port, rx->mask); + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); return -1; } From 85410e22084c7b660301ba0812974d3bc3dc37ff Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Sat, 5 Jul 2025 12:38:42 -0700 Subject: [PATCH 135/517] Minor changes in ports/analog/ busio UART. - Use m_malloc_without_collect storage API change from CircuitPython - Remove err number for asynchronous read error; just report it Signed-off-by: Brandon-Hurst --- ports/analog/common-hal/busio/UART.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index 80feebfb8f4b6..71da89652af39 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -197,12 +197,8 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->rx_pin = rx; common_hal_mcu_pin_claim(self->tx_pin); common_hal_mcu_pin_claim(self->rx_pin); - } else if (tx != NULL) { - mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); - } else if (rx != NULL) { - mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); } else { - // Should not get here, as shared-bindings API should not call this way + mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); } if ((cts) && (rts)) { @@ -243,7 +239,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // Initialize ringbuffer if (receiver_buffer == NULL) { - self->ringbuf = gc_alloc(receiver_buffer_size, false); + self->ringbuf = m_malloc_without_collect(receiver_buffer_size, false); if (!ringbuf_alloc(self->ringbuf, receiver_buffer_size)) { m_malloc_fail(receiver_buffer_size); mp_raise_RuntimeError(MP_ERROR_TEXT("ERR: Could not init ringbuffer\n")); From f395fadeb59d4aa45632ac936fed6dd1507c734a Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Sat, 5 Jul 2025 12:44:05 -0700 Subject: [PATCH 136/517] Fix UART malloc for ports/analog BUSIO - Remove excess argument from UART ringbuf malloc call - Remove unused err and intfl0 in I2C driver to remove warnings Signed-off-by: Brandon-Hurst --- ports/analog/common-hal/busio/I2C.c | 4 ++-- ports/analog/common-hal/busio/UART.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index b75d916c9faf0..19c71034eea4c 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -47,7 +47,7 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) { - int temp, err = 0; + int temp = 0; // Check for NULL Pointers && valid I2C settings assert(self); @@ -113,7 +113,7 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { // Probe device in I2C bus bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { - uint32_t int_fl0, int_fl1; + uint32_t int_fl0; bool ret = 0; // Clear FIFOs & all interrupt flags diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index 71da89652af39..633ecb1684005 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -239,7 +239,7 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // Initialize ringbuffer if (receiver_buffer == NULL) { - self->ringbuf = m_malloc_without_collect(receiver_buffer_size, false); + self->ringbuf = m_malloc_without_collect(receiver_buffer_size); if (!ringbuf_alloc(self->ringbuf, receiver_buffer_size)) { m_malloc_fail(receiver_buffer_size); mp_raise_RuntimeError(MP_ERROR_TEXT("ERR: Could not init ringbuffer\n")); From e5437cd688120e1c7bdfc90a42afd16b9addf84b Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Sat, 5 Jul 2025 13:03:03 -0700 Subject: [PATCH 137/517] Commit locale changes from ports/analog BUSIO updates Signed-off-by: Brandon-Hurst --- locale/circuitpython.pot | 106 +++++++++------------------------------ 1 file changed, 24 insertions(+), 82 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 5b9e9afed76bf..a0477d71abc19 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -28,25 +28,6 @@ msgid "" "Code stopped by auto-reload. Reloading soon.\n" msgstr "" -#: ports/analog/common-hal/busio/UART.c -#, c-format -msgid "" -"\n" -"ERR: Error starting transaction: %d\n" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "" -"\n" -"ERR: Requested bus is busy\n" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "" -"\n" -"ERR: Uart transaction timed out.\n" -msgstr "" - #: supervisor/shared/safe_mode.c msgid "" "\n" @@ -149,6 +130,7 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "" @@ -177,6 +159,10 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "" + #: py/argcheck.c msgid "%q must be %d" msgstr "" @@ -517,7 +503,8 @@ msgstr "" msgid "All SPI peripherals are in use" msgstr "" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "" @@ -647,10 +634,6 @@ msgid "" "disable.\n" msgstr "" -#: ports/analog/common-hal/busio/UART.c -msgid "Baudrate invalid. Must be a standard UART baudrate.\n" -msgstr "" - #: ports/espressif/common-hal/canio/CAN.c msgid "Baudrate not supported by peripheral" msgstr "" @@ -672,6 +655,7 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "" @@ -740,10 +724,6 @@ msgstr "" msgid "CIRCUITPY drive could not be found or created." msgstr "" -#: ports/analog/common-hal/busio/SPI.c -msgid "CPOL / CPHA must both be either 0 or 1\n" -msgstr "" - #: ports/espressif/common-hal/espidf/__init__.c msgid "CRC or checksum was invalid" msgstr "" @@ -974,30 +954,6 @@ msgstr "" msgid "ERR: Could not init ringbuffer\n" msgstr "" -#: ports/analog/peripherals/max32690/max32_uart.c -#, c-format -msgid "" -"ERR: Unable to find a uart matching pins...\n" -"TX: port %d mask %d\n" -"RX: port %d mask %d\n" -msgstr "" - -#: ports/analog/peripherals/max32690/max32_i2c.c -#, c-format -msgid "" -"ERR: Unable to find an I2C matching pins...\n" -"SCL: port %d mask %d\n" -"SDA: port %d mask %d\n" -msgstr "" - -#: ports/analog/peripherals/max32690/max32_spi.c -#, c-format -msgid "" -"ERR: Unable to find an SPI matching pins... \n" -"MOSI: port %d mask %d\n" -"MISO: port %d mask %d\n" -msgstr "" - #: ports/analog/common-hal/busio/I2C.c msgid "ERROR during I2C Transaction\n" msgstr "" @@ -1095,19 +1051,6 @@ msgstr "" msgid "Failed to enable continuous" msgstr "" -#: ports/analog/common-hal/busio/I2C.c -#, c-format -msgid "Failed to init I2C. ERR: %d\n" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -msgid "Failed to init SPI.\n" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "Failed to initialize UART.\n" -msgstr "" - #: shared-module/audiomp3/MP3Decoder.c msgid "Failed to parse MP3 file" msgstr "" @@ -1121,11 +1064,6 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" -#: ports/analog/common-hal/busio/I2C.c -#, c-format -msgid "Failed to set I2C frequency. ERR: %d\n" -msgstr "" - #: ports/analog/common-hal/busio/SPI.c msgid "Failed to set SPI Clock Mode\n" msgstr "" @@ -1178,10 +1116,6 @@ msgstr "" msgid "Firmware is too big" msgstr "" -#: ports/analog/common-hal/busio/UART.c -msgid "Flow Ctrl needs both CTS & RTS" -msgstr "" - #: shared-bindings/bitmaptools/__init__.c msgid "For L8 colorspace, input bitmap must have 8 bits per pixel" msgstr "" @@ -1241,10 +1175,6 @@ msgstr "" msgid "I2C init error" msgstr "" -#: ports/analog/common-hal/busio/I2C.c -msgid "I2C needs SDA & SCL" -msgstr "" - #: ports/raspberrypi/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c msgid "I2C peripheral in use" @@ -1348,6 +1278,10 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1862,10 +1796,6 @@ msgstr "" msgid "Parameter error" msgstr "" -#: ports/analog/common-hal/busio/UART.c -msgid "Parity must be ODD, EVEN, or NONE\n" -msgstr "" - #: ports/espressif/common-hal/audiobusio/__init__.c msgid "Peripheral in use" msgstr "" @@ -2290,6 +2220,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -2326,6 +2264,10 @@ msgstr "" msgid "UUID value is not str, int or byte buffer" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Uart transaction timed out." +msgstr "" + #: ports/raspberrypi/common-hal/memorymap/AddressRange.c msgid "Unable to access unaligned IO register" msgstr "" From ed92610e506d4f63bcf28152fa10c0d8eedbc010 Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Thu, 10 Jul 2025 21:57:47 -0700 Subject: [PATCH 138/517] Reuse more existing error messages in ports/analog BUSIO - Reuse more messages in locales/circuitpython.pot in BUSIO - Remove UART write timeout - Refine some error handling for SPI initialization Signed-off-by: Brandon-Hurst --- locale/circuitpython.pot | 26 ++++----------------- ports/analog/common-hal/busio/I2C.c | 2 +- ports/analog/common-hal/busio/SPI.c | 15 +++++------- ports/analog/common-hal/busio/UART.c | 34 +++++++--------------------- 4 files changed, 19 insertions(+), 58 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index a0477d71abc19..1e167c30353dd 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -242,6 +242,7 @@ msgstr "" msgid "%q out of bounds" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -950,14 +951,6 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "" -#: ports/analog/common-hal/busio/UART.c -msgid "ERR: Could not init ringbuffer\n" -msgstr "" - -#: ports/analog/common-hal/busio/I2C.c -msgid "ERROR during I2C Transaction\n" -msgstr "" - #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c msgid "ESP-IDF memory allocation failed" @@ -1006,7 +999,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1065,15 +1058,7 @@ msgid "Failed to release mutex, err 0x%04x" msgstr "" #: ports/analog/common-hal/busio/SPI.c -msgid "Failed to set SPI Clock Mode\n" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -msgid "Failed to set SPI Frame Size\n" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -msgid "Failed to set SPI Frequency\n" +msgid "Failed to set SPI Clock Mode" msgstr "" #: ports/zephyr-cp/common-hal/wifi/Radio.c @@ -1374,6 +1359,7 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -2264,10 +2250,6 @@ msgstr "" msgid "UUID value is not str, int or byte buffer" msgstr "" -#: ports/analog/common-hal/busio/UART.c -msgid "Uart transaction timed out." -msgstr "" - #: ports/raspberrypi/common-hal/memorymap/AddressRange.c msgid "Unable to access unaligned IO register" msgstr "" diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index 19c71034eea4c..2c8f2e328eb1f 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -191,7 +191,7 @@ uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, }; ret = MXC_I2C_MasterTransaction(&wr_req); if (ret) { - mp_raise_RuntimeError(MP_ERROR_TEXT("ERROR during I2C Transaction\n")); + return MP_EIO; } return 0; diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index 95ad1851a59ed..516ae26a8cf69 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -177,17 +177,19 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self, ret = MXC_SPI_SetFrequency(self->spi_regs, baudrate); if (ret) { - mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Frequency\n")); + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q out of range"), MP_QSTR_baudrate); return false; } ret = MXC_SPI_SetDataSize(self->spi_regs, bits); - if (ret) { - mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Frame Size\n")); + if (ret == E_BAD_PARAM) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q out of range"), MP_QSTR_bits); return false; + } else if (ret == E_BAD_STATE) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid state")); } ret = MXC_SPI_SetMode(self->spi_regs, clk_mode); if (ret) { - mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Clock Mode\n")); + mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Clock Mode")); return false; } return true; @@ -246,11 +248,6 @@ bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t write_value) { int ret = 0; - // uint8_t tx_buffer[len] = {0x0}; - - // for (int i = 0; i < len; i++) { - // tx_buffer[i] = write_value; - // } mxc_spi_req_t rd_req = { .spi = self->spi_regs, diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c index 633ecb1684005..be7851f52a21c 100644 --- a/ports/analog/common-hal/busio/UART.c +++ b/ports/analog/common-hal/busio/UART.c @@ -24,11 +24,6 @@ * THE SOFTWARE. */ -/** TODO: - * - Fix readline issue - * -*/ - #if CIRCUITPY_BUSIO_UART #include "mpconfigport.h" @@ -242,19 +237,19 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, self->ringbuf = m_malloc_without_collect(receiver_buffer_size); if (!ringbuf_alloc(self->ringbuf, receiver_buffer_size)) { m_malloc_fail(receiver_buffer_size); - mp_raise_RuntimeError(MP_ERROR_TEXT("ERR: Could not init ringbuffer\n")); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to allocate %q buffer"), + MP_QSTR_UART); } } else { if (!(ringbuf_init(self->ringbuf, receiver_buffer, receiver_buffer_size))) { - mp_raise_RuntimeError(MP_ERROR_TEXT("ERR: Could not init ringbuffer\n")); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to allocate %q buffer"), + MP_QSTR_UART); } - ; } context = self; // Setup UART interrupt - NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); NVIC_SetPriority(MXC_UART_GET_IRQ(self->uart_id), UART_PRIORITY); @@ -351,7 +346,7 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, } // Check for errors from the callback else if (uart_err != E_NO_ERROR) { - // todo: indicate error? + mp_raise_RuntimeError(MP_ERROR_TEXT("UART read error")); MXC_UART_AbortAsync(self->uart_regs); } @@ -368,7 +363,6 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { int err; - uint32_t start_time = 0; static size_t bytes_remaining; // Setup globals & status tracking @@ -390,7 +384,6 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, uart_wr_req.callback = (void *)uartCallback; // Start the transaction - start_time = supervisor_ticks_ms64(); err = MXC_UART_TransactionAsync(&uart_wr_req); if (err != E_NO_ERROR) { *errcode = err; @@ -399,27 +392,16 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, mp_raise_ValueError(MP_ERROR_TEXT("All UART peripherals are in use")); } - // Wait for transaction completion or timeout - while ((uart_status[self->uart_id] != UART_FREE) && - (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { - + // Wait for transaction completion + while (uart_status[self->uart_id] != UART_FREE) { // Call the handler and abort if errors uart_err = MXC_UART_AsyncHandler(self->uart_regs); if (uart_err != E_NO_ERROR) { - // todo: indicate error? MXC_UART_AbortAsync(self->uart_regs); } } - - // If the timeout gets hit, abort and error out - if (uart_status[self->uart_id] != UART_FREE) { - MXC_UART_AbortAsync(self->uart_regs); - NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); - mp_raise_RuntimeError(MP_ERROR_TEXT("Uart transaction timed out.")); - } // Check for errors from the callback - else if (uart_err != E_NO_ERROR) { - // todo: indicate error? + if (uart_err != E_NO_ERROR) { MXC_UART_AbortAsync(self->uart_regs); } From 183a517ba1e4ab818904254e2c4bd932caf4f52a Mon Sep 17 00:00:00 2001 From: Brandon-Hurst Date: Mon, 22 Sep 2025 20:30:39 -0700 Subject: [PATCH 139/517] Fix I2C bug in ports/analog/busio - Add clear flags + Shutdown/Init to I2C constructor - Explicitly set frequency - Add failsafe in probe function if MST mode disabled (can happen in some error conditions) - Fix Makefile bug with missing "\" for source files Signed-off-by: Brandon-Hurst --- ports/analog/Makefile | 2 +- ports/analog/common-hal/busio/I2C.c | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ports/analog/Makefile b/ports/analog/Makefile index 4a758eff9c46d..0ae276985f7f4 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -125,7 +125,7 @@ SRC_MAX32 += \ $(PERIPH_SRC)/UART/uart_$(DIE_TYPE).c \ $(PERIPH_SRC)/UART/uart_revb.c \ $(PERIPH_SRC)/TRNG/trng_revb.c \ - $(PERIPH_SRC)/TRNG/trng_$(DIE_TYPE).c + $(PERIPH_SRC)/TRNG/trng_$(DIE_TYPE).c \ $(PERIPH_SRC)/I2C/i2c_$(DIE_TYPE).c \ $(PERIPH_SRC)/I2C/i2c_reva.c \ $(PERIPH_SRC)/SPI/spi_$(DIE_TYPE).c \ diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index 2c8f2e328eb1f..b3aeac76ce2d3 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -76,6 +76,16 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, common_hal_mcu_pin_claim(self->sda); common_hal_mcu_pin_claim(self->scl); + // Clear all flags + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); + + // Init as master, no slave address + MXC_I2C_Shutdown(self->i2c_regs); + MXC_I2C_Init(self->i2c_regs, 1, 0); + + // Set frequency arg (CircuitPython shared-bindings validate) + MXC_I2C_SetFrequency(self->i2c_regs, frequency); + // Indicate to this module that the I2C is active i2c_active |= (1 << self->i2c_id); @@ -116,6 +126,11 @@ bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { uint32_t int_fl0; bool ret = 0; + // If not in Master mode, error out (can happen in some error conditions) + if (!(self->i2c_regs->ctrl & MXC_F_I2C_CTRL_MST_MODE)) { + return false; + } + // Clear FIFOs & all interrupt flags MXC_I2C_ClearRXFIFO(self->i2c_regs); MXC_I2C_ClearTXFIFO(self->i2c_regs); @@ -175,7 +190,6 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } // Write data to the device selected by address -// todo test uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { @@ -221,7 +235,7 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, return 0; } -// Write the bytes from out_data to the device selected by address, +// Write the bytes from out_data to the device selected by address uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { From 29914f88b80954cfb59a6bbb6152af78e8e1a098 Mon Sep 17 00:00:00 2001 From: gychang Date: Tue, 23 Sep 2025 15:03:40 -0700 Subject: [PATCH 140/517] Remove neopixel_write workaround Turns out it was a workaround for a bad LED on one particular board. --- .../boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk | 4 ---- ports/espressif/common-hal/neopixel_write/__init__.c | 10 ---------- 2 files changed, 14 deletions(-) diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk index a233eb526fdbd..4b23a76ba6a15 100644 --- a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk @@ -8,7 +8,3 @@ CIRCUITPY_ESP_FLASH_FREQ = 80m CIRCUITPY_ESP_FLASH_SIZE = 4MB CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 - -ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD = 1 - -CFLAGS += -DESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD=$(ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD) diff --git a/ports/espressif/common-hal/neopixel_write/__init__.c b/ports/espressif/common-hal/neopixel_write/__init__.c index 945b309372ac7..d0d46a3179787 100644 --- a/ports/espressif/common-hal/neopixel_write/__init__.c +++ b/ports/espressif/common-hal/neopixel_write/__init__.c @@ -126,11 +126,6 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, // Update the next start to +2 ticks. It ensures that we've gone 300+ us. next_start_raw_ticks = port_get_raw_ticks(NULL) + 2; - #if defined(ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD) - // Hold the pin, deleting the channel seems to glitch pixels into turning off. - gpio_hold_en(digitalinout->pin->number); - #endif - // Free channel again rmt_del_encoder(encoder); rmt_disable(channel); @@ -138,9 +133,4 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t *digitalinout, CHECK_ESP_RESULT(result); // Swap pin back to GPIO mode gpio_set_direction(digitalinout->pin->number, GPIO_MODE_OUTPUT); - - #if defined(ESPRESSIF_ESP32C3_LYRA_STATUS_LED_HOLD) - // Release hold - gpio_hold_dis(digitalinout->pin->number); - #endif } From bb27b4010c27e47912741be88481e77d6d7c580b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 24 Sep 2025 12:35:08 -0400 Subject: [PATCH 141/517] Use more explicit names for i2c_id and spi_id --- ports/analog/common-hal/busio/I2C.c | 11 ++++------- ports/analog/common-hal/busio/SPI.c | 10 +++++----- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index b3aeac76ce2d3..336c04fe039f9 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -46,9 +46,6 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) { - - int temp = 0; - // Check for NULL Pointers && valid I2C settings assert(self); @@ -58,12 +55,12 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, */ // Assign I2C ID based on pins - temp = pinsToI2c(sda, scl); - if (temp == -1) { + int i2c_id = pinsToI2c(sda, scl); + if (i2c_id == -1) { return; } else { - self->i2c_id = temp; - self->i2c_regs = MXC_I2C_GET_I2C(temp); + self->i2c_id = i2c_id; + self->i2c_regs = MXC_I2C_GET_I2C(i2c_id); } // Check for valid I2C controller diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index 516ae26a8cf69..b4a519c37a72d 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -56,18 +56,18 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { - int temp, err = 0; + int err = 0; // Check for NULL Pointer assert(self); // Assign SPI ID based on pins - temp = pinsToSpi(mosi, miso, sck); - if (temp == -1) { + int spi_id = pinsToSpi(mosi, miso, sck); + if (spi_id == -1) { return; } else { - self->spi_id = temp; - self->spi_regs = MXC_SPI_GET_SPI(temp); + self->spi_id = spi_id; + self->spi_regs = MXC_SPI_GET_SPI(spi_id); } // Other pins default to true From caf65d3ac9ef9db9619eb4c1ed781339686df778 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 24 Sep 2025 20:11:32 +0200 Subject: [PATCH 142/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 42 ++++++++++++++++++++++++++++++++++++++++-- locale/el.po | 45 ++++++++++++++++++++++++++++++++++++++++----- locale/hi.po | 42 ++++++++++++++++++++++++++++++++++++++++-- locale/ko.po | 47 +++++++++++++++++++++++++++++++++++++++++------ locale/ru.po | 45 ++++++++++++++++++++++++++++++++++++++++----- locale/tr.po | 42 ++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 241 insertions(+), 22 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index eab928c2aaaf6..fa1cb2d40b738 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -141,6 +141,7 @@ msgstr "Index %q je mimo rozsah" msgid "%q indices must be integers, not %s" msgstr "Indexy %q musí být celá čísla, nikoli %s" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "Inicializace %q selhala" @@ -169,6 +170,10 @@ msgstr "Délka %q musí být <= %d" msgid "%q length must be >= %d" msgstr "Délka %q musí být >= %d" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q musí být %d" @@ -248,6 +253,7 @@ msgstr "%q musí být mocnina 2" msgid "%q out of bounds" msgstr "%q je mimo hranice" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -509,7 +515,8 @@ msgstr "Všechny RX FIFO jsou používány" msgid "All SPI peripherals are in use" msgstr "Všechny SPI periferie jsou používány" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Všechny UART periferie jsou používány" @@ -662,6 +669,7 @@ msgstr "Velikost bitmapy a počet bitů na hodnotu se musí shodovat" msgid "Boot device must be first (interface #0)." msgstr "Bootovací zařízení musí být první (rozhraní #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "RX a TX jsou vyžadovány pro kontrolu toku" @@ -1009,7 +1017,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "Chyba alokace %q bufferu" @@ -1067,6 +1075,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Nepodařilo se uvolnit mutex, err 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1271,6 +1283,10 @@ msgstr "Chyba přerušení." msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1363,6 +1379,7 @@ msgstr "Chybná velikost" msgid "Invalid socket for TLS" msgstr "Chybný soket pro TLS" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1925,6 +1942,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2017,6 +2035,10 @@ msgstr "Konfigurace SPI selhala" msgid "SPI init error" msgstr "Chyba inicializace SPI" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "SPI periferie je používána" @@ -2148,6 +2170,10 @@ msgstr "Čas je v minulosti." msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "Časový limit je příliš dlouhý: maximální limit je %d vteřin" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "V samplu je příliš mnoho kanálů" @@ -2192,6 +2218,10 @@ msgstr "De-inicializace UART" msgid "UART init" msgstr "Inicializace UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "UART periférie je používána" @@ -2200,6 +2230,14 @@ msgstr "UART periférie je používána" msgid "UART re-init" msgstr "Opětovná inicializace UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "Zápis na UART" diff --git a/locale/el.po b/locale/el.po index 871ce82f6725c..91624ceb08672 100644 --- a/locale/el.po +++ b/locale/el.po @@ -145,6 +145,7 @@ msgstr "%q δείκτης εκτός εμβέλειας" msgid "%q indices must be integers, not %s" msgstr "%q δείκτες πρέπει να είναι ακέραιοι, όχι %s" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "%q εκκίνηση απέτυχε" @@ -173,6 +174,10 @@ msgstr "%q μήκος πρέπει να είναι <= %d" msgid "%q length must be >= %d" msgstr "%q μήκος πρέπει να είναι >= %d" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "%q μετακινήθηκε από το %q στο %q" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q πρέπει να είναι %d" @@ -252,6 +257,7 @@ msgstr "%q πρέπει να είναι δύναμη του 2" msgid "%q out of bounds" msgstr "%q εκτός ορίων" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -513,7 +519,8 @@ msgstr "Όλα τα RX FIFOs είναι σε χρήση" msgid "All SPI peripherals are in use" msgstr "Όλα τα SPI περιφεριακά είναι σε χρήση" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Όλα τα UART περιφεριακά ειναι σε χρήση" @@ -666,6 +673,7 @@ msgstr "Το μέγεθος του bitmap και τα bits ανα τιμή πρ msgid "Boot device must be first (interface #0)." msgstr "Η συσκευή εκκίνησης πρέπει να επιλεχθεί πρώτα (διεπαφή #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Και RX και TX απαιτούνται για έλεγχο flow" @@ -1017,7 +1025,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1075,6 +1083,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1277,6 +1289,10 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1369,6 +1385,7 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1930,6 +1947,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2022,6 +2040,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2153,6 +2175,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2196,6 +2222,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2204,6 +2234,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -4545,9 +4583,6 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" -#~ msgid "%q moved from %q to %q" -#~ msgstr "%q μετακινήθηκε από το %q στο %q" - #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c απαιτεί ακέραιο ή χαρακτήρα" diff --git a/locale/hi.po b/locale/hi.po index f8408f6c119a5..1ab48338ebaba 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -132,6 +132,7 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "" @@ -160,6 +161,10 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "" + #: py/argcheck.c msgid "%q must be %d" msgstr "" @@ -239,6 +244,7 @@ msgstr "" msgid "%q out of bounds" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -500,7 +506,8 @@ msgstr "" msgid "All SPI peripherals are in use" msgstr "" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "" @@ -651,6 +658,7 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "" @@ -993,7 +1001,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1051,6 +1059,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1253,6 +1265,10 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1345,6 +1361,7 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1904,6 +1921,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -1996,6 +2014,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2127,6 +2149,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2170,6 +2196,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2178,6 +2208,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 4a74ec9003488..35bfed4de08e9 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -143,6 +143,7 @@ msgstr "%q 인덱스 범위를 벗어났습니다" msgid "%q indices must be integers, not %s" msgstr "%q 인덱스는 %s 가 아닌 정수 여야합니다" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "%q 초기화 실패" @@ -171,6 +172,11 @@ msgstr "%q 길이는 <= %d>여야 합니다" msgid "%q length must be >= %d" msgstr "%q 길이는 >= %d이어야 합니다" +#: py/runtime.c +#, fuzzy +msgid "%q moved from %q to %q" +msgstr "%q가 %q에서 %q로 이동했습니다" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q는 %d이어야 합니다" @@ -256,6 +262,7 @@ msgstr "%q는 2의 거듭제곱이어야 합니다" msgid "%q out of bounds" msgstr "%q가 경계를 벗어남" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -531,7 +538,8 @@ msgstr "모든 RX FIFOs가 사용 중입니다" msgid "All SPI peripherals are in use" msgstr "사용중인 모든 SPI주변 기기" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "사용중인 모든 UART주변 기기" @@ -692,6 +700,7 @@ msgstr "비트맵 크기와 값 당 비트가 일치해야 합니다" msgid "Boot device must be first (interface #0)." msgstr "부팅 장치는 첫 번째(인터페이스 #0)여야 합니다." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "플로우 제어에 RX와 TX가 모두 필요합니다" @@ -1041,7 +1050,7 @@ msgstr "" "서비스 TXT 레코드를 추가하는 것에 실패했습니다; txt_records에서 비문자열 또" "는 바이트가 발견되었습니다" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "%q 버퍼 할당에 실패했습니다" @@ -1099,6 +1108,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "뮤텍스 해제에 실패했습니다, 오류 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1304,6 +1317,10 @@ msgstr "인터럽트 오류." msgid "Interrupted by output function" msgstr "출력 함수로 인해 종료되었다" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1396,6 +1413,7 @@ msgstr "잘못된 크기" msgid "Invalid socket for TLS" msgstr "TLS에 대한 잘못된 소켓" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1978,6 +1996,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2070,6 +2089,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2201,6 +2224,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2244,6 +2271,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2252,6 +2283,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -4594,10 +4633,6 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" -#, fuzzy -#~ msgid "%q moved from %q to %q" -#~ msgstr "%q가 %q에서 %q로 이동했습니다" - #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c 전수(int)또는 캐릭터(char)필요합니다" diff --git a/locale/ru.po b/locale/ru.po index 24b880972cb42..af7c8bca194d2 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -145,6 +145,7 @@ msgstr "Индекс %q вне диапазона" msgid "%q indices must be integers, not %s" msgstr "Индексы %q должны быть целыми числами, а не %s" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "Инициализация %q не удалась" @@ -173,6 +174,10 @@ msgstr "Длинна %q должна быть <= %d" msgid "%q length must be >= %d" msgstr "Длинна %q должна быть >= %d" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "%q переместился из %q в %q" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q должно быть %d" @@ -252,6 +257,7 @@ msgstr "%q должен быть во 2-й степени" msgid "%q out of bounds" msgstr "%q за пределом" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -513,7 +519,8 @@ msgstr "Все RX FIFO уже используются" msgid "All SPI peripherals are in use" msgstr "Все периферийные устройства SPI уже используются" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Все периферийные устройства UART уже используются" @@ -667,6 +674,7 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "Загрузочное устройство должно быть первым (интерфейс #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Для управления потоком требуется как RX так и TX" @@ -1022,7 +1030,7 @@ msgstr "" "Не удалось добавить служебную TXT-запись; в txt_records обнаружена нестрока " "или байт" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "Не удалось выделить буфер %q" @@ -1080,6 +1088,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Не удалось освободить mutex, ошибка 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1292,6 +1304,10 @@ msgstr "Прерванная ошибка." msgid "Interrupted by output function" msgstr "Прерывается функцией выхода" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1384,6 +1400,7 @@ msgstr "Неверный размер" msgid "Invalid socket for TLS" msgstr "Неверный сокет для TLS" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1955,6 +1972,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2047,6 +2065,10 @@ msgstr "Сбой конфигурации SPI" msgid "SPI init error" msgstr "Ошибка инициализации SPI" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "Используемое периферийное устройство SPI" @@ -2183,6 +2205,10 @@ msgstr "Время в прошлом." msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "Таймаут слишком длинный: максимальная длина таймаута %d секунд" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "Слишком много каналов в выборке" @@ -2226,6 +2252,10 @@ msgstr "Деинициализация UART" msgid "UART init" msgstr "Инициализация UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "Используемое периферийное устройство UART" @@ -2234,6 +2264,14 @@ msgstr "Используемое периферийное устройство U msgid "UART re-init" msgstr "Повторная инициализация UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "Запись UART" @@ -4612,9 +4650,6 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" -#~ msgid "%q moved from %q to %q" -#~ msgstr "%q переместился из %q в %q" - #~ msgid "start/end indices" #~ msgstr "Начальные/конечные индексы" diff --git a/locale/tr.po b/locale/tr.po index f51ad3e8c12ee..758137eae21f8 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -143,6 +143,7 @@ msgstr "%q indeksi aralık dışında" msgid "%q indices must be integers, not %s" msgstr "%q indeksleri integer olmalı, %s değil" +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-module/bitbangio/SPI.c msgid "%q init failed" msgstr "%q init başarısız oldu" @@ -171,6 +172,10 @@ msgstr "%q boyutu <= %d olmalıdır" msgid "%q length must be >= %d" msgstr "%q boyutu >= %d olmalıdır" +#: py/runtime.c +msgid "%q moved from %q to %q" +msgstr "" + #: py/argcheck.c msgid "%q must be %d" msgstr "%q, %d olmalıdır" @@ -250,6 +255,7 @@ msgstr "%q, 2'nin kuvveti olmalıdır" msgid "%q out of bounds" msgstr "%q sınırların dışında" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c @@ -511,7 +517,8 @@ msgstr "Tüm RX FIFO'ları kullanımda" msgid "All SPI peripherals are in use" msgstr "Tüm SPI çevre birimleri kullanımda" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Tüm UART çevre birimleri kullanımda" @@ -664,6 +671,7 @@ msgstr "Bitmap boyutu ve bit başına değer uyuşmalı" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Hem RX hem de TX akış kontrolü için gerekli" @@ -1007,7 +1015,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1065,6 +1073,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Muteks serbest bırakılamadı, err 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1271,6 +1283,10 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c @@ -1364,6 +1380,7 @@ msgstr "Geçersiz boyut" msgid "Invalid socket for TLS" msgstr "TLS için geçersiz soket" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" @@ -1926,6 +1943,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2018,6 +2036,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2149,6 +2171,10 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" @@ -2192,6 +2218,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2200,6 +2230,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" From 34725676049ff52fbb5a34090aabc7640cd8a01e Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 25 Sep 2025 11:32:05 -0400 Subject: [PATCH 143/517] use gc_ptr_on_heap() instead of checking if gc_bytes is zero --- main.c | 2 +- ports/espressif/common-hal/_bleio/Characteristic.c | 2 +- ports/nordic/common-hal/_bleio/Characteristic.c | 2 +- ports/nordic/common-hal/_bleio/Service.c | 2 +- ports/nordic/common-hal/busio/UART.c | 4 ++-- py/circuitpy_mpconfig.h | 8 ++++++++ py/gc.c | 2 +- py/gc.h | 2 +- shared-bindings/_bleio/Adapter.c | 2 +- supervisor/shared/usb/usb_msc_flash.c | 2 +- 10 files changed, 18 insertions(+), 10 deletions(-) diff --git a/main.c b/main.c index 6c8e9c8804906..78148f2cb019c 100644 --- a/main.c +++ b/main.c @@ -214,7 +214,7 @@ static void stop_mp(void) { mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); // Unmount all heap allocated vfs mounts. - while (gc_nbytes(vfs) > 0) { + while (gc_ptr_on_heap(vfs)) { vfs = vfs->next; } MP_STATE_VM(vfs_mount_table) = vfs; diff --git a/ports/espressif/common-hal/_bleio/Characteristic.c b/ports/espressif/common-hal/_bleio/Characteristic.c index 7e917d6334b6b..805c6d160f325 100644 --- a/ports/espressif/common-hal/_bleio/Characteristic.c +++ b/ports/espressif/common-hal/_bleio/Characteristic.c @@ -120,7 +120,7 @@ void common_hal_bleio_characteristic_deinit(bleio_characteristic_obj_t *self) { return; } if (self->current_value != NULL) { - if (gc_nbytes(self->current_value) > 0) { + if (gc_ptr_on_heap(self->current_value)) { m_free(self->current_value); } else { port_free(self->current_value); diff --git a/ports/nordic/common-hal/_bleio/Characteristic.c b/ports/nordic/common-hal/_bleio/Characteristic.c index 1975c2c3d79ce..51335be9e59be 100644 --- a/ports/nordic/common-hal/_bleio/Characteristic.c +++ b/ports/nordic/common-hal/_bleio/Characteristic.c @@ -80,7 +80,7 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, // to allocate. self->initial_value_len = initial_value_bufinfo->len; if (gc_alloc_possible()) { - if (gc_nbytes(initial_value_bufinfo->buf) > 0) { + if (gc_ptr_on_heap(initial_value_bufinfo->buf)) { uint8_t *initial_value = m_malloc_without_collect(self->initial_value_len); memcpy(initial_value, initial_value_bufinfo->buf, self->initial_value_len); self->initial_value = initial_value; diff --git a/ports/nordic/common-hal/_bleio/Service.c b/ports/nordic/common-hal/_bleio/Service.c index edd67a5fe739d..1bd75f8a48114 100644 --- a/ports/nordic/common-hal/_bleio/Service.c +++ b/ports/nordic/common-hal/_bleio/Service.c @@ -131,7 +131,7 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, BLE_GAP_CONN_SEC_MODE_SET_OPEN(&user_desc_md.read_perm); // If the description is on the Python heap, then have the SD copy it. If not, assume it's // static and will live for longer than the SD. - user_desc_md.vloc = gc_nbytes(user_description) > 0 ? BLE_GATTS_VLOC_STACK : BLE_GATTS_VLOC_USER; + user_desc_md.vloc = gc_ptr_on_heap(user_description) ? BLE_GATTS_VLOC_STACK : BLE_GATTS_VLOC_USER; char_md.p_user_desc_md = &user_desc_md; char_md.p_char_user_desc = (const uint8_t *)user_description; char_md.char_user_desc_max_size = strlen(user_description); diff --git a/ports/nordic/common-hal/busio/UART.c b/ports/nordic/common-hal/busio/UART.c index 0dfe6ae3f5de2..6939486a5e673 100644 --- a/ports/nordic/common-hal/busio/UART.c +++ b/ports/nordic/common-hal/busio/UART.c @@ -122,7 +122,7 @@ void uart_reset(void) { void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { // Don't never reset objects on the heap. - if (gc_alloc_possible() && gc_nbytes(self) > 0) { + if (gc_alloc_possible() && gc_ptr_on_heap(self)) { return; } for (size_t i = 0; i < MP_ARRAY_SIZE(nrfx_uartes); i++) { @@ -346,7 +346,7 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, RUN_BACKGROUND_TASKS; } - if (!nrfx_is_in_ram(data) && gc_alloc_possible() && gc_nbytes(tx_buf) > 0) { + if (!nrfx_is_in_ram(data) && gc_alloc_possible() && gc_ptr_on_heap(tx_buf)) { gc_free(tx_buf); } diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 3ca06f7a0fd3a..127baa51ffc57 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -513,6 +513,14 @@ void background_callback_run_all(void); // USB settings +#ifndef CIRCUITPY_SDCARD_USB +#define CIRCUITPY_SDCARD_USB (1) +#endif + +#if CIRCUITPY_SDCARD_USB && !(CIRCUITPY_SDCARDIO) +#error CIRCUITPY_SDCARD_USB requires CIRCUITPY_SDCARDIO +#endif + // Debug level for TinyUSB. Only outputs over debug UART so it doesn't cause // additional USB logging. #ifndef CIRCUITPY_DEBUG_TINYUSB diff --git a/py/gc.c b/py/gc.c index 2cf4dbb64a83b..c6da81d495c10 100644 --- a/py/gc.c +++ b/py/gc.c @@ -446,7 +446,7 @@ bool gc_is_locked(void) { } // CIRCUITPY-CHANGE: additional function -bool gc_ptr_on_heap(void *ptr) { +bool gc_ptr_on_heap(const void *ptr) { for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { if (ptr >= (void *)area->gc_pool_start // must be above start of pool && ptr < (void *)area->gc_pool_end) { // must be below end of pool diff --git a/py/gc.h b/py/gc.h index ebc32b080fb47..0752478d1f286 100644 --- a/py/gc.h +++ b/py/gc.h @@ -87,7 +87,7 @@ void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move); // CIRCUITPY-CHANGE // True if the pointer is on the MP heap. Doesn't require that it is the start // of a block. -bool gc_ptr_on_heap(void *ptr); +bool gc_ptr_on_heap(const void *ptr); typedef struct _gc_info_t { size_t total; diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c index 92b910c8b2e0f..839b8b19addfa 100644 --- a/shared-bindings/_bleio/Adapter.c +++ b/shared-bindings/_bleio/Adapter.c @@ -339,7 +339,7 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args if (args[ARG_prefixes].u_obj != MP_OBJ_NULL) { mp_get_buffer_raise(args[ARG_prefixes].u_obj, &prefix_bufinfo, MP_BUFFER_READ); // An empty buffer may not be on the heap, but that doesn't matter. - if (prefix_bufinfo.len > 0 && gc_nbytes(prefix_bufinfo.buf) == 0) { + if (prefix_bufinfo.len > 0 && !gc_ptr_on_heap(prefix_bufinfo.buf)) { mp_raise_ValueError(MP_ERROR_TEXT("Prefix buffer must be on the heap")); } } diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index 0b02faa5c18cb..4a23a5ec6c6c8 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -132,7 +132,7 @@ static fs_user_mount_t *get_vfs(int lun) { if (lun == SAVES_LUN) { const char *path_under_mount; fs_user_mount_t *saves = filesystem_for_path("/saves", &path_under_mount); - if (saves != root && (saves->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0 && gc_nbytes(saves) == 0) { + if (saves != root && (saves->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0 && !gc_ptr_on_heap(saves)) { return saves; } } From 3e1441e1926ffa26c1f2fe0d3930a1054d625b31 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 25 Sep 2025 18:12:33 -0400 Subject: [PATCH 144/517] allow enable/disable of presenting SD card to USB; suppress auto-reload when writing dirty bit --- ports/atmel-samd/mpconfigport.h | 3 ++ ports/cxd56/mpconfigport.h | 3 ++ ports/espressif/mpconfigport.h | 5 ++++ shared-module/sdcardio/__init__.c | 2 +- supervisor/shared/filesystem.c | 3 +- supervisor/shared/usb/usb_msc_flash.c | 40 +++++++++++++++++++++------ 6 files changed, 44 insertions(+), 12 deletions(-) diff --git a/ports/atmel-samd/mpconfigport.h b/ports/atmel-samd/mpconfigport.h index 087e0bc7d6820..e953ea0fc50d4 100644 --- a/ports/atmel-samd/mpconfigport.h +++ b/ports/atmel-samd/mpconfigport.h @@ -11,6 +11,9 @@ // Definitions that control circuitpy_mpconfig.h: +// On SAMD, presenting the SD card as a second LUN causes USB disconnect. This needs to be fixed eventually. +#define CIRCUITPY_SDCARD_USB (0) + //////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef SAMD21 diff --git a/ports/cxd56/mpconfigport.h b/ports/cxd56/mpconfigport.h index 3bcb252868786..f0a57248bebef 100644 --- a/ports/cxd56/mpconfigport.h +++ b/ports/cxd56/mpconfigport.h @@ -8,6 +8,9 @@ #define MICROPY_PY_SYS_PLATFORM "CXD56" +// SD card socket on board is configured for sdioio, which is not supported for automatic USB presentation. +#define CIRCUITPY_SDCARD_USB (0) + // 64kiB stack #define CIRCUITPY_DEFAULT_STACK_SIZE (0x10000) diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 712eb67f1f42a..8fdc1fad94d19 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -17,6 +17,11 @@ #define CIRCUITPY_DIGITALIO_HAVE_INPUT_ONLY (1) +// // Present SD card as USB MSC device by default +// #ifndef CIRCUITPY_SDCARD_USB +// #define CIRCUITPY_SDCARD_USB (1) +// #endif + #include "py/circuitpy_mpconfig.h" #define MICROPY_NLR_SETJMP (1) diff --git a/shared-module/sdcardio/__init__.c b/shared-module/sdcardio/__init__.c index a49a1506712db..29c890c6870c1 100644 --- a/shared-module/sdcardio/__init__.c +++ b/shared-module/sdcardio/__init__.c @@ -123,5 +123,5 @@ void automount_sd_card(void) { sdcard_vfs->next = MP_STATE_VM(vfs_mount_table); MP_STATE_VM(vfs_mount_table) = sdcard_vfs; _mounted = true; - #endif + #endif // DEFAULT_SD_CARD_DETECT } diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index 0df600e77fd2f..df70d963de6f1 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -148,8 +148,7 @@ bool filesystem_init(bool create_allowed, bool force_create) { res = f_mkdir(&circuitpy->fatfs, "/sd"); #if CIRCUITPY_FULL_BUILD MAKE_FILE_WITH_OPTIONAL_CONTENTS(&circuitpy->fatfs, "/sd/placeholder.txt", - "SD cards mounted at /sd will hide this file from Python." - " SD cards are not visible via USB CIRCUITPY.\n"); + "SD cards mounted at /sd will hide this file from Python.\n"); #endif #endif diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index 4a23a5ec6c6c8..e4e4801de9c88 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -45,6 +45,11 @@ static bool ejected[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = true}; static bool eject_once[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; static bool locked[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; +// Set to true if a write was in a file data or metadata area, +// as opposed to in the filesystem metadata area (e.g., dirty bit). +// Used to determine if an auto-reload is warranted. +static bool content_write[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; + #include "tusb.h" static const uint8_t usb_msc_descriptor_template[] = { @@ -141,6 +146,7 @@ static fs_user_mount_t *get_vfs(int lun) { if (lun == SDCARD_LUN) { const char *path_under_mount; fs_user_mount_t *sdcard = filesystem_for_path("/sd", &path_under_mount); + // If "/sd" is on the root filesystem, nothing has been mounted there. if (sdcard != root && (sdcard->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0) { return sdcard; } else { @@ -290,16 +296,19 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t * if (vfs == NULL) { return -1; } + disk_write(vfs, buffer, lba, block_count); // Since by getting here we assume the mount is read-only to - // MicroPython let's update the cached FatFs sector if it's the one + // CircuitPython let's update the cached FatFs sector if it's the one // we just wrote. + if #if FF_MAX_SS != FF_MIN_SS - if (vfs->fatfs.ssize == MSC_FLASH_BLOCK_SIZE) { + (vfs->fatfs.ssize == MSC_FLASH_BLOCK_SIZE) #else // The compiler can optimize this away. - if (FF_MAX_SS == FILESYSTEM_BLOCK_SIZE) { - #endif + (FF_MAX_SS == FILESYSTEM_BLOCK_SIZE) + #endif + { if (lba == vfs->fatfs.winsect && lba > 0) { memcpy(vfs->fatfs.win, buffer + MSC_FLASH_BLOCK_SIZE * (vfs->fatfs.winsect - lba), @@ -307,17 +316,30 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t * } } + // A write to an lba below fatbase is in the filesystem metadata (BPB) area or the "Reserved Region", + // and is probably setting or clearing the dirty bit. This should not trigger auto-reload. + // All other writes will trigger auto-reload. + if (lba >= vfs->fatfs.fatbase) { + content_write[lun] = true; + } + return block_count * MSC_FLASH_BLOCK_SIZE; } // Callback invoked when WRITE10 command is completed (status received and accepted by host). // used to flush any pending cache. void tud_msc_write10_complete_cb(uint8_t lun) { - (void)lun; - - // This write is complete; initiate an autoreload. autoreload_resume(AUTORELOAD_SUSPEND_USB); - autoreload_trigger(); + + // This write is complete; initiate an autoreload if this was a file data or metadata write, + // not just a dirty-bit write. + if (content_write[lun] && + // Fast path: lun == 0 is CIRCUITPY, which can always trigger auto-reload if enabled. + // Don't autoreload if this lun was mounted by the user: that will cause a VM stop and an unmount. + (lun == 0 || !gc_ptr_on_heap(get_vfs(lun)))) { + autoreload_trigger(); + content_write[lun] = false; + } } // Invoked when received SCSI_CMD_INQUIRY @@ -337,7 +359,7 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { return false; } - #if CIRCUITPY_SDCARDIO + #if CIRCUITPY_SDCARD_USB if (lun == SDCARD_LUN) { automount_sd_card(); } From f143e1d164c7e3b3ff5aa5dfcd8362d893e79862 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 25 Sep 2025 23:28:00 -0400 Subject: [PATCH 145/517] ports/espressif/mpconfigport.h: remove commented code --- ports/espressif/mpconfigport.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 8fdc1fad94d19..712eb67f1f42a 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -17,11 +17,6 @@ #define CIRCUITPY_DIGITALIO_HAVE_INPUT_ONLY (1) -// // Present SD card as USB MSC device by default -// #ifndef CIRCUITPY_SDCARD_USB -// #define CIRCUITPY_SDCARD_USB (1) -// #endif - #include "py/circuitpy_mpconfig.h" #define MICROPY_NLR_SETJMP (1) From d5ca68573dc230eec5f593245b5e2d2cebe2d3f9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 25 Sep 2025 23:41:31 -0400 Subject: [PATCH 146/517] fix CIRCUITPY_SDCARD_USB default --- py/circuitpy_mpconfig.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 127baa51ffc57..541c0420a6e80 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -514,7 +514,11 @@ void background_callback_run_all(void); // USB settings #ifndef CIRCUITPY_SDCARD_USB -#define CIRCUITPY_SDCARD_USB (1) +#if CIRCUITPY_USB_DEVICE +#define CIRCUITPY_SDCARD_USB (CIRCUITPY_SDCARDIO && CIRCUITPY_USB_MSC) +#else +#define CIRCUITPY_SDCARD_USB (0) +#endif #endif #if CIRCUITPY_SDCARD_USB && !(CIRCUITPY_SDCARDIO) From edd4532bf566ffae058acaf90e6c1eda1b24c1ec Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 26 Sep 2025 00:13:11 -0400 Subject: [PATCH 147/517] document SD card USB presentation --- docs/workflows.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/workflows.md b/docs/workflows.md index 761504144d8b9..84d7530e2fe99 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -44,6 +44,12 @@ A few boards have SD card automounting. (This is based on the ``DEFAULT_SD`` set ``mpconfigboard.h``.) The card is writable from CircuitPython by default and read-only to the host. `storage.remount()` can be used to remount the drive to the host as read-write. +On most other boards, except for ``atmel-samd`` boards, an SD card mounted in user code +at ``/sd`` will become visible after a few seconds on the attached host computer, as an +additional drive besides CIRCUITPY and (if present) CPSAVES. It will present with the volume +label on the SD card. Depending on the host operating system settings, the drive may or may not be +auto-mounted on the host. Host writes to drives mounted by user code will not trigger a reload. + ### CDC serial CircuitPython exposes one CDC USB interface for CircuitPython serial. This is a standard serial USB interface. From 9c9252c7100771d7b03eac4eb95ef89e5fbd9095 Mon Sep 17 00:00:00 2001 From: Manjunath CV Date: Sun, 28 Sep 2025 12:56:26 +0530 Subject: [PATCH 148/517] Added Board WeAct Studio RP2350B Core --- locale/circuitpython.pot | 4 -- .../boards/weact_studio_rp2350b_core/board.c | 9 +++ .../weact_studio_rp2350b_core/mpconfigboard.h | 10 +++ .../mpconfigboard.mk | 12 ++++ .../pico-sdk-configboard.h | 7 ++ .../boards/weact_studio_rp2350b_core/pins.c | 67 +++++++++++++++++++ 6 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 ports/raspberrypi/boards/weact_studio_rp2350b_core/board.c create mode 100644 ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h create mode 100644 ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk create mode 100644 ports/raspberrypi/boards/weact_studio_rp2350b_core/pico-sdk-configboard.h create mode 100644 ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 1e167c30353dd..e1c235252fb35 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -159,10 +159,6 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" -#: py/runtime.c -msgid "%q moved from %q to %q" -msgstr "" - #: py/argcheck.c msgid "%q must be %d" msgstr "" diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/board.c b/ports/raspberrypi/boards/weact_studio_rp2350b_core/board.c new file mode 100644 index 0000000000000..e6a868ab21226 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h new file mode 100644 index 0000000000000..80d557cdc25b6 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h @@ -0,0 +1,10 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "WeAct Studio RP2350B Core" +#define MICROPY_HW_MCU_NAME "rp2350b" + +//#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO47) diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk new file mode 100644 index 0000000000000..ceef6b4846d94 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk @@ -0,0 +1,12 @@ +USB_VID = 0x2E8A +USB_PID = 0x000F +USB_PRODUCT = "RP2350B Core" +USB_MANUFACTURER = "WeAct Studio" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = B +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" + +CIRCUITPY__EVE = 1 diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/pico-sdk-configboard.h b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pico-sdk-configboard.h new file mode 100644 index 0000000000000..66b57dfd13dc2 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pico-sdk-configboard.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c new file mode 100644 index 0000000000000..989855a3843a3 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c @@ -0,0 +1,67 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_GP23), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO23) }, + + { MP_ROM_QSTR(MP_QSTR_GP24), MP_ROM_PTR(&pin_GPIO24) }, + + { MP_ROM_QSTR(MP_QSTR_GP25), MP_ROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO25) }, + + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_GP30), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_GP31), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_GP32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_GP33), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_GP34), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_GP35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_GP36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_GP37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_GP38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_GP39), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_GP40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_GP41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_GP42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_GP43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_GP44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_GP45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_GP46), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_GP47), MP_ROM_PTR(&pin_GPIO47) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From c3b6fb34f74833baf97c020de2095f4d8ac823f1 Mon Sep 17 00:00:00 2001 From: Manjunath CV Date: Sun, 28 Sep 2025 13:57:42 +0530 Subject: [PATCH 149/517] Added Board WeAct Studio RP2350B Core --- .../boards/weact_studio_rp2350b_core/mpconfigboard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h index 80d557cdc25b6..95a81334c73d5 100644 --- a/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h @@ -7,4 +7,4 @@ #define MICROPY_HW_BOARD_NAME "WeAct Studio RP2350B Core" #define MICROPY_HW_MCU_NAME "rp2350b" -//#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO47) +#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO0) From 3d22d521fe8d5a6c62adb1857b838166d1cf747f Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 29 Sep 2025 20:15:51 -0400 Subject: [PATCH 150/517] Prevent both host and CircuitPython rw access to mounted filesystems --- py/circuitpy_mpconfig.h | 3 +++ shared-bindings/storage/__init__.c | 10 +++++++++- shared-module/storage/__init__.c | 5 +++++ supervisor/shared/filesystem.c | 8 ++++---- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 27d4e97ad4d45..2754d57b01c9b 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -345,6 +345,9 @@ typedef long mp_off_t; #endif +// For easy debugging printf's. +#define PLAT_PRINTF(...) mp_printf(&mp_plat_print, __VA_ARGS__) + #if MICROPY_PY_ASYNC_AWAIT && !CIRCUITPY_TRACEBACK #error CIRCUITPY_ASYNCIO requires CIRCUITPY_TRACEBACK #endif diff --git a/shared-bindings/storage/__init__.c b/shared-bindings/storage/__init__.c index ee0fb4ab53c3a..93c360d564e2f 100644 --- a/shared-bindings/storage/__init__.c +++ b/shared-bindings/storage/__init__.c @@ -54,15 +54,23 @@ static mp_obj_t storage_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t // get the mount point const char *mnt_str = mp_obj_str_get_str(args[ARG_mount_path].u_obj); + + mp_obj_t vfs_obj = args[ARG_filesystem].u_obj; + + // Currently, the only supported filesystem is VfsFat. + mp_arg_validate_type(vfs_obj, &mp_fat_vfs_type, MP_QSTR_filesystem); + + // Add this back if/when we start supporting other filesystems. + #if 0 // Make sure we're given an object we can mount. // TODO(tannewt): Make sure we have all the methods we need to operating it // as a file system. - mp_obj_t vfs_obj = args[ARG_filesystem].u_obj; mp_obj_t dest[2]; mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest); if (dest[0] == MP_OBJ_NULL) { mp_raise_ValueError(MP_ERROR_TEXT("filesystem must provide mount method")); } + #endif common_hal_storage_mount(vfs_obj, mnt_str, args[ARG_readonly].u_bool); diff --git a/shared-module/storage/__init__.c b/shared-module/storage/__init__.c index 9a2c8271222e0..dedcee1ff23f5 100644 --- a/shared-module/storage/__init__.c +++ b/shared-module/storage/__init__.c @@ -126,6 +126,11 @@ void common_hal_storage_mount(mp_obj_t vfs_obj, const char *mount_path, bool rea // call the underlying object to do any mounting operation mp_vfs_proxy_call(vfs, MP_QSTR_mount, 2, (mp_obj_t *)&args); + fs_user_mount_t *vfs_fat = MP_OBJ_TO_PTR(vfs_obj); + // Filesystem is read-only to USB if writable by CircuitPython, and vice versa. + filesystem_set_writable_by_usb(vfs_fat, readonly); + filesystem_set_concurrent_write_protection(vfs_fat, true); + // Insert the vfs into the mount table by pushing it onto the front of the // mount table. mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index df70d963de6f1..24c7f9bffc552 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -247,13 +247,13 @@ void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable) { } bool filesystem_is_writable_by_python(fs_user_mount_t *vfs) { - return (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0 || - (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) == 0; + return ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0) || + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) == 0); } bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) { - return (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0 || - (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) != 0; + return ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0) || + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) != 0); } void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection) { From 81e77aa71e1dfdd4989f832346874e89d3dd564a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 30 Sep 2025 10:57:27 -0400 Subject: [PATCH 151/517] main.c: unmount user-mounted vfs regardless of order --- main.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/main.c b/main.c index 78148f2cb019c..d7f3c7aaf1bce 100644 --- a/main.c +++ b/main.c @@ -211,14 +211,22 @@ static void start_mp(safe_mode_t safe_mode) { static void stop_mp(void) { #if MICROPY_VFS - mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); // Unmount all heap allocated vfs mounts. - while (gc_ptr_on_heap(vfs)) { + mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); + do { + if (gc_ptr_on_heap(vfs)) { + // mp_vfs_umount will splice out an unmounted vfs from the vfs_mount_table linked list. + mp_vfs_umount(vfs->obj); + // Start over at the beginning of the list since the first entry may have been removed. + vfs = MP_STATE_VM(vfs_mount_table); + continue; + } vfs = vfs->next; - } - MP_STATE_VM(vfs_mount_table) = vfs; + } while (vfs != NULL); + // The last vfs is CIRCUITPY and the root directory. + vfs = MP_STATE_VM(vfs_mount_table); while (vfs->next != NULL) { vfs = vfs->next; } From 216b3100652f93466730b49c5993293e0dc23459 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 30 Sep 2025 12:09:13 -0400 Subject: [PATCH 152/517] shrink some atmel-samd boards --- .../atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk | 3 ++- ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk | 1 + ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk | 1 + ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ports/atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk b/ports/atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk index 7c301e3ea9692..121becc472d36 100755 --- a/ports/atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk @@ -11,4 +11,5 @@ EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 -CIRCUITPY_JPEGIO = 0 +CIRCUITPY_ERRNO = 0 +CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk index 783e2cb565783..1152fb4acc419 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk @@ -11,4 +11,5 @@ EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C, W25Q16JVxQ" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 +CIRCUITPY_ERRNO = 0 CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk index bd068adc943e2..dd58e8d1ce5a7 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk @@ -11,4 +11,5 @@ EXTERNAL_FLASH_DEVICES = "S25FL064L" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 +CIRCUITPY_ERRNO = 0 CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk index a7c18acba3c55..ee414b6e214b0 100644 --- a/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk @@ -17,6 +17,7 @@ CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_GIFIO = 0 CIRCUITPY_I2CTARGET = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_PS2IO = 0 CIRCUITPY_SYNTHIO = 0 CIRCUITPY_LTO_PARTITION = one From 179a52ec5321a3ad9a45f102f88b771852a96025 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 30 Sep 2025 13:40:56 -0400 Subject: [PATCH 153/517] remove unused code --- shared-bindings/storage/__init__.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/shared-bindings/storage/__init__.c b/shared-bindings/storage/__init__.c index 93c360d564e2f..14ec16f3096c7 100644 --- a/shared-bindings/storage/__init__.c +++ b/shared-bindings/storage/__init__.c @@ -60,18 +60,6 @@ static mp_obj_t storage_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t // Currently, the only supported filesystem is VfsFat. mp_arg_validate_type(vfs_obj, &mp_fat_vfs_type, MP_QSTR_filesystem); - // Add this back if/when we start supporting other filesystems. - #if 0 - // Make sure we're given an object we can mount. - // TODO(tannewt): Make sure we have all the methods we need to operating it - // as a file system. - mp_obj_t dest[2]; - mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest); - if (dest[0] == MP_OBJ_NULL) { - mp_raise_ValueError(MP_ERROR_TEXT("filesystem must provide mount method")); - } - #endif - common_hal_storage_mount(vfs_obj, mnt_str, args[ARG_readonly].u_bool); return mp_const_none; From 0b89ac1e245bbf5070b8cb752ebc18a6516c2adf Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 30 Sep 2025 16:32:41 -0400 Subject: [PATCH 154/517] update frozen libraries --- frozen/Adafruit_CircuitPython_PortalBase | 2 +- frozen/Adafruit_CircuitPython_Wiznet5k | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase index c87f120723e9f..d26e2324de496 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit c87f120723e9fa742e3da25eaf33f3be1ae33715 +Subproject commit d26e2324de496761e0aa72abc30ba07cdce8814b diff --git a/frozen/Adafruit_CircuitPython_Wiznet5k b/frozen/Adafruit_CircuitPython_Wiznet5k index 6b3484d1ee243..736241c7a22f8 160000 --- a/frozen/Adafruit_CircuitPython_Wiznet5k +++ b/frozen/Adafruit_CircuitPython_Wiznet5k @@ -1 +1 @@ -Subproject commit 6b3484d1ee243a7e8bc0513ab84956e1b6e2a520 +Subproject commit 736241c7a22f86dcf8ff73a77c4536cedfdc4cdd From 3de2c639fd9070bf49f09f4f8af0da6148af7817 Mon Sep 17 00:00:00 2001 From: Juergen Pabel Date: Thu, 2 Oct 2025 18:15:47 +0200 Subject: [PATCH 155/517] Added new board: M5Stack DinMeter (esp32s3) --- .../espressif/boards/m5stack_dinmeter/board.c | 107 ++++++++++++++++++ .../boards/m5stack_dinmeter/mpconfigboard.h | 22 ++++ .../boards/m5stack_dinmeter/mpconfigboard.mk | 15 +++ .../espressif/boards/m5stack_dinmeter/pins.c | 61 ++++++++++ .../boards/m5stack_dinmeter/sdkconfig | 14 +++ 5 files changed, 219 insertions(+) create mode 100644 ports/espressif/boards/m5stack_dinmeter/board.c create mode 100644 ports/espressif/boards/m5stack_dinmeter/mpconfigboard.h create mode 100644 ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk create mode 100644 ports/espressif/boards/m5stack_dinmeter/pins.c create mode 100644 ports/espressif/boards/m5stack_dinmeter/sdkconfig diff --git a/ports/espressif/boards/m5stack_dinmeter/board.c b/ports/espressif/boards/m5stack_dinmeter/board.c new file mode 100644 index 0000000000000..cd49318fd1cec --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/board.c @@ -0,0 +1,107 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "mpconfigboard.h" +#include "supervisor/board.h" +#include "supervisor/shared/serial.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" +#include "py/runtime.h" +#include "py/ringbuf.h" +#include "shared/runtime/interrupt_char.h" + + +uint8_t display_init_sequence[] = { + 0x01, 0x80, 0x96, // SWRESET and Delay 150ms + 0x11, 0x80, 0xff, // SLPOUT and Delay + 0xb1, 0x03, 0x01, 0x2C, 0x2D, // _FRMCTR1 + 0xb2, 0x03, 0x01, 0x2C, 0x2D, // _FRMCTR2 + 0xb3, 0x06, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D, // _FRMCTR3 + 0xb4, 0x01, 0x07, // _INVCTR line inversion + 0xc0, 0x03, 0xa2, 0x02, 0x84, // _PWCTR1 GVDD = 4.7V, 1.0uA + 0xc1, 0x01, 0xc5, // _PWCTR2 VGH=14.7V, VGL=-7.35V + 0xc2, 0x02, 0x0a, 0x00, // _PWCTR3 Opamp current small, Boost frequency + 0xc3, 0x02, 0x8a, 0x2a, + 0xc4, 0x02, 0x8a, 0xee, + 0xc5, 0x01, 0x0e, // _VMCTR1 VCOMH = 4V, VOML = -1.1V + 0x36, 0x01, 0xc8, // MADCTL Rotate display + 0x21, 0x00, // _INVON + 0x3a, 0x01, 0x05, // COLMOD - 16bit color + 0xe0, 0x10, 0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10, // _GMCTRP1 Gamma + 0xe1, 0x10, 0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10, // _GMCTRN1 + 0x13, 0x80, 0x0a, // _NORON + 0x29, 0x80, 0x64 // _DISPON +}; + + +// Overrides the weakly linked function from supervisor/shared/board.c +void board_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + // see here for inspiration: https://github.com/m5stack/M5GFX/blob/33d7d3135e816a86a008fae8ab3757938cee95d2/src/M5GFX.cpp#L1350 + common_hal_fourwire_fourwire_construct( + bus, + spi, + &pin_GPIO4, // DC + &pin_GPIO7, // CS + &pin_GPIO8, // RST + 40000000, // baudrate + 0, // polarity + 0 // phase + ); + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 240, // width (after rotation) + 135, // height (after rotation) + 40, // column start + 52, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO9, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 80, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + + +bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + // Hold pin must be set high to avoid a power off when battery powered + if (pin_number == 46) { + // Turn on hold output + config_pin_as_output_with_level(pin_number, true); + return true; + } + return false; +} + +// TODO: Should we turn off the display when asleep, in board_deinit() ? diff --git a/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.h b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.h new file mode 100644 index 0000000000000..2d69e61ea950a --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 CDarius +// SPDX-FileCopyrightText: Copyright (c) 2025 juergenpabel +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "M5Stack DinMeter" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define CIRCUITPY_BOARD_I2C (2) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO12, .sda = &pin_GPIO11}, \ + {.scl = &pin_GPIO15, .sda = &pin_GPIO13}} + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO6, .mosi = &pin_GPIO5}} + +#define CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP (1) diff --git a/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk new file mode 100644 index 0000000000000..7b7e941b6bc0a --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk @@ -0,0 +1,15 @@ +USB_VID = 0x303A +USB_PID = 0x81DD + +USB_PRODUCT = "M5stack - DinMeter" +USB_MANUFACTURER = "M5Stack" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +# Very few pins. +CIRCUITPY_ESPCAMERA = 0 +CIRCUITPY_PARALLELDISPLAYBUS = 0 diff --git a/ports/espressif/boards/m5stack_dinmeter/pins.c b/ports/espressif/boards/m5stack_dinmeter/pins.c new file mode 100644 index 0000000000000..f75e05e848b0b --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/pins.c @@ -0,0 +1,61 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Juergen Pabel +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(porta_i2c, i2c, 1) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // Port A + { MP_ROM_QSTR(MP_QSTR_PORTA_SCL), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_SDA), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) }, + + // Port B + { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + + // Encoder + { MP_ROM_QSTR(MP_QSTR_ENC_A), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_ENC_B), MP_ROM_PTR(&pin_GPIO40) }, + + // Button + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO0) }, + + // Misc + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_KNOB_BUTTON), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_POWER_HOLD), MP_ROM_PTR(&pin_GPIO46) }, + + // Display + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_TFT_BACKLIGHT), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_I2C), MP_ROM_PTR(&board_porta_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)} +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/m5stack_dinmeter/sdkconfig b/ports/espressif/boards/m5stack_dinmeter/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration From 7806098d3866ce805b63c358ab64c837e008669d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Pabel?= Date: Thu, 2 Oct 2025 20:48:31 +0200 Subject: [PATCH 156/517] Update ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk Co-authored-by: Scott Shawcroft --- ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk index 7b7e941b6bc0a..02570b54230d7 100644 --- a/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk @@ -1,7 +1,7 @@ USB_VID = 0x303A USB_PID = 0x81DD -USB_PRODUCT = "M5stack - DinMeter" +USB_PRODUCT = "DinMeter" USB_MANUFACTURER = "M5Stack" IDF_TARGET = esp32s3 From 5b4b9e8db19c097e8422c15b04705050fab98798 Mon Sep 17 00:00:00 2001 From: Juergen Pabel Date: Thu, 2 Oct 2025 20:57:03 +0200 Subject: [PATCH 157/517] Updated USB-PID as per https://github.com/espressif/usb-pids/pull/261 --- ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk index 02570b54230d7..81a23af1fe464 100644 --- a/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk @@ -1,5 +1,5 @@ USB_VID = 0x303A -USB_PID = 0x81DD +USB_PID = 0x830B USB_PRODUCT = "DinMeter" USB_MANUFACTURER = "M5Stack" From 1ad0d5fcb113b9c44ed0328e9f28de4596bd789f Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 3 Oct 2025 14:02:14 -0400 Subject: [PATCH 158/517] drop Nano from Microchip Curiosity CircuitPython Nano --- .../board.c | 0 .../mpconfigboard.h | 2 +- .../mpconfigboard.mk | 2 +- .../pins.c | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename ports/atmel-samd/boards/{microchip_curiosity_circuitpython_nano => microchip_curiosity_circuitpython}/board.c (100%) rename ports/atmel-samd/boards/{microchip_curiosity_circuitpython_nano => microchip_curiosity_circuitpython}/mpconfigboard.h (99%) rename ports/atmel-samd/boards/{microchip_curiosity_circuitpython_nano => microchip_curiosity_circuitpython}/mpconfigboard.mk (83%) rename ports/atmel-samd/boards/{microchip_curiosity_circuitpython_nano => microchip_curiosity_circuitpython}/pins.c (100%) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/board.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/board.c similarity index 100% rename from ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/board.c rename to ports/atmel-samd/boards/microchip_curiosity_circuitpython/board.c diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.h similarity index 99% rename from ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h rename to ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.h index 6e67197e757f1..4416d8517d279 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.h +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.h @@ -1,7 +1,7 @@ #pragma once -#define MICROPY_HW_BOARD_NAME "Microchip Curiosity CircuitPython Nano" +#define MICROPY_HW_BOARD_NAME "Microchip Curiosity CircuitPython" #define MICROPY_HW_MCU_NAME "same51j20" #define CIRCUITPY_MCU_FAMILY samd51 diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.mk similarity index 83% rename from ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk rename to ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.mk index 4bb7b68550922..985eb4c6a4465 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/mpconfigboard.mk +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.mk @@ -1,6 +1,6 @@ USB_VID = 0x04D8 USB_PID = 0xE52B -USB_PRODUCT = "Microchip Curiosity CircuitPython Nano" +USB_PRODUCT = "Microchip Curiosity CircuitPython" USB_MANUFACTURER = "Microchip Technology Inc" CHIP_VARIANT = SAME51J20A diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c similarity index 100% rename from ports/atmel-samd/boards/microchip_curiosity_circuitpython_nano/pins.c rename to ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c From ea15d7461402e5a5346b28e73458c115fe72b419 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 5 Oct 2025 15:45:42 -0400 Subject: [PATCH 159/517] Allow subclassing deque --- py/objdeque.c | 36 ++++++++++++++++-------- tests/circuitpython/deque_subclass.py | 40 +++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 tests/circuitpython/deque_subclass.py diff --git a/py/objdeque.c b/py/objdeque.c index 264c795801ba4..4e1ee82e9d4de 100644 --- a/py/objdeque.c +++ b/py/objdeque.c @@ -46,6 +46,11 @@ static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in); static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque, mp_obj_iter_buf_t *iter_buf); #endif +// CIRCUITPY-CHANGE +static mp_obj_deque_t *native_deque(mp_obj_t self_in) { + return MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(self_in, MP_OBJ_FROM_PTR(&mp_type_deque))); +} + static mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 2, 3, false); @@ -79,7 +84,8 @@ static size_t deque_len(mp_obj_deque_t *self) { } static mp_obj_t deque_unary_op(mp_unary_op_t op, mp_obj_t self_in) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); switch (op) { case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->i_get != self->i_put); @@ -98,7 +104,8 @@ static mp_obj_t deque_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); size_t new_i_put = self->i_put + 1; if (new_i_put == self->alloc) { @@ -123,7 +130,8 @@ static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg) { static MP_DEFINE_CONST_FUN_OBJ_2(deque_append_obj, mp_obj_deque_append); static mp_obj_t mp_obj_deque_appendleft(mp_obj_t self_in, mp_obj_t arg) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); size_t new_i_get = self->i_get - 1; if (self->i_get == 0) { @@ -162,7 +170,8 @@ static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in) { static MP_DEFINE_CONST_FUN_OBJ_2(deque_extend_obj, mp_obj_deque_extend); static mp_obj_t deque_popleft(mp_obj_t self_in) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); if (self->i_get == self->i_put) { mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty")); @@ -180,7 +189,8 @@ static mp_obj_t deque_popleft(mp_obj_t self_in) { static MP_DEFINE_CONST_FUN_OBJ_1(deque_popleft_obj, deque_popleft); static mp_obj_t deque_pop(mp_obj_t self_in) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); if (self->i_get == self->i_put) { mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty")); @@ -205,7 +215,8 @@ static mp_obj_t deque_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { // delete not supported, fall back to mp_obj_subscr() error message return MP_OBJ_NULL; } - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); size_t offset = mp_get_index(self->base.type, deque_len(self), index, false); size_t index_val = self->i_get + offset; @@ -226,7 +237,8 @@ static mp_obj_t deque_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { #if 0 static mp_obj_t deque_clear(mp_obj_t self_in) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); self->i_get = self->i_put = 0; mp_seq_clear(self->items, 0, self->alloc, sizeof(*self->items)); return mp_const_none; @@ -286,7 +298,8 @@ typedef struct _mp_obj_deque_it_t { static mp_obj_t deque_it_iternext(mp_obj_t self_in) { mp_obj_deque_it_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_deque_t *deque = MP_OBJ_TO_PTR(self->deque); + // CIRCUITPY-CHANGE + mp_obj_deque_t *deque = native_deque(self->deque); if (self->cur != deque->i_put) { mp_obj_t o_out = deque->items[self->cur]; if (++self->cur == deque->alloc) { @@ -298,9 +311,10 @@ static mp_obj_t deque_it_iternext(mp_obj_t self_in) { } } -static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque, mp_obj_iter_buf_t *iter_buf) { - mp_obj_deque_t *deque_ = MP_OBJ_TO_PTR(deque); - size_t i_get = deque_->i_get; +static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque_in, mp_obj_iter_buf_t *iter_buf) { + // CIRCUITPY-CHANGE + mp_obj_deque_t *deque = native_deque(deque_in); + size_t i_get = deque->i_get; assert(sizeof(mp_obj_deque_it_t) <= sizeof(mp_obj_iter_buf_t)); mp_obj_deque_it_t *o = (mp_obj_deque_it_t *)iter_buf; o->base.type = &mp_type_polymorph_iter; diff --git a/tests/circuitpython/deque_subclass.py b/tests/circuitpython/deque_subclass.py new file mode 100644 index 0000000000000..5708407a9b630 --- /dev/null +++ b/tests/circuitpython/deque_subclass.py @@ -0,0 +1,40 @@ +try: + from collections import deque +except ImportError: + print("SKIP") + raise SystemExit + + +class DequeSubclass(deque): + def __init__(self, values, maxlen): + super().__init__(values, maxlen) + + def pop(self): + print("pop") + return super().pop() + + def popleft(self): + print("popleft") + return super().popleft() + + def append(self, value): + print("append") + return super().append(value) + + def appendleft(self, value): + print("appendleft") + return super().appendleft(value) + + def extend(self, value): + print("extend") + return super().extend(value) + + +d = DequeSubclass([1, 2, 3], 10) +print(d.append(4)) +print(d.appendleft(0)) +print(d.pop()) +print(d.popleft()) +d.extend([6, 7]) +# calling list() tests iteration. +print(list(d)) From 36d53efa7c6f9a3f0172620df309904a01a8e854 Mon Sep 17 00:00:00 2001 From: Chris Wilson <14631+cdwilson@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:41:37 -0700 Subject: [PATCH 160/517] stm/boards: Fix USB PID for SparkFun STM32 MicroMod. When I originally created the PR to add support for the SparkFun STM32 MicroMod, SparkFun provided me with the following PID/VID: PID: 0x0027 VID: 0X1B4F I missed a follow-up post from SparkFun about 5 months later which provided a corrected PID for this device: PID: 0x0029 See https://community.sparkfun.com/t/circuitpython-support-for-micromod-stm32-need-usb-pid/42486/5 --- ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk b/ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk index d1df685c6764e..31c2b7e2874f9 100644 --- a/ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk +++ b/ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk @@ -1,5 +1,5 @@ USB_VID = 0X1B4F -USB_PID = 0x0027 +USB_PID = 0x0029 USB_PRODUCT = "SparkFun STM32 MicroMod Processor" USB_MANUFACTURER = "SparkFun Electronics" From 3d40ba3531701407daa13f9a979ddbdd44806bcd Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 14 Aug 2025 11:40:43 -0700 Subject: [PATCH 161/517] Update to ESP IDF 5.5.1 --- .gitmodules | 2 +- lib/protomatter | 2 +- lib/tinyusb | 2 +- ports/espressif/Makefile | 31 +++++++++++++------ .../espressif/common-hal/alarm/pin/PinAlarm.c | 2 ++ .../espressif/common-hal/analogio/AnalogIn.c | 6 ---- ports/espressif/common-hal/espnow/ESPNow.c | 2 +- .../paralleldisplaybus/ParallelBus.c | 2 +- .../paralleldisplaybus/ParallelBus.h | 2 +- ports/espressif/esp-idf | 2 +- .../esp-idf-config/partitions-8MB-no-uf2.csv | 3 +- .../esp-idf-config/sdkconfig.defaults | 6 ++++ ports/espressif/mpconfigport.mk | 2 ++ ports/espressif/peripherals/pins.h | 2 +- py/circuitpy_mpconfig.mk | 4 +++ supervisor/supervisor.mk | 5 ++- 16 files changed, 48 insertions(+), 27 deletions(-) diff --git a/.gitmodules b/.gitmodules index b89769942af7f..7eaa0b04142b4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -143,7 +143,7 @@ [submodule "ports/espressif/esp-idf"] path = ports/espressif/esp-idf url = https://github.com/adafruit/esp-idf.git - branch = circuitpython-v5.4.1 + branch = circuitpython-v5.5.1 [submodule "ports/espressif/esp-protocols"] path = ports/espressif/esp-protocols url = https://github.com/adafruit/esp-protocols.git diff --git a/lib/protomatter b/lib/protomatter index 0bd9873153ab0..f83bac7e42107 160000 --- a/lib/protomatter +++ b/lib/protomatter @@ -1 +1 @@ -Subproject commit 0bd9873153ab0a91d6737c392622159a4515a2e5 +Subproject commit f83bac7e421077812523fddb83d3e25f29753315 diff --git a/lib/tinyusb b/lib/tinyusb index 5fb3c09963bca..8304587d77745 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 5fb3c09963bca362e535788e289d4b3518da5973 +Subproject commit 8304587d7774526a03c6881d11e6d6208fe759be diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index a063101991fdb..157b59d9aaa0f 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -64,6 +64,7 @@ INC += \ -isystem esp-idf/components/esp_driver_i2s/include \ -isystem esp-idf/components/esp_driver_$(IDF_TARGET)/include \ -isystem esp-idf/components/esp_driver_ledc/include \ + -isystem esp-idf/components/esp_driver_parlio/include \ -isystem esp-idf/components/esp_driver_pcnt/include \ -isystem esp-idf/components/esp_driver_rmt/include \ -isystem esp-idf/components/esp_driver_sdio/include \ @@ -149,6 +150,8 @@ CFLAGS += -DSTACK_CANARY_VALUE=0xa5a5a5a5 REGISTRATION_FUNCTIONS = \ -u ld_include_highint_hdl \ -u __cxx_fatal_exception \ + -u __cxx_init_dummy \ + -u __cxa_guard_dummy \ -u esp_app_desc \ -u esp_timer_init_include_func \ -u uart_vfs_include_dev_init \ @@ -156,13 +159,17 @@ REGISTRATION_FUNCTIONS = \ -u __ubsan_include \ -u esp_system_include_startup_funcs \ -u esp_efuse_startup_include_func \ - -u newlib_include_heap_impl \ - -u newlib_include_syscalls_impl \ - -u newlib_include_pthread_impl \ - -u newlib_include_assert_impl \ - -u newlib_include_init_funcs \ + -u esp_libc_include_heap_impl \ + -u esp_libc_include_reent_syscalls_impl \ + -u esp_libc_include_syscalls_impl \ + -u esp_libc_include_pthread_impl \ + -u esp_libc_include_assert_impl \ + -u esp_libc_include_getentropy_impl \ + -u esp_libc_include_init_funcs \ + -u esp_libc_init_funcs \ -u include_esp_phy_override \ - -u vfs_include_syscalls_impl + -u vfs_include_syscalls_impl \ + -u esp_vfs_include_nullfs_register #Debugging/Optimization @@ -222,7 +229,6 @@ else ifeq ($(IDF_TARGET_ARCH),riscv) -Trom.api.ld endif -$(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align LDFLAGS += $(CFLAGS) -Wl,-nostdlib -Wl,-Map=$@.map -Wl,-cref -Wl,--undefined=uxTopUsedPriority @@ -244,7 +250,9 @@ LDFLAGS += \ ifeq ($(IDF_TARGET),esp32) LDFLAGS += \ -Tesp32.rom.newlib-data.ld \ - -Tesp32.rom.newlib-funcs.ld \ + -Tesp32.rom.syscalls.ld \ + -Tesp32.rom.libc-funcs.ld \ + -Tesp32.rom.newlib-reent-funcs.ld \ -Tesp32.rom.spiflash_legacy.ld CHIP_COMPONENTS = \ @@ -280,7 +288,9 @@ LDFLAGS += \ -Tesp32c6.rom.phy.ld \ -Tesp32c6.rom.pp.ld \ -Tesp32c6.rom.net80211.ld \ + -Tesp32c6.rom.libc.ld \ -Tesp32c6.rom.newlib.ld \ + -Tesp32c6.rom.spiflash.ld \ -Tesp32c6.rom.coexist.ld \ -Tesp32c6.rom.heap.ld \ -Tesp32c6.rom.systimer.ld \ @@ -312,8 +322,9 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32s2) LDFLAGS += \ + -Tesp32s2.rom.libc-funcs.ld \ -Tesp32s2.rom.newlib-data.ld \ - -Tesp32s2.rom.newlib-funcs.ld \ + -Tesp32s2.rom.newlib-reent-funcs.ld \ -Tesp32s2.rom.spiflash_legacy.ld CHIP_COMPONENTS = \ @@ -782,7 +793,7 @@ endif IDF_CMAKE_TARGETS = \ bootloader/bootloader.bin \ - esp-idf/esp_system/__ldgen_output_sections.ld \ + __ldgen_output_sections.ld \ $(foreach component, $(ESP_IDF_COMPONENTS_LINK), esp-idf/$(component)/lib$(component).a) PARTITION_TABLE_OFFSET = 0x8000 diff --git a/ports/espressif/common-hal/alarm/pin/PinAlarm.c b/ports/espressif/common-hal/alarm/pin/PinAlarm.c index 8e3e46785f2e7..cecb91fe46df9 100644 --- a/ports/espressif/common-hal/alarm/pin/PinAlarm.c +++ b/ports/espressif/common-hal/alarm/pin/PinAlarm.c @@ -14,10 +14,12 @@ #include "esp_sleep.h" #include "hal/gpio_ll.h" +#include "driver/gpio.h" #include "esp_debug_helpers.h" #ifdef SOC_PM_SUPPORT_EXT0_WAKEUP #include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" #endif #include "driver/rtc_io.h" diff --git a/ports/espressif/common-hal/analogio/AnalogIn.c b/ports/espressif/common-hal/analogio/AnalogIn.c index 16340e91ac91c..b4039b3d32dd8 100644 --- a/ports/espressif/common-hal/analogio/AnalogIn.c +++ b/ports/espressif/common-hal/analogio/AnalogIn.c @@ -86,10 +86,8 @@ uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) { adc_cali_scheme_ver_t supported_schemes; adc_cali_check_scheme(&supported_schemes); - #ifndef CONFIG_IDF_TARGET_ESP32P4 adc_cali_scheme_ver_t calibration_scheme = 0; adc_cali_handle_t calibration; - #endif #if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) && ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED adc_cali_curve_fitting_config_t config = { .unit_id = self->pin->adc_index, @@ -137,11 +135,7 @@ uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) { // This corrects non-linear regions of the ADC range with a LUT, so it's a better reading than raw int voltage; - #ifdef CONFIG_IDF_TARGET_ESP32P4 - voltage = 0; - #else adc_cali_raw_to_voltage(calibration, adc_reading, &voltage); - #endif #if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) && ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED if (calibration_scheme == ADC_CALI_SCHEME_VER_CURVE_FITTING) { diff --git a/ports/espressif/common-hal/espnow/ESPNow.c b/ports/espressif/common-hal/espnow/ESPNow.c index 79bded96fb41d..ae11db250321c 100644 --- a/ports/espressif/common-hal/espnow/ESPNow.c +++ b/ports/espressif/common-hal/espnow/ESPNow.c @@ -56,7 +56,7 @@ typedef struct { // Callback triggered when a sent packet is acknowledged by the peer (or not). // Just count the number of responses and number of failures. // These are used in the send() logic. -static void send_cb(const uint8_t *mac, esp_now_send_status_t status) { +static void send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status) { espnow_obj_t *self = MP_STATE_PORT(espnow_singleton); if (status == ESP_NOW_SEND_SUCCESS) { self->send_success++; diff --git a/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c b/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c index 7721b5e197f49..52c4b67bf4efe 100644 --- a/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c +++ b/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c @@ -18,7 +18,7 @@ #include "common-hal/microcontroller/Pin.h" #include "py/runtime.h" -#include "driver/gpio.h" +#include /* * Current pin limitations for ESP32-S2 ParallelBus: diff --git a/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.h b/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.h index 4724ba7231a4e..114a1e527a6d5 100644 --- a/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.h +++ b/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.h @@ -8,7 +8,7 @@ #include "common-hal/digitalio/DigitalInOut.h" -#include "esp-idf/components/esp_lcd/include/esp_lcd_panel_io.h" +#include typedef struct { mp_obj_base_t base; diff --git a/ports/espressif/esp-idf b/ports/espressif/esp-idf index f50ec8ecdb31f..f4fddd2d05b44 160000 --- a/ports/espressif/esp-idf +++ b/ports/espressif/esp-idf @@ -1 +1 @@ -Subproject commit f50ec8ecdb31f681e6a778f145de95f849c1089d +Subproject commit f4fddd2d05b44d1d606b546b596d17de0a73e9eb diff --git a/ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv b/ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv index 6d78fe8af5916..44121812ea2bf 100644 --- a/ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv +++ b/ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv @@ -3,6 +3,5 @@ # partition_table, data, table, 0x8000, 4K nvs, data, nvs, 0x9000, 20K otadata, data, ota, 0xe000, 8K -ota_0, app, ota_0, 0x10000, 2048K -ota_1, app, ota_1, 0x210000, 2048K +ota_0, app, ota_0, 0x10000, 4096K user_fs, data, fat, 0x410000, 4032K diff --git a/ports/espressif/esp-idf-config/sdkconfig.defaults b/ports/espressif/esp-idf-config/sdkconfig.defaults index dd766675136d9..3c174057ceb19 100644 --- a/ports/espressif/esp-idf-config/sdkconfig.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig.defaults @@ -20,6 +20,12 @@ CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=y CONFIG_GPTIMER_ISR_IRAM_SAFE=y # end of GPTimer Configuration +# +# Touch Configuration +# +CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN=y +# end of Touch Configuration + # end of Driver Configurations # diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index 42bf3418f9639..b632c5dfd6fe1 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -54,6 +54,8 @@ CIRCUITPY_HASHLIB_MBEDTLS_ONLY = 0 CIRCUITPY_PORT_SERIAL = 1 +CIRCUITPY_LIB_TLSF = 0 + # These modules are implemented in ports//common-hal: CIRCUITPY__EVE ?= 1 CIRCUITPY_ALARM ?= 1 diff --git a/ports/espressif/peripherals/pins.h b/ports/espressif/peripherals/pins.h index f89855e308f59..f185cee92c437 100644 --- a/ports/espressif/peripherals/pins.h +++ b/ports/espressif/peripherals/pins.h @@ -14,7 +14,7 @@ #include "components/hal/include/hal/gpio_types.h" #include "components/hal/include/hal/adc_types.h" -#include "components/hal/include/hal/touch_sensor_types.h" +#include "components/hal/include/hal/touch_sensor_legacy_types.h" typedef struct { mp_obj_base_t base; diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 8c838a41218a3..76ee8a1d72bce 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -62,6 +62,10 @@ CFLAGS += -DCIRCUITPY_FULL_BUILD=$(CIRCUITPY_FULL_BUILD) # increased build time CIRCUITPY_MESSAGE_COMPRESSION_LEVEL ?= 9 +# By default, use our copy of TLSF. Some vendor SDKs may provide their own +# implementation of TLSF, which can be used instead by setting CIRCUITPY_LIB_TLSF=0. +CIRCUITPY_LIB_TLSF ?= 1 + # Reduce the size of in-flash properties. Requires support in the .ld linker # file, so not enabled by default. CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE ?= 0 diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 0ecc6ea3acfe6..21bda334131c7 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -1,6 +1,5 @@ SRC_SUPERVISOR = \ main.c \ - lib/tlsf/tlsf.c \ supervisor/port.c \ supervisor/shared/background_callback.c \ supervisor/shared/board.c \ @@ -20,6 +19,10 @@ SRC_SUPERVISOR = \ supervisor/shared/translate/translate.c \ supervisor/shared/workflow.c \ +ifeq ($(CIRCUITPY_LIB_TLSF),1) +SRC_SUPERVISOR += lib/tlsf/tlsf.c +endif + # For tlsf CFLAGS += -D_DEBUG=0 From c25ce600013058817b08f7838eab61e0ab0be490 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 11 Sep 2025 16:47:23 -0700 Subject: [PATCH 162/517] Stop using our own memcpy. Enhance decode backtrace script. --- ports/espressif/Makefile | 1 - .../esp-idf-config/sdkconfig-debug.defaults | 26 ++++++++++++ .../esp-idf-config/sdkconfig.defaults | 7 ++++ ports/espressif/tools/decode_backtrace.py | 41 ++++++++++++++----- py/circuitpy_defns.mk | 1 - 5 files changed, 63 insertions(+), 13 deletions(-) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 157b59d9aaa0f..5e4256749ee2e 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -290,7 +290,6 @@ LDFLAGS += \ -Tesp32c6.rom.net80211.ld \ -Tesp32c6.rom.libc.ld \ -Tesp32c6.rom.newlib.ld \ - -Tesp32c6.rom.spiflash.ld \ -Tesp32c6.rom.coexist.ld \ -Tesp32c6.rom.heap.ld \ -Tesp32c6.rom.systimer.ld \ diff --git a/ports/espressif/esp-idf-config/sdkconfig-debug.defaults b/ports/espressif/esp-idf-config/sdkconfig-debug.defaults index a9c8e34e54706..368ba81bb4aa7 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-debug.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-debug.defaults @@ -7,6 +7,21 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y # end of Compiler options +# Bootloader config +# +# +# Log +# +# +# Format +# +CONFIG_BOOTLOADER_LOG_COLORS=y +# end of Format + +# end of Log + +# end of Bootloader config + # # Component config # @@ -19,6 +34,17 @@ CONFIG_ESP_CONSOLE_SECONDARY_NONE=y # CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG is not set # end of ESP System Settings +# +# Log +# +# +# Format +# +CONFIG_LOG_COLORS=y +# end of Format + +# end of Log + # end of Component config # end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/esp-idf-config/sdkconfig.defaults b/ports/espressif/esp-idf-config/sdkconfig.defaults index 3c174057ceb19..5c4d6a31a62c1 100644 --- a/ports/espressif/esp-idf-config/sdkconfig.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig.defaults @@ -65,6 +65,13 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 CONFIG_FREERTOS_HZ=1000 # end of Kernel +# +# LibC +# +# end of LWIP +CONFIG_LIBC_OPTIMIZED_MISALIGNED_ACCESS=y +# end of LibC + # # LWIP # diff --git a/ports/espressif/tools/decode_backtrace.py b/ports/espressif/tools/decode_backtrace.py index 024e636207ec8..1733c69b5bb57 100644 --- a/ports/espressif/tools/decode_backtrace.py +++ b/ports/espressif/tools/decode_backtrace.py @@ -12,20 +12,39 @@ board = sys.argv[1] print(board) +elfs = [ + f"build-{board}/firmware.elf", + "/home/tannewt/Downloads/esp-rom-elfs-20241011/esp32c6_rev0_rom.elf", +] + while True: + print('"Backtrace:" or "Stack memory:". CTRL-D to finish multiline paste') addresses = input("? ") if addresses.startswith("Backtrace:"): addresses = addresses[len("Backtrace:") :] + addresses = addresses.strip().split() + addresses = [address.split(":")[0] for address in addresses] if addresses.startswith("Stack memory:"): - addresses = addresses[len("Stack memory:") :] - addresses = addresses.strip().split() - addresses = [address.split(":")[0] for address in addresses] + addresses = [] + extra_lines = sys.stdin.readlines() + for line in extra_lines: + if not line.strip(): + continue + addresses.extend(line.split(":")[1].strip().split()) for address in addresses: - result = subprocess.run( - ["xtensa-esp32s2-elf-addr2line", "-aipfe", "build-{}/firmware.elf".format(board)] - + [address], - capture_output=True, - ) - stdout = result.stdout.decode("utf-8") - if "?? ??" not in stdout: - print(stdout.strip()) + if address == "0xa5a5a5a5": + # Skip stack fill value. + continue + for elf in elfs: + result = subprocess.run( + ["riscv32-esp-elf-addr2line", "-aipfe", elf, address], + capture_output=True, + ) + stdout = result.stdout.decode("utf-8") + if not stdout: + continue + if "?? ??" not in stdout: + print(stdout.strip()) + break + + print("loop") diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index d6569bc39bd8e..f3d25f6270cef 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -992,7 +992,6 @@ endif # Sources used in all ports except unix. SRC_CIRCUITPY_COMMON = \ - shared/libc/string0.c \ shared/readline/readline.c \ lib/oofatfs/ff.c \ lib/oofatfs/ffunicode.c \ From f98014475dd2e91b29bc817f0d8c6afd4d43f114 Mon Sep 17 00:00:00 2001 From: Derek Daniels Date: Mon, 15 Sep 2025 14:38:14 -0400 Subject: [PATCH 163/517] [ports/espressif] esp-idf v5.5 changes --- ports/espressif/Makefile | 8 +++- .../espressif/common-hal/alarm/pin/PinAlarm.c | 40 +++++++++---------- .../common-hal/digitalio/DigitalInOut.c | 24 ++++++----- .../common-hal/microcontroller/Pin.c | 5 +-- ports/espressif/common-hal/wifi/__init__.c | 7 +++- 5 files changed, 47 insertions(+), 37 deletions(-) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 5e4256749ee2e..a080939e220a9 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -672,7 +672,7 @@ ifneq ($(IDF_TARGET),esp32p4) BINARY_BLOBS = esp-idf/components/esp_phy/lib/$(IDF_TARGET)/libphy.a endif ifneq ($(CIRCUITPY_WIFI),0) - BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, $(BINARY_WIFI_BLOBS)) + BINARY_BLOBS += $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, $(BINARY_WIFI_BLOBS)) ifneq ($(IDF_TARGET),esp32c2) BINARY_BLOBS += $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, libmesh.a libwapi.a) endif @@ -683,6 +683,12 @@ BINARY_BLOBS += esp-idf/components/esp_phy/lib/$(IDF_TARGET)/librtc.a endif ESP_IDF_COMPONENTS_LINK = $(IDF_TARGET_ARCH) $(CHIP_COMPONENTS) app_update bootloader_support driver esp_driver_gpio esp_driver_gptimer esp_driver_i2c esp_driver_ledc esp_driver_spi esp_driver_uart efuse esp_adc esp_app_format esp_common esp_event esp_hw_support esp_mm esp_partition esp_pm esp_ringbuf esp_rom esp_system esp_timer freertos hal heap log newlib nvs_flash pthread soc spi_flash vfs esp_vfs_console +NEEDS_COEX = $(CIRCUITPY_BLEIO_NATIVE) + $(CIRCUITPY_WIFI) +ifneq ($(NEEDS_COEX),0) + # esp_system_include_startup_funcs requires coexist as well BT regardless of wifi + BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a + ESP_IDF_COMPONENTS_LINK += esp_coex +endif ifneq ($(CIRCUITPY_WIFI),0) ESP_IDF_COMPONENTS_LINK += esp_coex esp_netif esp_security esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy endif diff --git a/ports/espressif/common-hal/alarm/pin/PinAlarm.c b/ports/espressif/common-hal/alarm/pin/PinAlarm.c index cecb91fe46df9..2b09c630925c0 100644 --- a/ports/espressif/common-hal/alarm/pin/PinAlarm.c +++ b/ports/espressif/common-hal/alarm/pin/PinAlarm.c @@ -360,38 +360,36 @@ void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ob if (gpio_isr_register(gpio_interrupt, NULL, 0, &gpio_interrupt_handle) != ESP_OK) { mp_raise_ValueError(MP_ERROR_TEXT("Can only alarm on RTC IO from deep sleep.")); } - for (size_t i = 0; i < 64; i++) { - uint64_t mask = 1ull << i; + for (gpio_num_t pin = 0; pin < SOC_GPIO_PIN_COUNT; pin++) { + uint64_t mask = 1ULL << pin; bool high = (high_alarms & mask) != 0; bool low = (low_alarms & mask) != 0; bool pull = (pull_pins & mask) != 0; if (!(high || low)) { continue; } - if (rtc_gpio_is_valid_gpio(i)) { + if (rtc_gpio_is_valid_gpio(pin)) { #ifdef SOC_PM_SUPPORT_RTC_PERIPH_PD - rtc_gpio_deinit(i); + rtc_gpio_deinit(pin); #endif } - gpio_int_type_t interrupt_mode = GPIO_INTR_DISABLE; - gpio_pull_mode_t pull_mode = GPIO_FLOATING; - if (high) { - interrupt_mode = GPIO_INTR_HIGH_LEVEL; - pull_mode = GPIO_PULLDOWN_ONLY; - } - if (low) { - interrupt_mode = GPIO_INTR_LOW_LEVEL; - pull_mode = GPIO_PULLUP_ONLY; - } - gpio_set_direction(i, GPIO_MODE_DEF_INPUT); - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[i], PIN_FUNC_GPIO); + gpio_set_direction(pin, GPIO_MODE_INPUT); if (pull) { - gpio_set_pull_mode(i, pull_mode); + if (high) { + ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_PULLDOWN_ONLY)); + } else { + ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY)); + } + } else { + ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_FLOATING)); } - never_reset_pin_number(i); - // Sets interrupt type and wakeup bits. - gpio_wakeup_enable(i, interrupt_mode); - gpio_intr_enable(i); + gpio_int_type_t intr = GPIO_INTR_DISABLE; + if (high) intr = GPIO_INTR_HIGH_LEVEL; + if (low) intr = GPIO_INTR_LOW_LEVEL; + never_reset_pin_number(pin); + gpio_wakeup_enable(pin, intr); + gpio_set_intr_type(pin, intr); + gpio_intr_enable(pin); } // Wait for any pulls to settle. mp_hal_delay_ms(50); diff --git a/ports/espressif/common-hal/digitalio/DigitalInOut.c b/ports/espressif/common-hal/digitalio/DigitalInOut.c index ed537419405b3..e4272ba1ff5ff 100644 --- a/ports/espressif/common-hal/digitalio/DigitalInOut.c +++ b/ports/espressif/common-hal/digitalio/DigitalInOut.c @@ -11,8 +11,11 @@ #include "hal/gpio_hal.h" static bool _pin_is_input(uint8_t pin_number) { - const uint32_t iomux = READ_PERI_REG(GPIO_PIN_MUX_REG[pin_number]); - return (iomux & FUN_IE) != 0; + gpio_io_config_t config; + if (gpio_get_io_config((gpio_num_t)pin_number, &config) != ESP_OK) { + return false; + } + return config.ie; } void digitalio_digitalinout_preserve_for_deep_sleep(size_t n_dios, digitalio_digitalinout_obj_t *preserve_dios[]) { @@ -113,10 +116,12 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode( digitalio_drive_mode_t common_hal_digitalio_digitalinout_get_drive_mode( digitalio_digitalinout_obj_t *self) { - if (GPIO_HAL_GET_HW(GPIO_PORT_0)->pin[self->pin->number].pad_driver == 1) { + gpio_io_config_t config; + if (gpio_get_io_config((gpio_num_t)self->pin->number, &config) != ESP_OK) { + // Should it fail closed or open? return DRIVE_MODE_OPEN_DRAIN; } - return DRIVE_MODE_PUSH_PULL; + return config.od ? DRIVE_MODE_OPEN_DRAIN : DRIVE_MODE_PUSH_PULL; } digitalinout_result_t common_hal_digitalio_digitalinout_set_pull( @@ -134,11 +139,10 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_pull( digitalio_pull_t common_hal_digitalio_digitalinout_get_pull( digitalio_digitalinout_obj_t *self) { - gpio_num_t gpio_num = self->pin->number; - if (REG_GET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU)) { - return PULL_UP; - } else if (REG_GET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD)) { - return PULL_DOWN; + gpio_io_config_t config; + if (gpio_get_io_config((gpio_num_t)self->pin->number, &config) != ESP_OK) { + // Should it fail closed or open? + return PULL_NONE; } - return PULL_NONE; + return config.pd ? PULL_DOWN : PULL_UP; } diff --git a/ports/espressif/common-hal/microcontroller/Pin.c b/ports/espressif/common-hal/microcontroller/Pin.c index 70912afb545b4..e14a3366691d0 100644 --- a/ports/espressif/common-hal/microcontroller/Pin.c +++ b/ports/espressif/common-hal/microcontroller/Pin.c @@ -370,9 +370,8 @@ void reset_all_pins(void) { gpio_deep_sleep_hold_dis(); #endif - for (uint8_t i = 0; i < GPIO_PIN_COUNT; i++) { - uint32_t iomux_address = GPIO_PIN_MUX_REG[i]; - if (iomux_address == 0 || + for (gpio_num_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) { + if (!GPIO_IS_VALID_GPIO(i) || _never_reset(i) || _skip_reset_once(i) || _preserved_pin(i)) { diff --git a/ports/espressif/common-hal/wifi/__init__.c b/ports/espressif/common-hal/wifi/__init__.c index 1a4c014bfbc5b..0eacd2bab3ec0 100644 --- a/ports/espressif/common-hal/wifi/__init__.c +++ b/ports/espressif/common-hal/wifi/__init__.c @@ -146,8 +146,8 @@ void common_hal_wifi_init(bool user_initiated) { common_hal_wifi_radio_obj.base.type = &wifi_radio_type; if (!wifi_ever_inited) { - ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(esp_netif_init()); wifi_ever_inited = true; } @@ -175,6 +175,7 @@ void common_hal_wifi_init(bool user_initiated) { &self->handler_instance_got_ip)); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); + esp_err_t result = esp_wifi_init(&config); #ifdef CONFIG_ESP32_WIFI_NVS_ENABLED // Generally we don't use this because we store ssid and passwords ourselves in the filesystem. esp_err_t err = nvs_flash_init(); @@ -185,8 +186,10 @@ void common_hal_wifi_init(bool user_initiated) { err = nvs_flash_init(); } ESP_ERROR_CHECK(err); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH)); + #else + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); #endif - esp_err_t result = esp_wifi_init(&config); if (result == ESP_ERR_NO_MEM) { if (gc_alloc_possible()) { mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to allocate Wifi memory")); From bc44db821442fcb5f5fcf5b8f1cef59d8f0e349b Mon Sep 17 00:00:00 2001 From: Derek Daniels Date: Mon, 15 Sep 2025 14:41:06 -0400 Subject: [PATCH 164/517] [ports/espressif] support for esp32c61 --- ports/espressif/Makefile | 28 +++++++- .../espressif_esp32c61_devkitc_1_n8r2/board.c | 9 +++ .../mpconfigboard.h | 15 ++++ .../mpconfigboard.mk | 13 ++++ .../espressif_esp32c61_devkitc_1_n8r2/pins.c | 48 +++++++++++++ .../sdkconfig | 14 ++++ .../common-hal/analogbufio/BufferedIn.c | 2 +- .../espressif/common-hal/analogio/AnalogIn.c | 2 + .../common-hal/microcontroller/Pin.c | 23 ++++++ .../common-hal/microcontroller/Processor.c | 6 +- .../common-hal/microcontroller/__init__.c | 5 +- .../sdkconfig-esp32c61.defaults | 54 ++++++++++++++ ports/espressif/mpconfigport.h | 2 +- ports/espressif/mpconfigport.mk | 42 +++++++++++ ports/espressif/peripherals/esp32c61/pins.c | 38 ++++++++++ ports/espressif/peripherals/esp32c61/pins.h | 72 +++++++++++++++++++ ports/espressif/peripherals/pins.h | 2 + ports/espressif/supervisor/port.c | 7 +- ports/espressif/tools/build_memory_info.py | 6 ++ 19 files changed, 381 insertions(+), 7 deletions(-) create mode 100644 ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/board.c create mode 100644 ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h create mode 100644 ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk create mode 100644 ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/pins.c create mode 100644 ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig create mode 100644 ports/espressif/esp-idf-config/sdkconfig-esp32c61.defaults create mode 100644 ports/espressif/peripherals/esp32c61/pins.c create mode 100644 ports/espressif/peripherals/esp32c61/pins.h diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index a080939e220a9..930cdd961820b 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -8,6 +8,8 @@ include ../../py/circuitpy_mkenv.mk ifeq ($(IDF_TARGET),esp32s3) BT_IDF_TARGET = esp32c3 +else ifeq ($(IDF_TARGET),esp32c61) +BT_IDF_TARGET = esp32c6 else BT_IDF_TARGET = $(IDF_TARGET) endif @@ -296,6 +298,23 @@ LDFLAGS += \ -Tesp32c6.rom.wdt.ld +CHIP_COMPONENTS = \ + esp_driver_tsens + +else ifeq ($(IDF_TARGET),esp32c61) +LDFLAGS += \ + -Tesp32c61.rom.phy.ld \ + -Tesp32c61.rom.pp.ld \ + -Tesp32c61.rom.net80211.ld \ + -Tesp32c61.rom.libc.ld \ + -Tesp32c61.rom.newlib.ld \ + -Tesp32c61.rom.version.ld \ + -Tesp32c61.rom.coexist.ld \ + -Tesp32c61.rom.heap.ld \ + -Tesp32c61.rom.systimer.ld \ + -Tesp32c61.rom.wdt.ld + + CHIP_COMPONENTS = \ esp_driver_tsens @@ -360,6 +379,8 @@ else ifeq ($(IDF_TARGET),esp32c3) CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32C3 else ifeq ($(IDF_TARGET),esp32c6) CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32C6 +else ifeq ($(IDF_TARGET),esp32c61) +CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32C61 else ifeq ($(IDF_TARGET),esp32p4) CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32P4 else ifeq ($(IDF_TARGET),esp32h2) @@ -698,6 +719,7 @@ ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) BLE_IMPL_esp32c2 := libble BLE_IMPL_esp32c3 := esp32c3 BLE_IMPL_esp32c6 := libble + BLE_IMPL_esp32c61 := libble BLE_IMPL_esp32h2 := libble BLE_IMPL = $(BLE_IMPL_$(IDF_TARGET)) @@ -716,8 +738,8 @@ ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) ifeq ($(BLE_IMPL),libble) BINARY_BLOBS += esp-idf/components/esp_phy/lib/$(IDF_TARGET)/libbtbb.a - ifeq ($(IDF_TARGET),esp32c6) - BINARY_BLOBS += esp-idf/components/bt/controller/lib_$(IDF_TARGET)/$(IDF_TARGET)-bt-lib/$(IDF_TARGET)/libble_app.a + ifeq ($(BT_IDF_TARGET),esp32c6) + BINARY_BLOBS += esp-idf/components/bt/controller/lib_$(BT_IDF_TARGET)/$(BT_IDF_TARGET)-bt-lib/$(IDF_TARGET)/libble_app.a else BINARY_BLOBS += esp-idf/components/bt/controller/lib_$(IDF_TARGET)/$(IDF_TARGET)-bt-lib/libble_app.a endif @@ -785,6 +807,8 @@ else ifeq ($(IDF_TARGET),esp32c3) BOOTLOADER_OFFSET = 0x0 else ifeq ($(IDF_TARGET),esp32c6) BOOTLOADER_OFFSET = 0x0 +else ifeq ($(IDF_TARGET),esp32c61) +BOOTLOADER_OFFSET = 0x0 else ifeq ($(IDF_TARGET),esp32p4) BOOTLOADER_OFFSET = 0x2000 else ifeq ($(IDF_TARGET),esp32s3) diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/board.c b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/board.c new file mode 100644 index 0000000000000..a3a9eec047145 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h new file mode 100644 index 0000000000000..d901bfb37ba28 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "ESP32-C61-DevKitC-1-N8R2" +#define MICROPY_HW_MCU_NAME "ESP32C61" + +#define DEFAULT_UART_BUS_RX (&pin_GPIO10) +#define DEFAULT_UART_BUS_TX (&pin_GPIO11) diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk new file mode 100644 index 0000000000000..1092a874a6868 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk @@ -0,0 +1,13 @@ +CIRCUITPY_CREATOR_ID = 0x000C303A +CIRCUITPY_CREATION_ID = 0x00C61001 + +IDF_TARGET = esp32c61 +IDF_TARGET_ARCH = riscv + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +CIRCUITPY_ESP_PSRAM_SIZE = 2MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 80m diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/pins.c b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/pins.c new file mode 100644 index 0000000000000..901e52166576b --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/pins.c @@ -0,0 +1,48 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_IO23), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_IO24), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_IO25), MP_ROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_IO26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_IO27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_IO28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_IO29), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/common-hal/analogbufio/BufferedIn.c b/ports/espressif/common-hal/analogbufio/BufferedIn.c index aac7ad28daf25..20c5c060574aa 100644 --- a/ports/espressif/common-hal/analogbufio/BufferedIn.c +++ b/ports/espressif/common-hal/analogbufio/BufferedIn.c @@ -33,7 +33,7 @@ #elif defined(CONFIG_IDF_TARGET_ESP32S2) #define ADC_RESULT_BYTE 2 #define ADC_CONV_LIMIT_EN 0 -#elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32P4) +#elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32P4) || defined(CONFIG_IDF_TARGET_ESP32C61) #define ADC_RESULT_BYTE 4 #define ADC_CONV_LIMIT_EN 0 #elif defined(CONFIG_IDF_TARGET_ESP32S3) diff --git a/ports/espressif/common-hal/analogio/AnalogIn.c b/ports/espressif/common-hal/analogio/AnalogIn.c index b4039b3d32dd8..34cd5b846cf22 100644 --- a/ports/espressif/common-hal/analogio/AnalogIn.c +++ b/ports/espressif/common-hal/analogio/AnalogIn.c @@ -32,6 +32,8 @@ #define DATA_WIDTH ADC_BITWIDTH_12 #elif defined(CONFIG_IDF_TARGET_ESP32C6) #define DATA_WIDTH ADC_BITWIDTH_12 +#elif defined(CONFIG_IDF_TARGET_ESP32C61) +#define DATA_WIDTH ADC_BITWIDTH_12 #elif defined(CONFIG_IDF_TARGET_ESP32P4) #define DATA_WIDTH ADC_BITWIDTH_12 #elif defined(CONFIG_IDF_TARGET_ESP32S2) diff --git a/ports/espressif/common-hal/microcontroller/Pin.c b/ports/espressif/common-hal/microcontroller/Pin.c index e14a3366691d0..922cde763e363 100644 --- a/ports/espressif/common-hal/microcontroller/Pin.c +++ b/ports/espressif/common-hal/microcontroller/Pin.c @@ -146,6 +146,29 @@ static const uint64_t pin_mask_reset_forbidden = #endif #endif // ESP32C6 + #if defined(CONFIG_IDF_TARGET_ESP32C61) + // Never ever reset pins used to communicate with SPI flash. + GPIO_SEL_15 | // SPICS0 (flash CS#) + GPIO_SEL_16 | // SPIQ (MISO/SIO1) + GPIO_SEL_17 | // SPIWP (WP#/SIO2) + GPIO_SEL_19 | // SPIHD (HOLD#/SIO3) + GPIO_SEL_20 | // SPICLK (CLK) + GPIO_SEL_21 | // SPID (MOSI/SIO0) + #if CIRCUITPY_ESP_USB_SERIAL_JTAG + // Never ever reset serial/JTAG communication pins. + GPIO_SEL_12 | // USB D- + GPIO_SEL_13 | // USB D+ + #endif + #if defined(CONFIG_SPIRAM) + GPIO_SEL_14 | // SPICS1 (PSRAM CS#); keep if PSRAM in use + #endif + #if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) && CONFIG_ESP_CONSOLE_UART_DEFAULT && CONFIG_ESP_CONSOLE_UART_NUM == 0 + // Never reset debug UART/console pins. + GPIO_SEL_10 | + GPIO_SEL_11 | + #endif + #endif // ESP32C6 + #if defined(CONFIG_IDF_TARGET_ESP32H2) // Never ever reset pins used to communicate with the in-package SPI flash. GPIO_SEL_15 | diff --git a/ports/espressif/common-hal/microcontroller/Processor.c b/ports/espressif/common-hal/microcontroller/Processor.c index 8a6a14c0236f5..0056465f1c5cd 100644 --- a/ports/espressif/common-hal/microcontroller/Processor.c +++ b/ports/espressif/common-hal/microcontroller/Processor.c @@ -59,7 +59,7 @@ uint32_t common_hal_mcu_processor_get_frequency(void) { // If the requested frequency is not supported by the hardware, return the next lower supported frequency static uint32_t get_valid_cpu_frequency(uint32_t requested_freq_mhz) { - #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C61) uint32_t valid_cpu_frequencies[] = {20, 40, 80, 160}; #elif defined(CONFIG_IDF_TARGET_ESP32C2) uint32_t valid_cpu_frequencies[] = {20, 40, 80, 120}; @@ -126,6 +126,8 @@ void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) { uint32_t mac_address_part = REG_READ(EFUSE_RD_MAC_SYS_0_REG); #elif defined(CONFIG_IDF_TARGET_ESP32C2) uint32_t mac_address_part = REG_READ(EFUSE_RD_BLK2_DATA0_REG); + #elif defined(CONFIG_IDF_TARGET_ESP32C61) + uint32_t mac_address_part = REG_READ(EFUSE_RD_MAC_SYS0_REG); #else uint32_t mac_address_part = REG_READ(EFUSE_RD_MAC_SPI_SYS_0_REG); #endif @@ -145,6 +147,8 @@ void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) { mac_address_part = REG_READ(EFUSE_RD_MAC_SYS_1_REG); #elif defined(CONFIG_IDF_TARGET_ESP32C2) mac_address_part = REG_READ(EFUSE_RD_BLK2_DATA1_REG); + #elif defined(CONFIG_IDF_TARGET_ESP32C61) + mac_address_part = REG_READ(EFUSE_RD_MAC_SYS1_REG); #else mac_address_part = REG_READ(EFUSE_RD_MAC_SPI_SYS_1_REG); #endif diff --git a/ports/espressif/common-hal/microcontroller/__init__.c b/ports/espressif/common-hal/microcontroller/__init__.c index 918366b8933c4..d23afce4d999e 100644 --- a/ports/espressif/common-hal/microcontroller/__init__.c +++ b/ports/espressif/common-hal/microcontroller/__init__.c @@ -36,6 +36,9 @@ #elif defined(CONFIG_IDF_TARGET_ESP32C6) #include "soc/lp_aon_reg.h" #include "esp32c6/rom/rtc.h" +#elif defined(CONFIG_IDF_TARGET_ESP32C61) +#include "soc/lp_aon_reg.h" +#include "esp32c61/rom/rtc.h" #elif defined(CONFIG_IDF_TARGET_ESP32P4) #include "esp32p4/rom/rtc.h" #elif defined(CONFIG_IDF_TARGET_ESP32S2) @@ -83,7 +86,7 @@ void common_hal_mcu_enable_interrupts(void) { void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { switch (runmode) { case RUNMODE_UF2: - #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C61) mp_arg_error_invalid(MP_QSTR_run_mode); #else // 0x11F2 is APP_REQUEST_UF2_RESET_HINT & is defined by TinyUF2 diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32c61.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32c61.defaults new file mode 100644 index 0000000000000..85dde905f3caa --- /dev/null +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32c61.defaults @@ -0,0 +1,54 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# Bluetooth +# +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y +# +# NimBLE Options +# +CONFIG_BT_NIMBLE_LOG_LEVEL_NONE=y +CONFIG_BT_NIMBLE_NVS_PERSIST=y +# +# Memory Settings +# +CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=20 +CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=70 +# end of Memory Settings + +CONFIG_BT_NIMBLE_EXT_ADV=y +# end of NimBLE Options + +# end of Bluetooth + +# +# Driver Configurations +# +# +# PCNT Configuration +# +CONFIG_PCNT_CTRL_FUNC_IN_IRAM=y +# end of PCNT Configuration + +# end of Driver Configurations + +# +# PHY +# +CONFIG_ESP_PHY_ENABLE_USB=y +# end of PHY + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=4 +# end of Wi-Fi + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 712eb67f1f42a..031676cf4d8ae 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -27,7 +27,7 @@ // Nearly all boards have this because it is used to enter the ROM bootloader. #ifndef CIRCUITPY_BOOT_BUTTON - #if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) + #if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32C61) #define CIRCUITPY_BOOT_BUTTON (&pin_GPIO9) #elif !defined(CONFIG_IDF_TARGET_ESP32) #define CIRCUITPY_BOOT_BUTTON (&pin_GPIO0) diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index b632c5dfd6fe1..572e0579ef00a 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -17,6 +17,9 @@ CROSS_COMPILE = riscv32-esp-elf- else ifeq ($(IDF_TARGET),esp32c6) IDF_TARGET_ARCH = riscv CROSS_COMPILE = riscv32-esp-elf- +else ifeq ($(IDF_TARGET),esp32c61) +IDF_TARGET_ARCH = riscv +CROSS_COMPILE = riscv32-esp-elf- else ifeq ($(IDF_TARGET),esp32h2) IDF_TARGET_ARCH = riscv CROSS_COMPILE = riscv32-esp-elf- @@ -201,6 +204,45 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 +#### esp32c6 ########################################################## +else ifeq ($(IDF_TARGET),esp32c61) +# Modules +CIRCUITPY_ESPCAMERA = 0 +CIRCUITPY_ESPULP = 0 +CIRCUITPY_MEMORYMAP = 0 +CIRCUITPY_RGBMATRIX = 0 + +# No DAC +CIRCUITPY_AUDIOIO = 0 + +# No space for this +CIRCUITPY_AUDIOBUSIO = 0 + +# No I80 support from the IDF +CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# No SDMMC +CIRCUITPY_SDIOIO = 0 + +CIRCUITPY_TOUCHIO ?= 1 +CIRCUITPY_TOUCHIO_USE_NATIVE = 0 +# Features +CIRCUITPY_USB_DEVICE = 0 +CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 + +# No TWAI on chip +CIRCUITPY_CANIO = 0 + +# No RMT on chip +CIRCUITPY_NEOPIXEL_WRITE = 0 +CIRCUITPY_PULSEIO = 0 +CIRCUITPY_RGBMATRIX = 0 + +# No PCNT on chip +CIRCUITPY_COUNTIO = 0 +CIRCUITPY_ROTARYIO = 0 +CIRCUITPY_FREQUENCYIO = 0 + #### esp32h2 ########################################################## else ifeq ($(IDF_TARGET),esp32h2) # Modules diff --git a/ports/espressif/peripherals/esp32c61/pins.c b/ports/espressif/peripherals/esp32c61/pins.c new file mode 100644 index 0000000000000..a32e1e8da9856 --- /dev/null +++ b/ports/espressif/peripherals/esp32c61/pins.c @@ -0,0 +1,38 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +const mcu_pin_obj_t pin_GPIO0 = PIN(0, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO1 = PIN(1, ADC_UNIT_1, ADC_CHANNEL_0, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO2 = PIN(2, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO3 = PIN(3, ADC_UNIT_1, ADC_CHANNEL_1, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO4 = PIN(4, ADC_UNIT_1, ADC_CHANNEL_2, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO5 = PIN(5, ADC_UNIT_1, ADC_CHANNEL_3, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO6 = PIN(6, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO7 = PIN(7, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO8 = PIN(8, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO9 = PIN(9, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO10 = PIN(10, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO11 = PIN(11, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO12 = PIN(12, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO13 = PIN(13, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO14 = PIN(14, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO15 = PIN(15, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO16 = PIN(16, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO17 = PIN(17, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO18 = PIN(18, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO19 = PIN(19, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO20 = PIN(20, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO21 = PIN(21, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO22 = PIN(22, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO23 = PIN(23, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO24 = PIN(24, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO25 = PIN(25, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO26 = PIN(26, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO27 = PIN(27, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO28 = PIN(28, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO29 = PIN(29, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); diff --git a/ports/espressif/peripherals/esp32c61/pins.h b/ports/espressif/peripherals/esp32c61/pins.h new file mode 100644 index 0000000000000..908c9fe29f0e2 --- /dev/null +++ b/ports/espressif/peripherals/esp32c61/pins.h @@ -0,0 +1,72 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries LLC +// +// SPDX-License-Identifier: MIT + +// DO NOT include this file directly. +// Use shared-bindings/microcontroller/Pin.h instead. +// This ensures that all necessary includes are already included. + +#pragma once + +#define GPIO0_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO0; +#define GPIO1_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO1; +#define GPIO2_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO2; +#define GPIO3_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO3; +#define GPIO4_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO4; +#define GPIO5_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO5; +#define GPIO6_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO6; +#define GPIO7_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO7; +#define GPIO8_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO8; +#define GPIO9_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO9; +#define GPIO10_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO10; +#define GPIO11_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO11; +#define GPIO12_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO12; +#define GPIO13_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO13; +#define GPIO14_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO14; +#define GPIO15_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO15; +#define GPIO16_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO16; +#define GPIO17_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO17; +#define GPIO18_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO18; +#define GPIO19_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO19; +#define GPIO20_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO20; +#define GPIO21_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO21; +#define GPIO22_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO22; +#define GPIO23_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO23; +#define GPIO24_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO24; +#define GPIO25_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO25; +#define GPIO26_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO26; +#define GPIO27_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO27; +#define GPIO28_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO28; +#define GPIO29_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO29; diff --git a/ports/espressif/peripherals/pins.h b/ports/espressif/peripherals/pins.h index f185cee92c437..bbe42be1bcae2 100644 --- a/ports/espressif/peripherals/pins.h +++ b/ports/espressif/peripherals/pins.h @@ -52,6 +52,8 @@ extern const mp_obj_type_t mcu_pin_type; #include "esp32c3/pins.h" #elif defined(CONFIG_IDF_TARGET_ESP32C6) #include "esp32c6/pins.h" +#elif defined(CONFIG_IDF_TARGET_ESP32C61) +#include "esp32c61/pins.h" #elif defined(CONFIG_IDF_TARGET_ESP32P4) #include "esp32p4/pins.h" #elif defined(CONFIG_IDF_TARGET_ESP32H2) diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 48ae29ab4f927..72c92c98baea2 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -216,6 +216,11 @@ static void _never_reset_spi_ram_flash(void) { never_reset_pin_number(bootloader_flash_get_wp_pin()); } #endif // CONFIG_IDF_TARGET_ESP32 + #if defined(CONFIG_IDF_TARGET_ESP32C61) + #if defined(CONFIG_SPIRAM) + common_hal_never_reset_pin(&pin_GPIO14); + #endif + #endif } safe_mode_t port_init(void) { @@ -271,7 +276,7 @@ safe_mode_t port_init(void) { common_hal_never_reset_pin(&pin_GPIO40); common_hal_never_reset_pin(&pin_GPIO41); common_hal_never_reset_pin(&pin_GPIO42); - #elif defined(CONFIG_IDF_TARGET_ESP32P4) + #elif defined(CONFIG_IDF_TARGET_ESP32P4) || defined(CONFIG_IDF_TARGET_ESP32C61) common_hal_never_reset_pin(&pin_GPIO3); common_hal_never_reset_pin(&pin_GPIO4); common_hal_never_reset_pin(&pin_GPIO5); diff --git a/ports/espressif/tools/build_memory_info.py b/ports/espressif/tools/build_memory_info.py index 9a3c55501388e..cd9cc91427116 100644 --- a/ports/espressif/tools/build_memory_info.py +++ b/ports/espressif/tools/build_memory_info.py @@ -61,6 +61,12 @@ ("LP SRAM", (0x5000_0000,), 16 * 1024), ("HP SRAM", (0x4080_0000,), 512 * 1024), ], + "esp32c61": [ + # Name, Start, Length + ("LP SRAM", (0x5000_0000,), 16 * 1024), + ("HP SRAM", (0x4080_0000,), 320 * 1024), + ("PSRAM", (0x4200_0000,), 2 * 1024 * 1024), + ], "esp32h2": [ # Name, Start, Length ("LP SRAM", (0x5000_0000,), 4 * 1024), From cdd423b01a5d7b02fd5eea19bf871fb38c28195e Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 17 Sep 2025 12:02:23 -0700 Subject: [PATCH 165/517] Make string0 use configurable --- ports/espressif/mpconfigport.mk | 2 ++ py/circuitpy_defns.mk | 4 ++++ py/circuitpy_mpconfig.mk | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index 572e0579ef00a..f133e86c6e068 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -59,6 +59,8 @@ CIRCUITPY_PORT_SERIAL = 1 CIRCUITPY_LIB_TLSF = 0 +CIRCUITPY_LIBC_STRING0 = 0 + # These modules are implemented in ports//common-hal: CIRCUITPY__EVE ?= 1 CIRCUITPY_ALARM ?= 1 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index f3d25f6270cef..f4ac2350ffbf1 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -1003,6 +1003,10 @@ SRC_CIRCUITPY_COMMON = \ shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c +ifeq ($(CIRCUITPY_LIBC_STRING0),1) +SRC_CIRCUITPY_COMMON += shared/libc/string0.c +endif + ifeq ($(CIRCUITPY_QRIO),1) SRC_CIRCUITPY_COMMON += lib/quirc/lib/decode.c lib/quirc/lib/identify.c lib/quirc/lib/quirc.c lib/quirc/lib/version_db.c $(BUILD)/lib/quirc/lib/%.o: CFLAGS += -Wno-type-limits -Wno-shadow -Wno-sign-compare -include shared-module/qrio/quirc_alloc.h diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 76ee8a1d72bce..09ef4361427da 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -66,6 +66,11 @@ CIRCUITPY_MESSAGE_COMPRESSION_LEVEL ?= 9 # implementation of TLSF, which can be used instead by setting CIRCUITPY_LIB_TLSF=0. CIRCUITPY_LIB_TLSF ?= 1 +# By default, use our copy of string0 (memcpy and friends) because it is optimized. Some vendor SDKs +# or ROMs may provide their own implementation of string0, which can be used instead by setting +# CIRCUITPY_LIBC_STRING0=0. +CIRCUITPY_LIBC_STRING0 ?= 1 + # Reduce the size of in-flash properties. Requires support in the .ld linker # file, so not enabled by default. CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE ?= 0 From 6ebd5bbfa31965b14a78d95b14ede21c362d31be Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 23 Sep 2025 11:16:15 -0700 Subject: [PATCH 166/517] Fix PID check and C3 build --- ports/espressif/Makefile | 1 + tools/ci_check_duplicate_usb_vid_pid.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 930cdd961820b..0f3ff1ca7083a 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -277,6 +277,7 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32c3) LDFLAGS += \ -Tesp32c3.rom.newlib.ld \ + -Tesp32c3.rom.libc.ld \ -Tesp32c3.rom.version.ld \ -Tesp32c3.rom.eco3_bt_funcs.ld \ -Tesp32c3.rom.eco3.ld \ diff --git a/tools/ci_check_duplicate_usb_vid_pid.py b/tools/ci_check_duplicate_usb_vid_pid.py index 5361914af199b..38cce0f28be94 100644 --- a/tools/ci_check_duplicate_usb_vid_pid.py +++ b/tools/ci_check_duplicate_usb_vid_pid.py @@ -104,7 +104,7 @@ def check_vid_pid(files, clusterlist): """ usb_pattern = re.compile( - r"^CIRCUITPY_USB_DEVICE\s*=\s*0$|^IDF_TARGET = (esp32|esp32c2|esp32c3|esp32c6|esp32h2|esp32p4)$|^MCU_SERIES = MG24$", + r"^CIRCUITPY_USB_DEVICE\s*=\s*0$|^IDF_TARGET = (esp32|esp32c2|esp32c3|esp32c5|esp32c6|esp32c61|esp32h2|esp32p4)$|^MCU_SERIES = MG24$", flags=re.M, ) From 680fb207d141afe784ca0976ee27dcaa51e3b566 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 23 Sep 2025 15:31:15 -0700 Subject: [PATCH 167/517] Limit cryptography version to IDF limit --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 261a5cc2c7fe8..fb931079c524e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -25,7 +25,7 @@ intelhex # for building & testing natmods pyelftools -cryptography +cryptography<45 # for web workflow minify minify_html From 8190b409d20cfa8a10b06299d9b45908aa173d51 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 23 Sep 2025 16:15:32 -0700 Subject: [PATCH 168/517] Make room of IDF in S2 RTC slow memory --- ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults index 5c748bd0e6f02..101f90f9fa4fe 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults @@ -55,7 +55,7 @@ CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_TYPE_FSM=y CONFIG_ULP_COPROC_TYPE_RISCV=y # Note: enabling both ULPs simultaneously only works due to a modification of adafruit/esp-idf # (see adafruit/esp-idf/pull/16) until espressif/esp-idf/issues/12999 is fixed. -CONFIG_ULP_COPROC_RESERVE_MEM=8176 +CONFIG_ULP_COPROC_RESERVE_MEM=8144 # end of Ultra Low Power (ULP) Co-processor # From 2fd8589e2876adffa69f647ace904f3818e4e39e Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 24 Sep 2025 12:04:59 -0700 Subject: [PATCH 169/517] Fix C2, H2 and S3 builds --- ports/espressif/Makefile | 8 ++++++-- ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 0f3ff1ca7083a..b759fe3c0052a 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -205,7 +205,6 @@ CFLAGS += $(INC) -Werror -Wall -std=gnu11 -Wl,--gc-sections $(BASE_CFLAGS) $(C_D # Most current ESPs have nano versions of newlib in ROM so we use them. ifneq ($(IDF_TARGET),esp32c6) CFLAGS += --specs=nano.specs - LDFLAGS += -T$(IDF_TARGET).rom.newlib-nano.ld else LDFLAGS += -T$(IDF_TARGET).rom.newlib-normal.ld endif @@ -264,7 +263,9 @@ else ifeq ($(IDF_TARGET),esp32c2) LDFLAGS += \ -Tesp32c2.rom.ble.ld \ -Tesp32c2.rom.heap.ld \ + -Tesp32c2.rom.libc.ld \ -Tesp32c2.rom.newlib.ld \ + -Tesp32c2.rom.newlib-nano.ld \ -Tesp32c2.rom.version.ld \ -Tesp32c2.rom.systimer.ld \ -Tesp32c2.rom.wdt.ld @@ -321,6 +322,7 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32p4) LDFLAGS += \ + -Tesp32p4.rom.libc.ld \ -Tesp32p4.rom.newlib.ld \ -Tesp32p4.rom.systimer.ld \ -Tesp32p4.rom.wdt.ld @@ -332,6 +334,7 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32h2) LDFLAGS += \ -Tesp32h2.rom.heap.ld \ + -Tesp32h2.rom.libc.ld \ -Tesp32h2.rom.newlib.ld \ -Tesp32h2.rom.systimer.ld \ -Tesp32h2.rom.wdt.ld @@ -352,6 +355,7 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32s3) LDFLAGS += \ + -Tesp32s3.rom.libc.ld \ -Tesp32s3.rom.newlib.ld \ -Tesp32s3.rom.version.ld \ -Tesp32s3.rom.systimer.ld \ @@ -712,7 +716,7 @@ ifneq ($(NEEDS_COEX),0) ESP_IDF_COMPONENTS_LINK += esp_coex endif ifneq ($(CIRCUITPY_WIFI),0) - ESP_IDF_COMPONENTS_LINK += esp_coex esp_netif esp_security esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy + ESP_IDF_COMPONENTS_LINK += esp_netif esp_security esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy endif ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) BLE_IMPL_esp32 := esp32 diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults index fe3c3e0a2da88..2553c648018bd 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults @@ -95,7 +95,7 @@ CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_TYPE_FSM=y CONFIG_ULP_COPROC_TYPE_RISCV=y # Note: enabling both ULPs simultaneously only works due to a modification of adafruit/esp-idf # (see adafruit/esp-idf/pull/16) until espressif/esp-idf/issues/12999 is fixed. -CONFIG_ULP_COPROC_RESERVE_MEM=8176 +CONFIG_ULP_COPROC_RESERVE_MEM=8144 # end of Ultra Low Power (ULP) Co-processor # end of Component config From 4ea6bb7b3b929d8fb69391072fbe6f958cd8abca Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 24 Sep 2025 16:21:56 -0700 Subject: [PATCH 170/517] Tweak coex inclusion --- ports/espressif/Makefile | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index b759fe3c0052a..ed744da878af2 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -709,14 +709,13 @@ BINARY_BLOBS += esp-idf/components/esp_phy/lib/$(IDF_TARGET)/librtc.a endif ESP_IDF_COMPONENTS_LINK = $(IDF_TARGET_ARCH) $(CHIP_COMPONENTS) app_update bootloader_support driver esp_driver_gpio esp_driver_gptimer esp_driver_i2c esp_driver_ledc esp_driver_spi esp_driver_uart efuse esp_adc esp_app_format esp_common esp_event esp_hw_support esp_mm esp_partition esp_pm esp_ringbuf esp_rom esp_system esp_timer freertos hal heap log newlib nvs_flash pthread soc spi_flash vfs esp_vfs_console -NEEDS_COEX = $(CIRCUITPY_BLEIO_NATIVE) + $(CIRCUITPY_WIFI) -ifneq ($(NEEDS_COEX),0) - # esp_system_include_startup_funcs requires coexist as well BT regardless of wifi - BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a - ESP_IDF_COMPONENTS_LINK += esp_coex -endif ifneq ($(CIRCUITPY_WIFI),0) ESP_IDF_COMPONENTS_LINK += esp_netif esp_security esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy + ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) + # esp_system_include_startup_funcs requires coexist as well BT regardless of wifi + BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a + ESP_IDF_COMPONENTS_LINK += esp_coex + endif endif ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) BLE_IMPL_esp32 := esp32 From 40620146f4d8a8dd7eb82e5b82ea1b8ed46f798b Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 25 Sep 2025 16:37:39 -0700 Subject: [PATCH 171/517] Tweak coex inclusion (again). The wrapper is always needed with wifi. --- ports/espressif/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index ed744da878af2..4fbcd62bdc367 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -710,11 +710,9 @@ endif ESP_IDF_COMPONENTS_LINK = $(IDF_TARGET_ARCH) $(CHIP_COMPONENTS) app_update bootloader_support driver esp_driver_gpio esp_driver_gptimer esp_driver_i2c esp_driver_ledc esp_driver_spi esp_driver_uart efuse esp_adc esp_app_format esp_common esp_event esp_hw_support esp_mm esp_partition esp_pm esp_ringbuf esp_rom esp_system esp_timer freertos hal heap log newlib nvs_flash pthread soc spi_flash vfs esp_vfs_console ifneq ($(CIRCUITPY_WIFI),0) - ESP_IDF_COMPONENTS_LINK += esp_netif esp_security esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy + ESP_IDF_COMPONENTS_LINK += esp_coex esp_netif esp_security esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) - # esp_system_include_startup_funcs requires coexist as well BT regardless of wifi BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a - ESP_IDF_COMPONENTS_LINK += esp_coex endif endif ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) From fec20467062c723e31d90bf308b4b0778a26fc0c Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 26 Sep 2025 10:32:32 -0700 Subject: [PATCH 172/517] Formatting --- ports/espressif/common-hal/alarm/pin/PinAlarm.c | 8 ++++++-- ports/espressif/tools/build_memory_info.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ports/espressif/common-hal/alarm/pin/PinAlarm.c b/ports/espressif/common-hal/alarm/pin/PinAlarm.c index 2b09c630925c0..97ad3b2a94255 100644 --- a/ports/espressif/common-hal/alarm/pin/PinAlarm.c +++ b/ports/espressif/common-hal/alarm/pin/PinAlarm.c @@ -384,8 +384,12 @@ void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ob ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_FLOATING)); } gpio_int_type_t intr = GPIO_INTR_DISABLE; - if (high) intr = GPIO_INTR_HIGH_LEVEL; - if (low) intr = GPIO_INTR_LOW_LEVEL; + if (high) { + intr = GPIO_INTR_HIGH_LEVEL; + } + if (low) { + intr = GPIO_INTR_LOW_LEVEL; + } never_reset_pin_number(pin); gpio_wakeup_enable(pin, intr); gpio_set_intr_type(pin, intr); diff --git a/ports/espressif/tools/build_memory_info.py b/ports/espressif/tools/build_memory_info.py index cd9cc91427116..0cf609c4d63f4 100644 --- a/ports/espressif/tools/build_memory_info.py +++ b/ports/espressif/tools/build_memory_info.py @@ -65,7 +65,7 @@ # Name, Start, Length ("LP SRAM", (0x5000_0000,), 16 * 1024), ("HP SRAM", (0x4080_0000,), 320 * 1024), - ("PSRAM", (0x4200_0000,), 2 * 1024 * 1024), + ("PSRAM", (0x4200_0000,), 2 * 1024 * 1024), ], "esp32h2": [ # Name, Start, Length From c493f1b48054c74f99811c71686ecca0481db18e Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 26 Sep 2025 11:01:15 -0700 Subject: [PATCH 173/517] Always link coex with wifi. Shrink 2M C3 --- ports/espressif/Makefile | 4 +--- ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig | 5 +++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 4fbcd62bdc367..b6ddc27e4084c 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -699,6 +699,7 @@ ifneq ($(IDF_TARGET),esp32p4) endif ifneq ($(CIRCUITPY_WIFI),0) BINARY_BLOBS += $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, $(BINARY_WIFI_BLOBS)) + BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a ifneq ($(IDF_TARGET),esp32c2) BINARY_BLOBS += $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, libmesh.a libwapi.a) endif @@ -711,9 +712,6 @@ endif ESP_IDF_COMPONENTS_LINK = $(IDF_TARGET_ARCH) $(CHIP_COMPONENTS) app_update bootloader_support driver esp_driver_gpio esp_driver_gptimer esp_driver_i2c esp_driver_ledc esp_driver_spi esp_driver_uart efuse esp_adc esp_app_format esp_common esp_event esp_hw_support esp_mm esp_partition esp_pm esp_ringbuf esp_rom esp_system esp_timer freertos hal heap log newlib nvs_flash pthread soc spi_flash vfs esp_vfs_console ifneq ($(CIRCUITPY_WIFI),0) ESP_IDF_COMPONENTS_LINK += esp_coex esp_netif esp_security esp-tls esp_wifi lwip mbedtls mdns wpa_supplicant esp_phy - ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) - BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a - endif endif ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) BLE_IMPL_esp32 := esp32 diff --git a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig index e962866216039..15b64d0ce2ebe 100644 --- a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig +++ b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig @@ -9,6 +9,11 @@ # # end of LWIP +# +# Wireless Coexistence +# +# CONFIG_ESP_COEX_SW_COEXIST_ENABLE is not set +# end of Wireless Coexistence # end of Component config # end of Espressif IoT Development Framework Configuration From b0f3a238f820a15e8541aced8c27edcae294395c Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 26 Sep 2025 15:52:06 -0700 Subject: [PATCH 174/517] Disable memory speed optimization on 2m board. Also disable dynamic log level on opt builds --- .../boards/ai_thinker_esp32-c3s-2m/sdkconfig | 7 +++++++ .../esp-idf-config/sdkconfig-opt.defaults | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig index 15b64d0ce2ebe..5fd531f274a6a 100644 --- a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig +++ b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig @@ -14,6 +14,13 @@ # # CONFIG_ESP_COEX_SW_COEXIST_ENABLE is not set # end of Wireless Coexistence + +# +# LibC +# +# CONFIG_LIBC_OPTIMIZED_MISALIGNED_ACCESS is not set +# end of LibC + # end of Component config # end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/esp-idf-config/sdkconfig-opt.defaults b/ports/espressif/esp-idf-config/sdkconfig-opt.defaults index 16f5b990386a1..0da23e8122762 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-opt.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-opt.defaults @@ -38,6 +38,24 @@ CONFIG_ESP_CONSOLE_SECONDARY_NONE=y CONFIG_LOG_DEFAULT_LEVEL_NONE=y # end of Log output +# +# Log +# +# +# Log Level +# +# +# Level Settings +# +# CONFIG_LOG_DYNAMIC_LEVEL_CONTROL is not set +CONFIG_LOG_TAG_LEVEL_IMPL_NONE=y +# CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST is not set +# end of Level Settings + +# end of Log Level + +# end of Log + # end of Component config # end of Espressif IoT Development Framework Configuration From 9dbe0d1aa886114eb334b5ecbd3b93f091782bf6 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 6 Oct 2025 11:49:39 -0700 Subject: [PATCH 175/517] Don't duplicate USB callbacks. --- lib/tinyusb | 2 +- ports/espressif/supervisor/usb.c | 25 ----------------------- ports/espressif/tools/decode_backtrace.py | 2 +- supervisor/shared/usb/usb_device.c | 10 +++++++++ 4 files changed, 12 insertions(+), 27 deletions(-) diff --git a/lib/tinyusb b/lib/tinyusb index 8304587d77745..c1bf19ed6cf1e 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 8304587d7774526a03c6881d11e6d6208fe759be +Subproject commit c1bf19ed6cf1eaa791f221c1bc5ce4b3d069f76d diff --git a/ports/espressif/supervisor/usb.c b/ports/espressif/supervisor/usb.c index 612abaa808ae2..274542ef22a7d 100644 --- a/ports/espressif/supervisor/usb.c +++ b/ports/espressif/supervisor/usb.c @@ -54,31 +54,6 @@ static void usb_device_task(void *param) { vTaskDelay(1); } } - -/** - * Callback invoked when received an "wanted" char. - * @param itf Interface index (for multiple cdc interfaces) - * @param wanted_char The wanted char (set previously) - */ -void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { - (void)itf; // not used - // CircuitPython's VM is run in a separate FreeRTOS task from TinyUSB. - // So, we must notify the other task when a CTRL-C is received. - port_wake_main_task(); - // Workaround for using shared/runtime/interrupt_char.c - // Compare mp_interrupt_char with wanted_char and ignore if not matched - if (mp_interrupt_char == wanted_char) { - tud_cdc_read_flush(); // flush read fifo - mp_sched_keyboard_interrupt(); - } -} - -void tud_cdc_rx_cb(uint8_t itf) { - (void)itf; - // Workaround for "press any key to enter REPL" response being delayed on espressif. - // Wake main task when any key is pressed. - port_wake_main_task(); -} #endif // CIRCUITPY_USB_DEVICE void init_usb_hardware(void) { diff --git a/ports/espressif/tools/decode_backtrace.py b/ports/espressif/tools/decode_backtrace.py index 1733c69b5bb57..d597741a59676 100644 --- a/ports/espressif/tools/decode_backtrace.py +++ b/ports/espressif/tools/decode_backtrace.py @@ -24,7 +24,7 @@ addresses = addresses[len("Backtrace:") :] addresses = addresses.strip().split() addresses = [address.split(":")[0] for address in addresses] - if addresses.startswith("Stack memory:"): + elif addresses.startswith("Stack memory:"): addresses = [] extra_lines = sys.stdin.readlines() for line in extra_lines: diff --git a/supervisor/shared/usb/usb_device.c b/supervisor/shared/usb/usb_device.c index ec08b8bf4d7e2..16ae137eddefb 100644 --- a/supervisor/shared/usb/usb_device.c +++ b/supervisor/shared/usb/usb_device.c @@ -166,6 +166,9 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ * @param wanted_char The wanted char (set previously) */ void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { + // CircuitPython's VM is run in a separate FreeRTOS task from TinyUSB on ESP. + // So, we must notify the other task when a CTRL-C is received. + port_wake_main_task(); // Workaround for using shared/runtime/interrupt_char.c // Compare mp_interrupt_char with wanted_char and ignore if not matched if (mp_interrupt_char == wanted_char) { @@ -177,7 +180,14 @@ void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) { if (usb_cdc_console_enabled() && mp_interrupt_char != -1 && itf == 0 && duration_ms > 0) { mp_sched_keyboard_interrupt(); + port_wake_main_task(); } } +void tud_cdc_rx_cb(uint8_t itf) { + (void)itf; + // Workaround for "press any key to enter REPL" response being delayed on espressif. + // Wake main task when any key is pressed. + port_wake_main_task(); +} #endif From 802380d381f44583355e6fbc292a2a30e0140a7b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 6 Oct 2025 19:01:50 -0400 Subject: [PATCH 176/517] py/misc.h: prevent clang warning --- py/misc.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/py/misc.h b/py/misc.h index eb7fc54be6fec..555e58dd14fc9 100644 --- a/py/misc.h +++ b/py/misc.h @@ -54,7 +54,14 @@ typedef unsigned int uint; #define MP_STRINGIFY(x) MP_STRINGIFY_HELPER(x) // Static assertion macro +#if __cplusplus +#define MP_STATIC_ASSERT(cond) static_assert((cond), #cond) +#elif __GNUC__ >= 5 || __STDC_VERSION__ >= 201112L +#define MP_STATIC_ASSERT(cond) _Static_assert((cond), #cond) +#else #define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)])) +#endif + // In C++ things like comparing extern const pointers are not constant-expressions so cannot be used // in MP_STATIC_ASSERT. Note that not all possible compiler versions will reject this. Some gcc versions // do, others only with -Werror=vla, msvc always does. @@ -63,7 +70,10 @@ typedef unsigned int uint; #if defined(_MSC_VER) || defined(__cplusplus) #define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)1) #else -#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) MP_STATIC_ASSERT(cond) +#if __clang__ +#pragma GCC diagnostic ignored "-Wgnu-folding-constant" +#endif +#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)sizeof(char[1 - 2 * !(cond)])) #endif // Round-up integer division From 2e1c6169c1e44d8859f2f0d3b9adb62aa3a84134 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 6 Oct 2025 20:40:21 -0400 Subject: [PATCH 177/517] py/misc.h: test for __cplusplus and __clang__ more carefully --- py/misc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/py/misc.h b/py/misc.h index 555e58dd14fc9..22f32147124fc 100644 --- a/py/misc.h +++ b/py/misc.h @@ -54,7 +54,7 @@ typedef unsigned int uint; #define MP_STRINGIFY(x) MP_STRINGIFY_HELPER(x) // Static assertion macro -#if __cplusplus +#if defined(__cplusplus) #define MP_STATIC_ASSERT(cond) static_assert((cond), #cond) #elif __GNUC__ >= 5 || __STDC_VERSION__ >= 201112L #define MP_STATIC_ASSERT(cond) _Static_assert((cond), #cond) @@ -70,7 +70,7 @@ typedef unsigned int uint; #if defined(_MSC_VER) || defined(__cplusplus) #define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)1) #else -#if __clang__ +#if defined(__clang__) #pragma GCC diagnostic ignored "-Wgnu-folding-constant" #endif #define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)sizeof(char[1 - 2 * !(cond)])) From d5d6a8ef97fed4850a76c5bc0247a212c7304400 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 7 Oct 2025 11:04:57 -0400 Subject: [PATCH 178/517] use MP_STATIC_ASSERT_NONCONSTEXPR in ports/analog --- ports/analog/common-hal/digitalio/DigitalInOut.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/analog/common-hal/digitalio/DigitalInOut.c b/ports/analog/common-hal/digitalio/DigitalInOut.c index 7d4048d77e70e..93e2242fbb6f6 100644 --- a/ports/analog/common-hal/digitalio/DigitalInOut.c +++ b/ports/analog/common-hal/digitalio/DigitalInOut.c @@ -107,7 +107,7 @@ digitalio_direction_t common_hal_digitalio_digitalinout_get_direction( if (self->pin->port < 4) { // Check that I/O mode is enabled and we don't have in AND out on at the same time - MP_STATIC_ASSERT(!((port->en0 & mask) && (port->inen & mask) && (port->outen & mask))); + MP_STATIC_ASSERT_NONCONSTEXPR(!((port->en0 & mask) && (port->inen & mask) && (port->outen & mask))); if ((port->en0 & mask) && (port->outen & mask)) { return DIRECTION_OUTPUT; From e2cbc2db3d4a3be0e9600e935339081be212f9f0 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 7 Oct 2025 16:45:51 -0400 Subject: [PATCH 179/517] Fix filesystem writability from USB --- extmod/vfs.h | 2 ++ extmod/vfs_fat.c | 11 ++++------- extmod/vfs_fat_diskio.c | 6 +++++- main.c | 18 +++++++++++------- supervisor/filesystem.h | 1 + supervisor/shared/filesystem.c | 14 ++++++++++++-- supervisor/shared/usb/usb_msc_flash.c | 2 +- 7 files changed, 36 insertions(+), 18 deletions(-) diff --git a/extmod/vfs.h b/extmod/vfs.h index 67d5d9239a33e..699232e56cd87 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -52,6 +52,8 @@ #define MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED (0x0020) // Bit set when something has claimed the right to mutate the blockdev. #define MP_BLOCKDEV_FLAG_LOCKED (0x0040) +// Ignore write protections. Used to override other flags temporarily. +#define MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION (0x0080) // constants for block protocol ioctl #define MP_BLOCKDEV_IOCTL_INIT (1) diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 5011a820af864..6267452cd32c7 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -427,13 +427,10 @@ static MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_statvfs_obj, fat_vfs_statvfs); static mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) { fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); - // Read-only device indicated by writeblocks[0] == MP_OBJ_NULL. - // User can specify read-only device by: - // 1. readonly=True keyword argument - // 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already) - if (mp_obj_is_true(readonly)) { - self->blockdev.writeblocks[0] = MP_OBJ_NULL; - } + // CIRCUITPY-CHANGE: Use MP_BLOCKDEV_FLAG_USB_WRITABLE instead of writeblocks[0] =/!= MP_OBJ_NULL + // to specify read-write. + // If readonly to Python, it's writable by USB and vice versa. + filesystem_set_writable_by_usb(self, mp_obj_is_true(readonly)); // check if we need to make the filesystem FRESULT res = (self->blockdev.flags & MP_BLOCKDEV_FLAG_NO_FILESYSTEM) ? FR_NO_FILESYSTEM : FR_OK; diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c index 6f500104d1e86..591da07eb5630 100644 --- a/extmod/vfs_fat_diskio.c +++ b/extmod/vfs_fat_diskio.c @@ -43,6 +43,9 @@ #include "lib/oofatfs/diskio.h" #include "extmod/vfs_fat.h" +// CIRCUITPY-CHANGE +#include "supervisor/filesystem.h" + typedef void *bdev_t; static fs_user_mount_t *disk_get_device(void *bdev) { return (fs_user_mount_t *)bdev; @@ -153,7 +156,8 @@ DRESULT disk_ioctl( if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) { // error initialising stat = STA_NOINIT; - } else if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL) { + // CIRCUITPY-CHANGE: writability from Python check + } else if (!filesystem_is_writable_by_python(vfs)) { stat = STA_PROTECT; } else { stat = 0; diff --git a/main.c b/main.c index d7f3c7aaf1bce..91687ff2f8bc7 100644 --- a/main.c +++ b/main.c @@ -863,6 +863,14 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { boot_output = &boot_text; #endif + // Get the base filesystem. + fs_user_mount_t *vfs = filesystem_circuitpy(); + FATFS *fs = &vfs->fatfs; + + // Allow boot.py access to CIRCUITPY, and allow writes to boot_out.txt. + // We can't use the regular flags for this, because they might get modified inside boot.py. + filesystem_set_ignore_write_protection(vfs, true); + // Write version info mp_printf(&mp_plat_print, "%s\nBoard ID:%s\n", MICROPY_FULL_VERSION_INFO, CIRCUITPY_BOARD_ID); #if CIRCUITPY_MICROCONTROLLER && COMMON_HAL_MCU_PROCESSOR_UID_LENGTH > 0 @@ -881,10 +889,6 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - // Get the base filesystem. - fs_user_mount_t *vfs = filesystem_circuitpy(); - FATFS *fs = &vfs->fatfs; - boot_output = NULL; #if CIRCUITPY_STATUS_BAR supervisor_status_bar_resume(); @@ -906,9 +910,6 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { // in case power is momentary or will fail shortly due to, say a low, battery. mp_hal_delay_ms(1000); - // USB isn't up, so we can write the file. - // operating at the oofatfs (f_open) layer means the usb concurrent write permission - // is not even checked! f_open(fs, &boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); UINT chars_written; f_write(&boot_output_file, boot_text.buf, boot_text.len, &chars_written); @@ -916,6 +917,9 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { filesystem_flush(); } #endif + + // Back to regular filesystem protections. + filesystem_set_ignore_write_protection(vfs, false); } cleanup_after_vm(_exec_result.exception); diff --git a/supervisor/filesystem.h b/supervisor/filesystem.h index a0445b0510da9..30cd83d443815 100644 --- a/supervisor/filesystem.h +++ b/supervisor/filesystem.h @@ -21,6 +21,7 @@ void filesystem_set_internal_writable_by_usb(bool usb_writable); void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection); void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable); void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concurrent_write_protection); +void filesystem_set_ignore_write_protection(fs_user_mount_t *vfs, bool ignore_write_protection); // Whether user code can modify the filesystem. It doesn't depend on the state // of USB. Don't use this for a workflow. In workflows, grab the shared file diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index 24c7f9bffc552..025c9ddbd6de3 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -248,12 +248,14 @@ void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable) { bool filesystem_is_writable_by_python(fs_user_mount_t *vfs) { return ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0) || - ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) == 0); + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) == 0) || + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION) != 0); } bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) { return ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0) || - ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) != 0); + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) != 0) || + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION) != 0); } void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection) { @@ -268,6 +270,14 @@ void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concu } } +void filesystem_set_ignore_write_protection(fs_user_mount_t *vfs, bool ignore_write_protection) { + if (ignore_write_protection) { + vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION; + } else { + vfs->blockdev.flags &= ~MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION; + } +} + bool filesystem_present(void) { return _circuitpy_vfs.len > 0; } diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index e4e4801de9c88..febede876bbf5 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -249,7 +249,7 @@ bool tud_msc_is_writable_cb(uint8_t lun) { if (vfs == NULL) { return false; } - if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL || !filesystem_is_writable_by_usb(vfs)) { + if (!filesystem_is_writable_by_usb(vfs)) { return false; } // Lock the blockdev once we say we're writable. From a6a6ebe5892343e59056b0178c3369cb1ac4505d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 7 Oct 2025 17:03:16 -0700 Subject: [PATCH 180/517] Fix C6 by disabling hardware SHA --- ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults index 85dde905f3caa..63f42cac95244 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults @@ -49,6 +49,12 @@ CONFIG_ESP_PHY_ENABLE_USB=y CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=4 # end of Wi-Fi +# +# mbedTLS +# +# CONFIG_MBEDTLS_HARDWARE_SHA is not set +# end of mbedTLS + # end of Component config # end of Espressif IoT Development Framework Configuration From a55305eae9aebfecff91410951eecafbe1f505f9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 9 Oct 2025 12:32:08 -0400 Subject: [PATCH 181/517] new location of importlib Traversable --- tools/board_stubs/circuitpython_setboard/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/board_stubs/circuitpython_setboard/__init__.py b/tools/board_stubs/circuitpython_setboard/__init__.py index 3a73beb3119df..69a405405b800 100644 --- a/tools/board_stubs/circuitpython_setboard/__init__.py +++ b/tools/board_stubs/circuitpython_setboard/__init__.py @@ -12,7 +12,12 @@ import shutil from collections import defaultdict from importlib import resources -from importlib.abc import Traversable + +try: + from importlib.resources.abc import Traversable +except ModuleNotFoundError: + # 3.10 and earlier. + from importlib.abc import Traversable def get_definitions_or_exit(board: str) -> Traversable: From 6a1a59b84d8d3d8d2a766d898e88d0d87c11cf18 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 9 Oct 2025 11:48:13 -0500 Subject: [PATCH 182/517] docs: add TilePaletteMapper to pixel_shader allowed types. --- shared-bindings/displayio/TileGrid.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared-bindings/displayio/TileGrid.c b/shared-bindings/displayio/TileGrid.c index eddf3f66c4ec0..560fb66a67553 100644 --- a/shared-bindings/displayio/TileGrid.c +++ b/shared-bindings/displayio/TileGrid.c @@ -49,7 +49,7 @@ void displayio_tilegrid_validate_pixel_shader(mp_obj_t pixel_shader) { //| self, //| bitmap: Union[Bitmap, OnDiskBitmap], //| *, -//| pixel_shader: Union[ColorConverter, Palette], +//| pixel_shader: Union[ColorConverter, Palette, tilepalettemapper.TilePaletteMapper], //| width: int = 1, //| height: int = 1, //| tile_width: Optional[int] = None, @@ -68,7 +68,7 @@ void displayio_tilegrid_validate_pixel_shader(mp_obj_t pixel_shader) { //| tile_width and tile_height match the height of the bitmap by default. //| //| :param Bitmap,OnDiskBitmap bitmap: The bitmap storing one or more tiles. -//| :param ColorConverter,Palette pixel_shader: The pixel shader that produces colors from values +//| :param ColorConverter,Palette,tilepalettemapper.TilePaletteMapper pixel_shader: The pixel shader that produces colors from values //| :param int width: Width of the grid in tiles. //| :param int height: Height of the grid in tiles. //| :param int tile_width: Width of a single tile in pixels. Defaults to the full Bitmap and must evenly divide into the Bitmap's dimensions. @@ -330,7 +330,7 @@ static mp_obj_t displayio_tilegrid_obj_contains(mp_obj_t self_in, mp_obj_t touch } MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_contains_obj, displayio_tilegrid_obj_contains); -//| pixel_shader: Union[ColorConverter, Palette] +//| pixel_shader: Union[ColorConverter, Palette, tilepalettemapper.TilePaletteMapper] //| """The pixel shader of the tilegrid.""" static mp_obj_t displayio_tilegrid_obj_get_pixel_shader(mp_obj_t self_in) { displayio_tilegrid_t *self = native_tilegrid(self_in); From 611b887a8113c71a504a1893ac8b07f64f55c058 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 15 Oct 2025 11:09:57 -0400 Subject: [PATCH 183/517] Fix SPI DMA on SAMD --- ports/atmel-samd/audio_dma.c | 18 ++++++------- .../atmel-samd/common-hal/audiobusio/PDMIn.c | 2 +- ports/atmel-samd/common-hal/busio/SPI.c | 27 ++++++++++--------- .../imagecapture/ParallelImageCapture.c | 3 ++- .../common-hal/spitarget/SPITarget.c | 10 +++---- .../common-hal/spitarget/SPITarget.h | 2 +- ports/atmel-samd/peripherals | 2 +- 7 files changed, 34 insertions(+), 30 deletions(-) diff --git a/ports/atmel-samd/audio_dma.c b/ports/atmel-samd/audio_dma.c index e25b74d9a2893..e39804015063b 100644 --- a/ports/atmel-samd/audio_dma.c +++ b/ports/atmel-samd/audio_dma.c @@ -39,14 +39,14 @@ uint8_t find_sync_event_channel_raise(void) { } void audio_dma_disable_channel(uint8_t channel) { - if (channel >= AUDIO_DMA_CHANNEL_COUNT) { + if (channel == NO_DMA_CHANNEL) { return; } dma_disable_channel(channel); } void audio_dma_enable_channel(uint8_t channel) { - if (channel >= AUDIO_DMA_CHANNEL_COUNT) { + if (channel == NO_DMA_CHANNEL) { return; } dma_enable_channel(channel); @@ -171,8 +171,8 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, bool output_signed, uint32_t output_register_address, uint8_t dma_trigger_source) { - uint8_t dma_channel = dma_allocate_channel(true); - if (dma_channel >= AUDIO_DMA_CHANNEL_COUNT) { + uint8_t dma_channel = dma_allocate_audio_channel(); + if (dma_channel == NO_DMA_CHANNEL) { return AUDIO_DMA_DMA_BUSY; } @@ -298,14 +298,14 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, void audio_dma_stop(audio_dma_t *dma) { uint8_t channel = dma->dma_channel; - if (channel < AUDIO_DMA_CHANNEL_COUNT) { + if (channel != NO_DMA_CHANNEL) { audio_dma_disable_channel(channel); disable_event_channel(dma->event_channel); MP_STATE_PORT(playing_audio)[channel] = NULL; audio_dma_state[channel] = NULL; dma_free_channel(dma->dma_channel); } - dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT; + dma->dma_channel = NO_DMA_CHANNEL; dma->playing_in_progress = false; } @@ -318,7 +318,7 @@ void audio_dma_resume(audio_dma_t *dma) { } bool audio_dma_get_paused(audio_dma_t *dma) { - if (dma->dma_channel >= AUDIO_DMA_CHANNEL_COUNT) { + if (dma->dma_channel == NO_DMA_CHANNEL) { return false; } uint32_t status = dma_transfer_status(dma->dma_channel); @@ -327,7 +327,7 @@ bool audio_dma_get_paused(audio_dma_t *dma) { } void audio_dma_init(audio_dma_t *dma) { - dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT; + dma->dma_channel = NO_DMA_CHANNEL; } void audio_dma_reset(void) { @@ -341,7 +341,7 @@ void audio_dma_reset(void) { } bool audio_dma_get_playing(audio_dma_t *dma) { - if (dma->dma_channel >= AUDIO_DMA_CHANNEL_COUNT) { + if (dma->dma_channel == NO_DMA_CHANNEL) { return false; } return dma->playing_in_progress; diff --git a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c index a1a67d0408247..f409563f5ed69 100644 --- a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c +++ b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c @@ -364,7 +364,7 @@ static uint16_t filter_sample(uint32_t pdm_samples[4]) { // output_buffer_length is the number of slots, not the number of bytes. uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t *self, uint16_t *output_buffer, uint32_t output_buffer_length) { - uint8_t dma_channel = dma_allocate_channel(true); + uint8_t dma_channel = dma_allocate_audio_channel(); pdmin_event_channel = find_sync_event_channel_raise(); pdmin_dma_block_done = false; diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index 5d7633be7f8d3..5dd7bedebc394 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -21,7 +21,17 @@ #include "samd/dma.h" #include "samd/sercom.h" -void setup_pin(const mcu_pin_obj_t *pin, uint32_t pinmux); + +static void setup_pin(const mcu_pin_obj_t *pin, uint32_t pinmux, const enum gpio_direction direction) { + gpio_set_pin_direction(pin->number, direction); + gpio_set_pin_pull_mode(pin->number, GPIO_PULL_OFF); + gpio_set_pin_function(pin->number, pinmux); + if (direction == GPIO_DIRECTION_OUT) { + // Use strong drive strength for SPI outputs. + hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(pin->number), GPIO_PIN(pin->number)); + } + claim_pin(pin); +} void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, @@ -128,6 +138,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, // Pads must be set after spi_m_sync_init(), which uses default values from // the prototypical SERCOM. + // Set to SPI host mode and choose pads. hri_sercomspi_write_CTRLA_MODE_bf(sercom, 3); hri_sercomspi_write_CTRLA_DOPO_bf(sercom, dopo); hri_sercomspi_write_CTRLA_DIPO_bf(sercom, miso_pad); @@ -141,20 +152,20 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_OSError(MP_EIO); } - setup_pin(clock, clock_pinmux); + setup_pin(clock, clock_pinmux, GPIO_DIRECTION_OUT); self->clock_pin = clock->number; if (mosi_none) { self->MOSI_pin = NO_PIN; } else { - setup_pin(mosi, mosi_pinmux); + setup_pin(mosi, mosi_pinmux, GPIO_DIRECTION_OUT); self->MOSI_pin = mosi->number; } if (miso_none) { self->MISO_pin = NO_PIN; } else { - setup_pin(miso, miso_pinmux); + setup_pin(miso, miso_pinmux, GPIO_DIRECTION_IN); self->MISO_pin = miso->number; } @@ -317,11 +328,3 @@ uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { void *hw = self->spi_desc.dev.prvt; return hri_sercomspi_get_CTRLA_CPOL_bit(hw); } - -void setup_pin(const mcu_pin_obj_t *pin, uint32_t pinmux) { - gpio_set_pin_direction(pin->number, GPIO_DIRECTION_OUT); - gpio_set_pin_pull_mode(pin->number, GPIO_PULL_OFF); - gpio_set_pin_function(pin->number, pinmux); - claim_pin(pin); - hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(pin->number), GPIO_PIN(pin->number)); -} diff --git a/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c index a6d8fef0f394b..56eb82678ec01 100644 --- a/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +++ b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c @@ -135,7 +135,8 @@ void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecaptur mp_buffer_info_t bufinfo; mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_RW); - uint8_t dma_channel = dma_allocate_channel(true); + // Allocate a permanent channel (not really audio). + uint8_t dma_channel = dma_allocate_audio_channel(); uint32_t *dest = bufinfo.buf; size_t count = bufinfo.len / 4; // PCC receives 4 bytes (2 pixels) at a time diff --git a/ports/atmel-samd/common-hal/spitarget/SPITarget.c b/ports/atmel-samd/common-hal/spitarget/SPITarget.c index b9062911cc353..e5011b141fc12 100644 --- a/ports/atmel-samd/common-hal/spitarget/SPITarget.c +++ b/ports/atmel-samd/common-hal/spitarget/SPITarget.c @@ -192,7 +192,7 @@ void common_hal_spitarget_spi_target_transfer_start(spitarget_spi_target_obj_t * self->miso_packet = miso_packet; Sercom *sercom = self->spi_desc.dev.prvt; - self->running_dma = shared_dma_transfer_start(sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0); + shared_dma_transfer_start(&self->running_dma, sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0); // There is an issue where if an unexpected SPI transfer is received before the user calls "end" for the in-progress, expected // transfer, the SERCOM has an error and gets confused. This can be detected from INTFLAG.ERROR. I think the code in @@ -200,7 +200,7 @@ void common_hal_spitarget_spi_target_transfer_start(spitarget_spi_target_obj_t * // s->SPI.DATA.reg) is supposed to fix this, but experimentation seems to show that it does not in fact fix anything. Anyways, if // the ERROR bit is set, let's just reset the peripheral and then setup the transfer again -- that seems to work. if (hri_sercomspi_get_INTFLAG_ERROR_bit(sercom)) { - shared_dma_transfer_close(self->running_dma); + shared_dma_transfer_close(&self->running_dma); // disable the sercom spi_m_sync_disable(&self->spi_desc); @@ -223,19 +223,19 @@ void common_hal_spitarget_spi_target_transfer_start(spitarget_spi_target_obj_t * spi_m_sync_enable(&self->spi_desc); hri_sercomspi_wait_for_sync(sercom, SERCOM_SPI_SYNCBUSY_MASK); - self->running_dma = shared_dma_transfer_start(sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0); + shared_dma_transfer_start(&self->running_dma, sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0); } } bool common_hal_spitarget_spi_target_transfer_is_finished(spitarget_spi_target_obj_t *self) { - return self->running_dma.failure == 1 || shared_dma_transfer_finished(self->running_dma); + return self->running_dma.failure == 1 || shared_dma_transfer_finished(&self->running_dma); } int common_hal_spitarget_spi_target_transfer_close(spitarget_spi_target_obj_t *self) { if (self->running_dma.failure == 1) { return 0; } - int res = shared_dma_transfer_close(self->running_dma); + int res = shared_dma_transfer_close(&self->running_dma); self->running_dma.failure = 1; self->mosi_packet = NULL; diff --git a/ports/atmel-samd/common-hal/spitarget/SPITarget.h b/ports/atmel-samd/common-hal/spitarget/SPITarget.h index 50f2bcb33094b..9fff23c174370 100644 --- a/ports/atmel-samd/common-hal/spitarget/SPITarget.h +++ b/ports/atmel-samd/common-hal/spitarget/SPITarget.h @@ -18,7 +18,7 @@ typedef struct { uint8_t *mosi_packet; const uint8_t *miso_packet; - dma_descr_t running_dma; + dma_transfer_t running_dma; } spitarget_spi_target_obj_t; #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_TARGET_H diff --git a/ports/atmel-samd/peripherals b/ports/atmel-samd/peripherals index d3210221bbd01..a1f1504d41438 160000 --- a/ports/atmel-samd/peripherals +++ b/ports/atmel-samd/peripherals @@ -1 +1 @@ -Subproject commit d3210221bbd018ae9d0183ea4640c42cf4bce672 +Subproject commit a1f1504d414387fc3ff48dc2ba98fd7af4461a63 From c8377c75533cb25f18b74f605e24adeaca3059fa Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 16 Oct 2025 13:50:05 -0400 Subject: [PATCH 184/517] incorporate https://github.com/adafruit/samd-peripherals/pull/47 --- ports/atmel-samd/peripherals | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/atmel-samd/peripherals b/ports/atmel-samd/peripherals index a1f1504d41438..863e615ff98c3 160000 --- a/ports/atmel-samd/peripherals +++ b/ports/atmel-samd/peripherals @@ -1 +1 @@ -Subproject commit a1f1504d414387fc3ff48dc2ba98fd7af4461a63 +Subproject commit 863e615ff98c3a8aa904e87a157553559c933b81 From a2d9e9e68b482d04c045cb53455e565a8b4292e7 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 16 Oct 2025 14:38:43 -0400 Subject: [PATCH 185/517] Restore atmel-samd SD card USB presentation --- ports/atmel-samd/mpconfigport.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/ports/atmel-samd/mpconfigport.h b/ports/atmel-samd/mpconfigport.h index e953ea0fc50d4..087e0bc7d6820 100644 --- a/ports/atmel-samd/mpconfigport.h +++ b/ports/atmel-samd/mpconfigport.h @@ -11,9 +11,6 @@ // Definitions that control circuitpy_mpconfig.h: -// On SAMD, presenting the SD card as a second LUN causes USB disconnect. This needs to be fixed eventually. -#define CIRCUITPY_SDCARD_USB (0) - //////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef SAMD21 From 5df18c37b93fa7853494a8cfc1483488ca04eefd Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 16 Oct 2025 12:01:06 -0700 Subject: [PATCH 186/517] Clean up two leftovers from debugging --- ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv | 3 ++- ports/espressif/tools/decode_backtrace.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv b/ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv index 44121812ea2bf..6d78fe8af5916 100644 --- a/ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv +++ b/ports/espressif/esp-idf-config/partitions-8MB-no-uf2.csv @@ -3,5 +3,6 @@ # partition_table, data, table, 0x8000, 4K nvs, data, nvs, 0x9000, 20K otadata, data, ota, 0xe000, 8K -ota_0, app, ota_0, 0x10000, 4096K +ota_0, app, ota_0, 0x10000, 2048K +ota_1, app, ota_1, 0x210000, 2048K user_fs, data, fat, 0x410000, 4032K diff --git a/ports/espressif/tools/decode_backtrace.py b/ports/espressif/tools/decode_backtrace.py index d597741a59676..16cef9e0822cc 100644 --- a/ports/espressif/tools/decode_backtrace.py +++ b/ports/espressif/tools/decode_backtrace.py @@ -14,7 +14,9 @@ elfs = [ f"build-{board}/firmware.elf", - "/home/tannewt/Downloads/esp-rom-elfs-20241011/esp32c6_rev0_rom.elf", + # Add additional ELF files here such as the ROM ELF files from: + # https://github.com/espressif/esp-rom-elfs/releases + # "/home/tannewt/Downloads/esp-rom-elfs-20241011/esp32c6_rev0_rom.elf", ] while True: From 1786e9abc96ae007aa4d31a25c6238d790168f4a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 17 Oct 2025 13:37:16 -0400 Subject: [PATCH 187/517] restrict Python cryptography version --- requirements-dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 261a5cc2c7fe8..0e8426d5174d4 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -25,7 +25,8 @@ intelhex # for building & testing natmods pyelftools -cryptography +# newer versions break ESP-IDF now +cryptography<45 # for web workflow minify minify_html From f8cb53c367cf4b3bfe348112848d5ec358a033cf Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 17 Oct 2025 11:27:06 -0700 Subject: [PATCH 188/517] Fix overriding delay without an OS --- shared-module/max3421e/Max3421E.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-module/max3421e/Max3421E.c b/shared-module/max3421e/Max3421E.c index 4946e4e7d8dfa..3448cdd4c7596 100644 --- a/shared-module/max3421e/Max3421E.c +++ b/shared-module/max3421e/Max3421E.c @@ -83,7 +83,7 @@ void common_hal_max3421e_max3421e_deinit(max3421e_max3421e_obj_t *self) { // anyway. Don't run background tasks because this function is used by // tuh_task() which is run as a background task. #if CFG_TUSB_OS == OPT_OS_NONE -void osal_task_delay(uint32_t msec) { +void tusb_time_delay_ms_api(uint32_t msec) { uint32_t end_time = common_hal_time_monotonic_ms() + msec; while (common_hal_time_monotonic_ms() < end_time) { if (tuh_callback.prev != NULL) { From 33b8aca6b2025cf507a3f8408311dac072b959b2 Mon Sep 17 00:00:00 2001 From: RetiredWizard Date: Wed, 22 Oct 2025 16:35:39 -0400 Subject: [PATCH 189/517] make jpegio unsupported format error more helpful --- locale/circuitpython.pot | 16 ++++------------ shared-module/jpegio/JpegDecoder.c | 2 +- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 1e167c30353dd..86587e0a4e804 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -159,10 +159,6 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" -#: py/runtime.c -msgid "%q moved from %q to %q" -msgstr "" - #: py/argcheck.c msgid "%q must be %d" msgstr "" @@ -1644,10 +1640,6 @@ msgstr "" msgid "Not playing" msgstr "" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -2371,6 +2363,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" @@ -3174,10 +3170,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" diff --git a/shared-module/jpegio/JpegDecoder.c b/shared-module/jpegio/JpegDecoder.c index 484ba8e7ccd09..89cb624a8d03f 100644 --- a/shared-module/jpegio/JpegDecoder.c +++ b/shared-module/jpegio/JpegDecoder.c @@ -46,7 +46,7 @@ static void check_jresult(JRESULT j) { msg = MP_ERROR_TEXT("Right format but not supported"); break; case JDR_FMT3: - msg = MP_ERROR_TEXT("Not supported JPEG standard"); + msg = MP_ERROR_TEXT("Unsupported JPEG (may be progressive)"); break; } mp_raise_RuntimeError(msg); From 61e80a61b055124678d3e1de214d29951b9270bc Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 22 Oct 2025 19:46:00 -0400 Subject: [PATCH 190/517] pin changes --- .../atmel-samd/boards/microchip_curiosity_circuitpython/pins.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c index f3069930c6b0b..fd8213a573a14 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c @@ -44,6 +44,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_PA07) }, { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, + { MP_ROM_QSTR(MP_QSTR_LCD_LEDA), MP_ROM_PTR(&pin_PA06) }, { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB30) }, { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB31) }, @@ -59,9 +60,11 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_PB00) }, { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB00) }, + { MP_ROM_QSTR(MP_QSTR_IMU_INT), MP_ROM_PTR(&pin_PB00) }, { MP_ROM_QSTR(MP_QSTR_CS), MP_ROM_PTR(&pin_PB01) }, { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_PB01) }, + { MP_ROM_QSTR(MP_QSTR_IMU_ADDR), MP_ROM_PTR(&pin_PB01) }, { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB02) }, { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_PB02) }, From b076e08283ac8b72d833b1dae8527dc186ddfd53 Mon Sep 17 00:00:00 2001 From: natheihei Date: Fri, 24 Oct 2025 21:47:10 -0700 Subject: [PATCH 191/517] board: add board waveshare_esp32_s3_touch_lcd_1_47 --- .../waveshare_esp32_s3_touch_lcd_1_47/board.c | 145 ++++++++++++++++++ .../mpconfigboard.h | 22 +++ .../mpconfigboard.mk | 14 ++ .../waveshare_esp32_s3_touch_lcd_1_47/pins.c | 58 +++++++ .../sdkconfig | 14 ++ 5 files changed, 253 insertions(+) create mode 100644 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c create mode 100644 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.h create mode 100644 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.mk create mode 100644 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/pins.c create mode 100644 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/sdkconfig diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c new file mode 100644 index 0000000000000..4c037ce8df057 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c @@ -0,0 +1,145 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 natheihei +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + +#define DELAY 0x80 + +// Driver is JD9853 +// 172 X 320 Pixels RGB 18-bit +// Init sequence converted from Arduino example + +uint8_t display_init_sequence[] = { + // Command: 0x11 (SLPOUT: Sleep Out) + // Description: Exits sleep mode. A 120ms delay is added for the power supply and clock circuits to stabilize. + 0x11, 0 | DELAY, 120, + + 0xDF, 2, 0x98, 0x53, + 0xB2, 1, 0x23, + + 0xB7, 4, 0x00, 0x47, 0x00, 0x6F, + 0xBB, 6, 0x1C, 0x1A, 0x55, 0x73, 0x63, 0xF0, + 0xC0, 2, 0x44, 0xA4, + 0xC1, 1, 0x16, + 0xC3, 8, 0x7D, 0x07, 0x14, 0x06, 0xCF, 0x71, 0x72, 0x77, + 0xC4, 12, 0x00, 0x00, 0xA0, 0x79, 0x0B, 0x0A, 0x16, 0x79, 0x0B, 0x0A, 0x16, 0x82, + + 0xC8, 32, 0x3F, 0x32, 0x29, 0x29, 0x27, 0x2B, 0x27, 0x28, 0x28, 0x26, 0x25, 0x17, 0x12, 0x0D, 0x04, 0x00, + 0x3F, 0x32, 0x29, 0x29, 0x27, 0x2B, 0x27, 0x28, 0x28, 0x26, 0x25, 0x17, 0x12, 0x0D, 0x04, 0x00, + + 0xD0, 5, 0x04, 0x06, 0x6B, 0x0F, 0x00, + 0xD7, 2, 0x00, 0x30, + 0xE6, 1, 0x14, + 0xDE, 1, 0x01, + + 0xB7, 5, 0x03, 0x13, 0xEF, 0x35, 0x35, + 0xC1, 3, 0x14, 0x15, 0xC0, + 0xC2, 2, 0x06, 0x3A, + 0xC4, 2, 0x72, 0x12, + 0xBE, 1, 0x00, + 0xDE, 1, 0x02, + + 0xE5, 3, 0x00, 0x02, 0x00, + 0xE5, 3, 0x01, 0x02, 0x00, + + 0xDE, 1, 0x00, + + // Command: 0x35 (TEON: Tearing Effect Line ON) + // Description: Turns on the Tearing Effect output signal. + 0x35, 1, 0x00, + + // Command: 0x3A (COLMOD: Pixel Format Set) + // Description: Sets the pixel format for the MCU interface. + 0x3A, 1, 0x05, + + // Command: 0x2A (CASET: Column Address Set) + // Description: Defines the accessible column range in frame memory. + 0x2A, 4, 0x00, 0x22, 0x00, 0xCD, + + // Command: 0x2B (PASET: Page Address Set) + // Description: Defines the accessible page (row) range. + 0x2B, 4, 0x00, 0x00, 0x01, 0x3F, + + 0xDE, 1, 0x02, + 0xE5, 3, 0x00, 0x02, 0x00, + 0xDE, 1, 0x00, + + // Command: 0x36 (MADCTL: Memory Access Control) + // Description: Sets the read/write scanning direction of the frame memory. + 0x36, 1, 0x00, + + // Command: 0x21 (INVON: Display Inversion ON) + // 0x21, 0 | DELAY, 10, + + // Command: 0x29 (DISPON: Display ON) + // Description: Turns the display on by enabling output from the frame memory. + 0x29, 0, +}; + +static void display_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + &pin_GPIO45, // DC + &pin_GPIO21, // CS + &pin_GPIO40, // RST + 80000000, // baudrate + 0, // polarity + 0 // phase + ); + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 172, // width (after rotation) + 320, // height (after rotation) + 34, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO46, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + +void board_init(void) { + // Display + display_init(); +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.h new file mode 100644 index 0000000000000..0dc2ad4a6616b --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 natheihei +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Waveshare ESP32-S3 Touch LCD 1.47" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define CIRCUITPY_BOARD_UART (1) +#define CIRCUITPY_BOARD_UART_PIN {{.rx = &pin_GPIO44, .tx = &pin_GPIO43}} + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO41, .sda = &pin_GPIO42}} /* for Touchscreen */ + +#define CIRCUITPY_BOARD_SPI (2) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO38, .mosi = &pin_GPIO39}, /* for LCD display */ \ + {.clock = &pin_GPIO16, .mosi = &pin_GPIO15, .miso = &pin_GPIO17} /* for SD Card */} diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.mk new file mode 100644 index 0000000000000..b337b21149537 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x303A +USB_PID = 0x8325 +USB_PRODUCT = "ESP32-S3-Touch-LCD-1.47" +USB_MANUFACTURER = "Waveshare Electronics" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/pins.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/pins.c new file mode 100644 index 0000000000000..32c7d0dd21d7e --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/pins.c @@ -0,0 +1,58 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 natheihei +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(touch_i2c, i2c, 0) +CIRCUITPY_BOARD_BUS_SINGLETON(sd_spi, spi, 1) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // UART + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + + // GPIO + { MP_ROM_QSTR(MP_QSTR_GPIO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GPIO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GPIO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GPIO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GPIO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GPIO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GPIO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GPIO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GPIO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GPIO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GPIO11), MP_ROM_PTR(&pin_GPIO11) }, + + // I2C (occupied by Touch I2C) + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + // SD Card + { MP_ROM_QSTR(MP_QSTR_SD_CMD), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SD_D0), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SD_D1), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SD_D2), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_SD_D3), MP_ROM_PTR(&pin_GPIO14) }, + // CLK, CMD, D0 is also included in the SPI object as its MISO pin, MOSI pin, and SCK pin respectively + { MP_ROM_QSTR(MP_QSTR_SD_SPI), MP_ROM_PTR(&board_sd_spi_obj) }, + + // AXS5106L Touch + { MP_ROM_QSTR(MP_QSTR_TOUCH_I2C), MP_ROM_PTR(&board_touch_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_RST), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_IRQ), MP_ROM_PTR(&pin_GPIO48) }, + + // JD9853 LCD Display + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, + +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration From cb458bf1b469870d9882650679806b0461905431 Mon Sep 17 00:00:00 2001 From: Gustavo Date: Sun, 26 Oct 2025 23:14:34 -0400 Subject: [PATCH 192/517] Add Waveshare ESP32S3 Touch LCD 2.8inch display board --- .../waveshare_esp32_s3_touch_lcd_2_8/board.c | 77 +++++++++++++ .../mpconfigboard.h | 19 +++ .../mpconfigboard.mk | 14 +++ .../waveshare_esp32_s3_touch_lcd_2_8/pins.c | 109 ++++++++++++++++++ .../sdkconfig | 0 5 files changed, 219 insertions(+) create mode 100755 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c create mode 100755 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.h create mode 100755 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk create mode 100755 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c create mode 100755 ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/sdkconfig diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c new file mode 100755 index 0000000000000..deb764b80a5f7 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c @@ -0,0 +1,77 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + +#define DELAY 0x80 + +// display init sequence according to LilyGO example app +uint8_t display_init_sequence[] = { + 0x01, DELAY, 0x96, // _SWRESET and Delay 150ms + 0x11, DELAY, 0xFF, // _SLPOUT and Delay 500ms + 0x3A, DELAY | 1, 0x55, 0x0A, // _COLMOD and Delay 10ms + 0x21, DELAY, 0x0A, // _INVON Hack and Delay 10ms + 0x13, DELAY, 0x0A, // _NORON and Delay 10ms + 0x36, 0x01, 0x60, // _MADCTL + 0x29, DELAY, 0xFF, // _DISPON and Delay 500ms +}; + +void board_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + &pin_GPIO41, // DC + &pin_GPIO42, // CS + &pin_GPIO39, // RST + 40000000, // baudrate + 0, // polarity + 0 // phase + ); + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 320, // width (after rotation) + 240, // height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO5, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.h new file mode 100755 index 0000000000000..3e0db8bf494d5 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.h @@ -0,0 +1,19 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Neradoc +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Waveshare ESP32-S3 Touch LCD 2.8" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO3, .sda = &pin_GPIO1}} + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO40, .mosi = &pin_GPIO45, .miso = &pin_GPIO46}} + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk new file mode 100755 index 0000000000000..dc845ad13990d --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x303A +USB_PID = 0x825F +USB_MANUFACTURER = "Waveshare Electronics" +USB_PRODUCT = "ESP32-S3 Touch LCD 2.8" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m \ No newline at end of file diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c new file mode 100755 index 0000000000000..00ff3557088a1 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c @@ -0,0 +1,109 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // LCD (SPI0) + { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MISO), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RST), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_GPIO5) }, // PWM-capable + + // microSD (SPI1) + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO21) }, + + // Touch panel (I2C0) + { MP_ROM_QSTR(MP_QSTR_TP_SCL), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_TP_SDA), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_TP_RST), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_TP_INT), MP_ROM_PTR(&pin_GPIO4) }, + + // IMU (I2C1) + { MP_ROM_QSTR(MP_QSTR_IMU_SCL), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IMU_SDA), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_IMU_INT2), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IMU_INT1), MP_ROM_PTR(&pin_GPIO13) }, + + // I2S Audio + { MP_ROM_QSTR(MP_QSTR_I2S_BCK), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DIN), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCK), MP_ROM_PTR(&pin_GPIO38) }, + + // Battery management + { MP_ROM_QSTR(MP_QSTR_BAT_CONTROL), MP_ROM_PTR(&pin_GPIO7) }, // control pin + { MP_ROM_QSTR(MP_QSTR_BAT_PWR), MP_ROM_PTR(&pin_GPIO6) }, // Board name + { MP_ROM_QSTR(MP_QSTR_KEY_BAT), MP_ROM_PTR(&pin_GPIO6) }, // Schematics name + { MP_ROM_QSTR(MP_QSTR_BAT_ADC), MP_ROM_PTR(&pin_GPIO8) }, // VBAT sense (ADC) + + // UART header + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, // User accessible + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, // User accessible + + // I2C header + { MP_ROM_QSTR(MP_QSTR_I2C_SCL), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_I2C_SDA), MP_ROM_PTR(&pin_GPIO11) }, + + // Boot/User button + { MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON0), MP_ROM_PTR(&pin_GPIO0) }, // Optional Alias + + // Primary bus pins + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO40) }, // Primary SPI (LCD) + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO3) }, // Primary I2C (TP) + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO1) }, + + // Objects + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, + + // Fallback mapping for all GPIO pins + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, // User accessible + { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, // User accessible + { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, // User accessible + { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, // User accessible + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, // User accessible + { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, // User accessible + { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_IO47), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/sdkconfig new file mode 100755 index 0000000000000..e69de29bb2d1d From b406116b87442c66c30b4c708e404841742b555d Mon Sep 17 00:00:00 2001 From: Ross Satchell Date: Mon, 27 Oct 2025 11:32:32 -0700 Subject: [PATCH 193/517] Update pins.c Changed LED_LEDA to LCD_BL Changed CAN _STANDBY to CAN_STDBY --- .../boards/microchip_curiosity_circuitpython/pins.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c index fd8213a573a14..49c7f7565e26e 100644 --- a/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c @@ -44,7 +44,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_PA07) }, { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, - { MP_ROM_QSTR(MP_QSTR_LCD_LEDA), MP_ROM_PTR(&pin_PA06) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_PA06) }, { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB30) }, { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB31) }, @@ -78,7 +78,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, - { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STANDBY), MP_ROM_PTR(&pin_PB17) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STDBY), MP_ROM_PTR(&pin_PB17) }, { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, From 064574d21bec47cf8d5c278f6b18965fee0f556a Mon Sep 17 00:00:00 2001 From: Gustavo Date: Mon, 27 Oct 2025 15:23:31 -0400 Subject: [PATCH 194/517] Fix trailing-whitespace and end-of-files --- .../boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk | 2 +- ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk index dc845ad13990d..421a58976002a 100755 --- a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk @@ -11,4 +11,4 @@ CIRCUITPY_ESP_FLASH_SIZE = 16MB CIRCUITPY_ESP_PSRAM_SIZE = 8MB CIRCUITPY_ESP_PSRAM_MODE = opi -CIRCUITPY_ESP_PSRAM_FREQ = 80m \ No newline at end of file +CIRCUITPY_ESP_PSRAM_FREQ = 80m diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c index 00ff3557088a1..24891099ab170 100755 --- a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c @@ -90,7 +90,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, // User accessible - { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, // User accessible { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, From 5028d2ce369afb9a083086be6086df3dbb657d87 Mon Sep 17 00:00:00 2001 From: Gustavo Date: Tue, 28 Oct 2025 18:33:56 -0400 Subject: [PATCH 195/517] Remove fallback pin aliases; keep only user-accessible pins --- .../waveshare_esp32_s3_touch_lcd_2_8/pins.c | 47 +++++-------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c index 24891099ab170..0bfb3e940b4de 100755 --- a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c @@ -49,8 +49,8 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_BAT_ADC), MP_ROM_PTR(&pin_GPIO8) }, // VBAT sense (ADC) // UART header - { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, // User accessible - { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, // User accessible + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, // I2C header { MP_ROM_QSTR(MP_QSTR_I2C_SCL), MP_ROM_PTR(&pin_GPIO10) }, @@ -58,7 +58,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { // Boot/User button { MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_PTR(&pin_GPIO0) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON0), MP_ROM_PTR(&pin_GPIO0) }, // Optional Alias + { MP_ROM_QSTR(MP_QSTR_BUTTON0), MP_ROM_PTR(&pin_GPIO0) }, // Primary bus pins { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO40) }, // Primary SPI (LCD) @@ -73,37 +73,14 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, - // Fallback mapping for all GPIO pins - { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, - { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, - { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, - { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, - { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, - { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, - { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, - { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, // User accessible - { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, // User accessible - { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, - { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, // User accessible - { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, - { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, - { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, // User accessible - { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, - { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, - { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, - { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, - { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, - { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, // User accessible - { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, // User accessible - { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, - { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, - { MP_ROM_QSTR(MP_QSTR_IO47), MP_ROM_PTR(&pin_GPIO47) }, - { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, + // User accessible + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 5a4662c1e58164d9929351037acf566b998c2f55 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 30 Oct 2025 16:00:42 -0500 Subject: [PATCH 196/517] Add terminalio.Terminal example usage to docstring. Move init function docstring so it appears above the rest of the docs next to the function signature. --- shared-bindings/terminalio/Terminal.c | 53 +++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/shared-bindings/terminalio/Terminal.c b/shared-bindings/terminalio/Terminal.c index f8394dcc17d52..199128e56e925 100644 --- a/shared-bindings/terminalio/Terminal.c +++ b/shared-bindings/terminalio/Terminal.c @@ -21,7 +21,10 @@ #endif //| class Terminal: -//| """Display a character stream with a TileGrid +//| """Terminal manages tile indices and cursor position based on VT100 commands. The ``font`` should be +//| a `fontio.BuiltinFont` and the ``scroll_area`` TileGrid's bitmap should match the font's bitmap. +//| +//| Display a character stream with a TileGrid //| //| ASCII control: //| @@ -75,6 +78,52 @@ //| +--------+------------+------------+ //| | White | 37 | 47 | //| +--------+------------+------------+ +//| +//| Example Usage: +//| +//| .. code-block:: python +//| +//| import time +//| import displayio +//| import supervisor +//| from displayio import Group, TileGrid +//| from terminalio import FONT, Terminal +//| +//| main_group = Group() +//| display = supervisor.runtime.display +//| font_bb = FONT.get_bounding_box() +//| screen_size = (display.width // font_bb[0], display.height // font_bb[1]) +//| char_size = FONT.get_bounding_box() +//| +//| palette = displayio.Palette(2) +//| palette[0] = 0x000000 +//| palette[1] = 0xffffff +//| +//| tilegrid = TileGrid( +//| bitmap=FONT.bitmap, width=screen_size[0], height=screen_size[1], +//| tile_width=char_size[0], tile_height=char_size[1], pixel_shader=palette) +//| +//| terminal = Terminal(tilegrid, FONT) +//| +//| main_group.append(tilegrid) +//| display.root_group = main_group +//| +//| message = "Hello World\\n" +//| terminal.write(message) +//| +//| print(terminal.cursor_x, terminal.cursor_y) +//| move_cursor = chr(27) + "[10;10H" +//| terminal.write(f"Moving the cursor\\n{move_cursor} To here") +//| +//| cursor_home = chr(27) + f"[{screen_size[1]};0H" +//| terminal.write(cursor_home) +//| i = 1 +//| while True: +//| terminal.write(f"Writing again {i}\\n") +//| i = i + 1 +//| time.sleep(1) +//| +//| //| """ //| //| def __init__( @@ -84,8 +133,6 @@ //| *, //| status_bar: Optional[displayio.TileGrid] = None, //| ) -> None: -//| """Terminal manages tile indices and cursor position based on VT100 commands. The font should be -//| a `fontio.BuiltinFont` and the TileGrid's bitmap should match the font's bitmap.""" //| ... //| From 6b458a907eef7f78e7a75ac342e43171f8b4e7b6 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 30 Oct 2025 18:23:59 -0400 Subject: [PATCH 197/517] finalisers for SPI; clean up sdcardio; reset all pins after finalisers; avoid USB SD races --- main.c | 15 ++++++ ports/analog/common-hal/busio/SPI.c | 10 +++- ports/analog/supervisor/port.c | 1 - ports/atmel-samd/common-hal/busio/SPI.c | 10 +++- ports/atmel-samd/supervisor/port.c | 2 - ports/broadcom/common-hal/busio/SPI.c | 36 +++++-------- ports/broadcom/common-hal/busio/SPI.h | 2 - ports/broadcom/supervisor/port.c | 3 -- ports/cxd56/common-hal/busio/SPI.c | 6 ++- ports/cxd56/supervisor/port.c | 2 - ports/espressif/common-hal/busio/I2C.c | 5 ++ ports/espressif/common-hal/busio/SPI.c | 43 ++++++++------- ports/espressif/common-hal/busio/SPI.h | 2 - ports/espressif/supervisor/port.c | 5 +- ports/litex/common-hal/microcontroller/Pin.c | 4 ++ ports/litex/supervisor/port.c | 1 - ports/mimxrt10xx/common-hal/busio/SPI.c | 26 ++++----- ports/mimxrt10xx/common-hal/busio/SPI.h | 2 - ports/mimxrt10xx/supervisor/port.c | 6 --- ports/nordic/common-hal/busio/SPI.c | 18 +++---- ports/nordic/common-hal/busio/SPI.h | 2 - ports/nordic/supervisor/port.c | 3 -- ports/raspberrypi/common-hal/busio/SPI.c | 21 ++++---- ports/raspberrypi/common-hal/busio/SPI.h | 2 - ports/raspberrypi/supervisor/port.c | 3 -- ports/renode/common-hal/busio/SPI.c | 3 ++ ports/silabs/common-hal/busio/SPI.c | 19 ++++--- ports/silabs/supervisor/port.c | 3 -- ports/stm/common-hal/busio/SPI.c | 19 +++---- ports/stm/supervisor/port.c | 3 -- py/mpstate.h | 3 ++ shared-bindings/busio/SPI.c | 3 +- shared-bindings/busio/SPI.h | 4 ++ shared-bindings/sdcardio/SDCard.c | 22 ++++++-- shared-bindings/sdcardio/SDCard.h | 2 + shared-module/displayio/__init__.c | 4 ++ shared-module/sdcardio/SDCard.c | 56 +++++++++++++------- shared-module/sdcardio/SDCard.h | 5 +- shared-module/sdcardio/__init__.c | 18 +++---- supervisor/shared/usb/usb_msc_flash.c | 20 ++++--- 40 files changed, 228 insertions(+), 186 deletions(-) diff --git a/main.c b/main.c index 91687ff2f8bc7..f4f67b0434787 100644 --- a/main.c +++ b/main.c @@ -43,6 +43,7 @@ #include "supervisor/shared/external_flash/external_flash.h" #include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/supervisor/__init__.h" #include "shared-bindings/supervisor/Runtime.h" @@ -124,6 +125,7 @@ static void reset_devices(void) { static uint8_t *_heap; static uint8_t *_pystack; +static volatile bool _vm_is_running = false; static const char line_clear[] = "\x1b[2K\x1b[0G"; @@ -207,9 +209,12 @@ static void start_mp(safe_mode_t safe_mode) { // Always return to root common_hal_os_chdir("/"); + + _vm_is_running = true; } static void stop_mp(void) { + _vm_is_running = false; #if MICROPY_VFS // Unmount all heap allocated vfs mounts. @@ -409,8 +414,13 @@ static void cleanup_after_vm(mp_obj_t exception) { // Free the heap last because other modules may reference heap memory and need to shut down. filesystem_flush(); + + // Runs finalisers while shutting down the heap. stop_mp(); + // Don't reset pins until finalisers have run. + reset_all_pins(); + // Let the workflows know we've reset in case they want to restart. supervisor_workflow_reset(); } @@ -513,6 +523,7 @@ static bool __attribute__((noinline)) run_code_py(safe_mode_t safe_mode, bool *s // Finished executing python code. Cleanup includes filesystem flush and a board reset. + _vm_is_running = false; cleanup_after_vm(_exec_result.exception); _exec_result.exception = NULL; @@ -1201,6 +1212,10 @@ void NORETURN nlr_jump_fail(void *val) { } } +bool vm_is_running(void) { + return _vm_is_running; +} + #ifndef NDEBUG static void NORETURN __fatal_error(const char *msg) { #if CIRCUITPY_DEBUG == 0 diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index b4a519c37a72d..a9bf4aad8df48 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -61,6 +61,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, // Check for NULL Pointer assert(self); + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + // Assign SPI ID based on pins int spi_id = pinsToSpi(mosi, miso, sck); if (spi_id == -1) { @@ -127,6 +130,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->sck == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->sck = NULL; +} + // Deinit SPI obj void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { @@ -138,8 +145,9 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { self->mosi = NULL; self->miso = NULL; - self->sck = NULL; self->nss = NULL; + + common_hal_busio_spi_mark_deinit(self); } // Configures the SPI bus. The SPI object must be locked. diff --git a/ports/analog/supervisor/port.c b/ports/analog/supervisor/port.c index 40e8e1d605038..86d96bc251ba2 100644 --- a/ports/analog/supervisor/port.c +++ b/ports/analog/supervisor/port.c @@ -166,7 +166,6 @@ void reset_cpu(void) { // Reset MCU state void reset_port(void) { - reset_all_pins(); } // Reset to the bootloader diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index 5dd7bedebc394..fe53580afdf23 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -53,7 +53,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } // Ensure the object starts in its deinit state. - self->clock_pin = NO_PIN; + common_hal_busio_spi_mark_deinit(self); // Special case for SAMR21 boards. (feather_radiofruit_zigbee) #if defined(PIN_PC19F_SERCOM4_PAD0) @@ -184,18 +184,24 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock_pin == NO_PIN; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock_pin = NO_PIN; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; } allow_reset_sercom(self->spi_desc.dev.prvt); + // Mark as deinit early in case we are used in an interrupt. + common_hal_busio_spi_mark_deinit(self); + spi_m_sync_disable(&self->spi_desc); spi_m_sync_deinit(&self->spi_desc); reset_pin_number(self->clock_pin); reset_pin_number(self->MOSI_pin); reset_pin_number(self->MISO_pin); - self->clock_pin = NO_PIN; } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 03c2c2543a7a7..9654a791dbb32 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -411,8 +411,6 @@ void reset_port(void) { reset_ticks(); } - reset_all_pins(); - // Output clocks for debugging. // not supported by SAMD51G; uncomment for SAMD51J or update for 51G // #ifdef SAM_D5X_E5X diff --git a/ports/broadcom/common-hal/busio/SPI.c b/ports/broadcom/common-hal/busio/SPI.c index 2396a9b921ee2..01d61ba9d1d0d 100644 --- a/ports/broadcom/common-hal/busio/SPI.c +++ b/ports/broadcom/common-hal/busio/SPI.c @@ -33,28 +33,6 @@ static SPI1_Type *aux_spi[NUM_SPI] = {NULL, SPI1, SPI2}; static bool never_reset_spi[NUM_SPI]; static bool spi_in_use[NUM_SPI]; -void reset_spi(void) { - for (size_t i = 0; i < NUM_SPI; i++) { - if (never_reset_spi[i]) { - continue; - } - - if (i == 1 || i == 2) { - if (i == 1) { - AUX->ENABLES_b.SPI_1 = false; - } else { - AUX->ENABLES_b.SPI_2 = false; - } - aux_spi[i]->CNTL0 = 0; - } else { - // Set CS back to default. All 0 except read enable. - spi[i]->CS = SPI0_CS_REN_Msk; - } - - spi_in_use[i] = false; - } -} - void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { @@ -67,6 +45,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_NotImplementedError(MP_ERROR_TEXT("Half duplex SPI is not implemented")); } + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + // BCM_VERSION != 2711 have 3 SPI but as listed in peripherals/gen/pins.c two are on // index 0, once one index 0 SPI is found the other will throw an invalid_pins error. for (size_t i = 0; i < NUM_SPI; i++) { @@ -129,6 +110,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; @@ -138,7 +123,7 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { common_hal_reset_pin(self->clock); common_hal_reset_pin(self->MOSI); common_hal_reset_pin(self->MISO); - self->clock = NULL; + spi_in_use[self->index] = false; if (self->index == 1 || @@ -149,7 +134,12 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { } else if (self->index == 2) { AUX->ENABLES_b.SPI_2 = false; } + } else { + // Set CS back to default. All 0 except read enable. + spi[self->index]->CS = SPI0_CS_REN_Msk; } + + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, diff --git a/ports/broadcom/common-hal/busio/SPI.h b/ports/broadcom/common-hal/busio/SPI.h index aec91263677c0..3d52b6b0a4488 100644 --- a/ports/broadcom/common-hal/busio/SPI.h +++ b/ports/broadcom/common-hal/busio/SPI.h @@ -23,5 +23,3 @@ typedef struct { uint8_t bits; uint8_t index; } busio_spi_obj_t; - -void reset_spi(void); diff --git a/ports/broadcom/supervisor/port.c b/ports/broadcom/supervisor/port.c index 83dd50f1b8b52..a0f0be30dd758 100644 --- a/ports/broadcom/supervisor/port.c +++ b/ports/broadcom/supervisor/port.c @@ -67,7 +67,6 @@ safe_mode_t port_init(void) { void reset_port(void) { #if CIRCUITPY_BUSIO reset_i2c(); - reset_spi(); reset_uart(); #endif @@ -85,8 +84,6 @@ void reset_port(void) { #if CIRCUITPY_AUDIOCORE audio_dma_reset(); #endif - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/cxd56/common-hal/busio/SPI.c b/ports/cxd56/common-hal/busio/SPI.c index 8eedb624201dd..6c49e0f275066 100644 --- a/ports/cxd56/common-hal/busio/SPI.c +++ b/ports/cxd56/common-hal/busio/SPI.c @@ -54,7 +54,7 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { return; } - self->spi_dev = NULL; + common_hal_busio_spi_mark_deinit(self); reset_pin_number(self->clock_pin->number); reset_pin_number(self->mosi_pin->number); @@ -65,6 +65,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->spi_dev == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->spi_dev = NULL; +} + bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { uint8_t mode; diff --git a/ports/cxd56/supervisor/port.c b/ports/cxd56/supervisor/port.c index 75abcbf6d93e0..3dc54df96fbb5 100644 --- a/ports/cxd56/supervisor/port.c +++ b/ports/cxd56/supervisor/port.c @@ -66,8 +66,6 @@ void reset_port(void) { #if CIRCUITPY_RTC rtc_reset(); #endif - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/espressif/common-hal/busio/I2C.c b/ports/espressif/common-hal/busio/I2C.c index 98c453ba3c086..9ef3877ea92ed 100644 --- a/ports/espressif/common-hal/busio/I2C.c +++ b/ports/espressif/common-hal/busio/I2C.c @@ -128,6 +128,11 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { i2c_del_master_bus(self->handle); self->handle = NULL; + // Release the mutex before we delete it. Otherwise FreeRTOS gets unhappy. + xSemaphoreGive(self->xSemaphore); + vSemaphoreDelete(self->xSemaphore); + self->xSemaphore = NULL; + common_hal_reset_pin(self->sda_pin); common_hal_reset_pin(self->scl_pin); common_hal_busio_i2c_mark_deinit(self); diff --git a/ports/espressif/common-hal/busio/SPI.c b/ports/espressif/common-hal/busio/SPI.c index 6439ca4112993..3db4c770190de 100644 --- a/ports/espressif/common-hal/busio/SPI.c +++ b/ports/espressif/common-hal/busio/SPI.c @@ -18,24 +18,11 @@ static bool spi_never_reset[SOC_SPI_PERIPH_NUM]; static spi_device_handle_t spi_handle[SOC_SPI_PERIPH_NUM]; -static StaticSemaphore_t spi_mutex[SOC_SPI_PERIPH_NUM]; static bool spi_bus_is_free(spi_host_device_t host_id) { return spi_bus_get_attr(host_id) == NULL; } -void spi_reset(void) { - for (spi_host_device_t host_id = SPI2_HOST; host_id < SOC_SPI_PERIPH_NUM; host_id++) { - if (spi_never_reset[host_id]) { - continue; - } - if (!spi_bus_is_free(host_id)) { - spi_bus_remove_device(spi_handle[host_id]); - spi_bus_free(host_id); - } - } -} - static void set_spi_config(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { // 128 is a 50% duty cycle. @@ -61,6 +48,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + const spi_bus_config_t bus_config = { .mosi_io_num = mosi != NULL ? mosi->number : -1, .miso_io_num = miso != NULL ? miso->number : -1, @@ -83,6 +73,11 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_ValueError(MP_ERROR_TEXT("All SPI peripherals are in use")); } + self->mutex = xSemaphoreCreateMutex(); + if (self->mutex == NULL) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Unable to create lock")); + } + esp_err_t result = spi_bus_initialize(self->host_id, &bus_config, SPI_DMA_CH_AUTO); if (result == ESP_ERR_NO_MEM) { mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("ESP-IDF memory allocation failed")); @@ -90,6 +85,12 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, raise_ValueError_invalid_pins(); } + self->mutex = xSemaphoreCreateMutex(); + if (self->mutex == NULL) { + spi_bus_free(self->host_id); + mp_raise_RuntimeError(MP_ERROR_TEXT("Unable to create lock")); + } + set_spi_config(self, 250000, 0, 0, 8); self->MOSI = mosi; @@ -103,8 +104,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, claim_pin(miso); } claim_pin(clock); - - self->mutex = xSemaphoreCreateMutexStatic(&spi_mutex[self->host_id]); } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { @@ -122,6 +121,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; @@ -132,9 +135,9 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { RUN_BACKGROUND_TASKS; } - // Mark us as deinit early in case we are used in an interrupt. + // Mark as deinit early in case we are used in an interrupt. common_hal_reset_pin(self->clock); - self->clock = NULL; + common_hal_busio_spi_mark_deinit(self); spi_never_reset[self->host_id] = false; spi_bus_remove_device(spi_handle[self->host_id]); @@ -170,11 +173,13 @@ bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { } bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { - return self->mutex != NULL && xSemaphoreGetMutexHolder(self->mutex) == xTaskGetCurrentTaskHandle(); + return (self->mutex != NULL) && (xSemaphoreGetMutexHolder(self->mutex) == xTaskGetCurrentTaskHandle()); } void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { - xSemaphoreGive(self->mutex); + if (self->mutex != NULL) { + xSemaphoreGive(self->mutex); + } } bool common_hal_busio_spi_write(busio_spi_obj_t *self, diff --git a/ports/espressif/common-hal/busio/SPI.h b/ports/espressif/common-hal/busio/SPI.h index 820f29333c756..ac2f404042b2f 100644 --- a/ports/espressif/common-hal/busio/SPI.h +++ b/ports/espressif/common-hal/busio/SPI.h @@ -25,5 +25,3 @@ typedef struct { SemaphoreHandle_t mutex; } busio_spi_obj_t; - -void spi_reset(void); diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 72c92c98baea2..b67fae426b73f 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -356,14 +356,11 @@ void reset_port(void) { ssl_reset(); #endif - reset_all_pins(); - #if CIRCUITPY_ANALOGIO analogout_reset(); #endif #if CIRCUITPY_BUSIO - spi_reset(); uart_reset(); #endif @@ -417,7 +414,7 @@ void reset_to_bootloader(void) { } void reset_cpu(void) { - #if CIRCUITPY_DEBUG + #if CIRCUITPY_DEBUG || 1 esp_backtrace_print(100); #endif esp_restart(); diff --git a/ports/litex/common-hal/microcontroller/Pin.c b/ports/litex/common-hal/microcontroller/Pin.c index dea848f1ec3f3..bb3636c5b3faa 100644 --- a/ports/litex/common-hal/microcontroller/Pin.c +++ b/ports/litex/common-hal/microcontroller/Pin.c @@ -11,6 +11,10 @@ static uint8_t claimed_pins[1]; +void reset_all_pins(void) { + // TODO +} + // Mark pin as free and return it to a quiescent state. void reset_pin_number(uint8_t pin_port, uint8_t pin_number) { if (pin_port == 0x0F) { diff --git a/ports/litex/supervisor/port.c b/ports/litex/supervisor/port.c index 98fce16152e0b..7f9aab7f7ffb4 100644 --- a/ports/litex/supervisor/port.c +++ b/ports/litex/supervisor/port.c @@ -59,7 +59,6 @@ extern uint32_t _heap_start; extern uint32_t _estack; void reset_port(void) { - // reset_all_pins(); // i2c_reset(); // spi_reset(); // uart_reset(); diff --git a/ports/mimxrt10xx/common-hal/busio/SPI.c b/ports/mimxrt10xx/common-hal/busio/SPI.c index 732b23d8c9b3b..9b988e0d15ef8 100644 --- a/ports/mimxrt10xx/common-hal/busio/SPI.c +++ b/ports/mimxrt10xx/common-hal/busio/SPI.c @@ -53,22 +53,6 @@ static void config_periph_pin(const mcu_periph_obj_t *periph) { | IOMUXC_SW_PAD_CTL_PAD_SRE(0)); } -void spi_reset(void) { - for (uint i = 0; i < MP_ARRAY_SIZE(mcu_spi_banks); i++) { - if (!never_reset_spi[i]) { - reserved_spi[i] = false; - #if IMXRT11XX - // Skip resetting SPIs that aren't clocked. Doing so generates a bus fault. - if ((CCM->LPCG[s_lpspiClocks[i + 1]].STATUS0 & CCM_LPCG_STATUS0_ON_MASK) == ((uint32_t)kCLOCK_Off & CCM_LPCG_STATUS0_ON_MASK)) { - continue; - } - #endif - - LPSPI_Deinit(mcu_spi_banks[i]); - } - } -} - void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { @@ -82,6 +66,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_half_duplex); } + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + for (uint i = 0; i < sck_count; i++) { if (mcu_spi_sck_list[i].pin != clock) { continue; @@ -214,6 +201,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; @@ -226,9 +217,10 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { common_hal_reset_pin(self->mosi->pin); common_hal_reset_pin(self->miso->pin); - self->clock = NULL; self->mosi = NULL; self->miso = NULL; + + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, diff --git a/ports/mimxrt10xx/common-hal/busio/SPI.h b/ports/mimxrt10xx/common-hal/busio/SPI.h index 67801078261c1..d86489428ec78 100644 --- a/ports/mimxrt10xx/common-hal/busio/SPI.h +++ b/ports/mimxrt10xx/common-hal/busio/SPI.h @@ -21,5 +21,3 @@ typedef struct { const mcu_periph_obj_t *mosi; const mcu_periph_obj_t *miso; } busio_spi_obj_t; - -void spi_reset(void); diff --git a/ports/mimxrt10xx/supervisor/port.c b/ports/mimxrt10xx/supervisor/port.c index d7f7f280d1958..62d2569cfde4f 100644 --- a/ports/mimxrt10xx/supervisor/port.c +++ b/ports/mimxrt10xx/supervisor/port.c @@ -425,10 +425,6 @@ safe_mode_t port_init(void) { } void reset_port(void) { - #if CIRCUITPY_BUSIO - spi_reset(); - #endif - #if CIRCUITPY_AUDIOIO audio_dma_reset(); #endif @@ -450,8 +446,6 @@ void reset_port(void) { #endif // reset_event_system(); - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/nordic/common-hal/busio/SPI.c b/ports/nordic/common-hal/busio/SPI.c index 8af4c5f4e83f1..b4fc887aa342d 100644 --- a/ports/nordic/common-hal/busio/SPI.c +++ b/ports/nordic/common-hal/busio/SPI.c @@ -65,15 +65,6 @@ static bool never_reset[MP_ARRAY_SIZE(spim_peripherals)]; // https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52840_Rev2%2FERR%2FnRF52840%2FRev2%2Flatest%2Fanomaly_840_198.html static uint8_t *spim3_transmit_buffer = (uint8_t *)SPIM3_BUFFER_RAM_START_ADDR; -void spi_reset(void) { - for (size_t i = 0; i < MP_ARRAY_SIZE(spim_peripherals); i++) { - if (never_reset[i]) { - continue; - } - nrfx_spim_uninit(&spim_peripherals[i].spim); - } -} - void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { for (size_t i = 0; i < MP_ARRAY_SIZE(spim_peripherals); i++) { if (self->spim_peripheral == &spim_peripherals[i]) { @@ -125,6 +116,9 @@ static nrf_spim_frequency_t baudrate_to_spim_frequency(const uint32_t baudrate) void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + if (half_duplex) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_half_duplex); } @@ -178,6 +172,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock_pin_number == NO_PIN; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock_pin_number = NO_PIN; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; @@ -188,6 +186,8 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { reset_pin_number(self->clock_pin_number); reset_pin_number(self->MOSI_pin_number); reset_pin_number(self->MISO_pin_number); + + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { diff --git a/ports/nordic/common-hal/busio/SPI.h b/ports/nordic/common-hal/busio/SPI.h index 7bfddd9625dcb..3260c5c27dee2 100644 --- a/ports/nordic/common-hal/busio/SPI.h +++ b/ports/nordic/common-hal/busio/SPI.h @@ -23,5 +23,3 @@ typedef struct { uint8_t MOSI_pin_number; uint8_t MISO_pin_number; } busio_spi_obj_t; - -void spi_reset(void); diff --git a/ports/nordic/supervisor/port.c b/ports/nordic/supervisor/port.c index ed371c6ad8582..1eabfcbe2166b 100644 --- a/ports/nordic/supervisor/port.c +++ b/ports/nordic/supervisor/port.c @@ -189,7 +189,6 @@ safe_mode_t port_init(void) { void reset_port(void) { #if CIRCUITPY_BUSIO - spi_reset(); uart_reset(); #endif @@ -216,8 +215,6 @@ void reset_port(void) { nrfx_gpiote_uninit(); } nrfx_gpiote_init(NRFX_GPIOTE_CONFIG_IRQ_PRIORITY); - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/raspberrypi/common-hal/busio/SPI.c b/ports/raspberrypi/common-hal/busio/SPI.c index 4735d1284f98e..21af168df3b73 100644 --- a/ports/raspberrypi/common-hal/busio/SPI.c +++ b/ports/raspberrypi/common-hal/busio/SPI.c @@ -20,23 +20,15 @@ #define NO_INSTANCE 0xff static bool never_reset_spi[2]; -static spi_inst_t *spi[2] = {spi0, spi1}; - -void reset_spi(void) { - for (size_t i = 0; i < 2; i++) { - if (never_reset_spi[i]) { - continue; - } - - spi_deinit(spi[i]); - } -} void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { size_t instance_index = NO_INSTANCE; + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + if (half_duplex) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_half_duplex); } @@ -107,6 +99,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; @@ -117,7 +113,8 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { common_hal_reset_pin(self->clock); common_hal_reset_pin(self->MOSI); common_hal_reset_pin(self->MISO); - self->clock = NULL; + + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, diff --git a/ports/raspberrypi/common-hal/busio/SPI.h b/ports/raspberrypi/common-hal/busio/SPI.h index 8510eb7693ae2..3d43c1eff0072 100644 --- a/ports/raspberrypi/common-hal/busio/SPI.h +++ b/ports/raspberrypi/common-hal/busio/SPI.h @@ -25,5 +25,3 @@ typedef struct { uint8_t phase; uint8_t bits; } busio_spi_obj_t; - -void reset_spi(void); diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 66e63248c4810..5cfbdfa66a32b 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -422,7 +422,6 @@ safe_mode_t port_init(void) { void reset_port(void) { #if CIRCUITPY_BUSIO - reset_spi(); reset_uart(); #endif @@ -453,8 +452,6 @@ void reset_port(void) { #if CIRCUITPY_WIFI wifi_reset(); #endif - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/renode/common-hal/busio/SPI.c b/ports/renode/common-hal/busio/SPI.c index 6f5f60506074f..1f66fc5ec4af1 100644 --- a/ports/renode/common-hal/busio/SPI.c +++ b/ports/renode/common-hal/busio/SPI.c @@ -21,6 +21,9 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return true; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; diff --git a/ports/silabs/common-hal/busio/SPI.c b/ports/silabs/common-hal/busio/SPI.c index 74cfc69bfb2a0..fcff2031fa267 100644 --- a/ports/silabs/common-hal/busio/SPI.c +++ b/ports/silabs/common-hal/busio/SPI.c @@ -39,15 +39,6 @@ static SPIDRV_Init_t spidrv_eusart_init = SPIDRV_MASTER_EUSART1; static bool in_used = false; static bool never_reset = false; -// Reset SPI when reload -void spi_reset(void) { - if (!never_reset && in_used) { - SPIDRV_DeInit(&spidrv_eusart_handle); - in_used = false; - } - return; -} - // Construct SPI protocol, this function init SPI peripheral void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *sck, @@ -61,6 +52,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, MP_ERROR_TEXT("Half duplex SPI is not implemented")); } + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + if ((sck != NULL) && (mosi != NULL) && (miso != NULL)) { if (sck->function_list[FN_EUSART1_SCLK] == 1 && miso->function_list[FN_EUSART1_RX] == 1 @@ -119,6 +113,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->sck == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->sck = NULL; +} + // Deinit SPI obj void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { @@ -132,13 +130,14 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { } in_used = false; - self->sck = NULL; self->mosi = NULL; self->miso = NULL; self->handle = NULL; common_hal_reset_pin(self->mosi); common_hal_reset_pin(self->miso); common_hal_reset_pin(self->sck); + + common_hal_busio_spi_mark_deinit(self); } // Configures the SPI bus. The SPI object must be locked. diff --git a/ports/silabs/supervisor/port.c b/ports/silabs/supervisor/port.c index 2409e907deac1..85a6e0f92a5ec 100644 --- a/ports/silabs/supervisor/port.c +++ b/ports/silabs/supervisor/port.c @@ -156,10 +156,7 @@ safe_mode_t port_init(void) { } void reset_port(void) { - reset_all_pins(); - #if CIRCUITPY_BUSIO - spi_reset(); uart_reset(); #endif diff --git a/ports/stm/common-hal/busio/SPI.c b/ports/stm/common-hal/busio/SPI.c index 98696271ca93a..347e94bef9ff9 100644 --- a/ports/stm/common-hal/busio/SPI.c +++ b/ports/stm/common-hal/busio/SPI.c @@ -76,18 +76,6 @@ static uint32_t stm32_baud_to_spi_div(uint32_t baudrate, uint16_t *prescaler, ui return SPI_BAUDRATEPRESCALER_256; } -void spi_reset(void) { - uint16_t never_reset_mask = 0x00; - for (int i = 0; i < MAX_SPI; i++) { - if (!never_reset_spi[i]) { - reserved_spi[i] = false; - } else { - never_reset_mask |= 1 << i; - } - } - spi_clock_disable(ALL_CLOCKS & ~(never_reset_mask)); -} - static const mcu_periph_obj_t *find_pin_function(const mcu_periph_obj_t *table, size_t sz, const mcu_pin_obj_t *pin, int periph_index) { for (size_t i = 0; i < sz; i++, table++) { if (periph_index == table->periph_index && pin == table->pin) { @@ -152,6 +140,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, int periph_index = check_pins(self, sck, mosi, miso); SPI_TypeDef *SPIx = mcu_spi_banks[periph_index - 1]; + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + // Start GPIO for each pin GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = pin_mask(sck->number); @@ -238,6 +229,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->sck == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->sck = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; diff --git a/ports/stm/supervisor/port.c b/ports/stm/supervisor/port.c index c5a1685a7fddc..3820a046fc4af 100644 --- a/ports/stm/supervisor/port.c +++ b/ports/stm/supervisor/port.c @@ -302,14 +302,11 @@ void SysTick_Handler(void) { } void reset_port(void) { - reset_all_pins(); - #if CIRCUITPY_RTC rtc_reset(); #endif #if CIRCUITPY_BUSIO - spi_reset(); uart_reset(); #endif #if CIRCUITPY_SDIOIO diff --git a/py/mpstate.h b/py/mpstate.h index 4c48e9edaf4bd..e5e5f8d9fa3d1 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -369,4 +369,7 @@ extern mp_state_thread_t *mp_thread_get_state(void); #define mp_thread_is_main_thread() (true) #endif +// CIRCUITPY-CHANGE: defined in main.c +bool vm_is_running(void); + #endif // MICROPY_INCLUDED_PY_MPSTATE_H diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 0c8ae1bfdd72c..1513a7cf0944a 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -88,7 +88,7 @@ // TODO(tannewt): Support LSB SPI. static mp_obj_t busio_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_BUSIO_SPI - busio_spi_obj_t *self = mp_obj_malloc(busio_spi_obj_t, &busio_spi_type); + busio_spi_obj_t *self = mp_obj_malloc_with_finaliser(busio_spi_obj_t, &busio_spi_type); enum { ARG_clock, ARG_MOSI, ARG_MISO, ARG_half_duplex }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -466,6 +466,7 @@ MP_PROPERTY_GETTER(busio_spi_frequency_obj, static const mp_rom_map_elem_t busio_spi_locals_dict_table[] = { #if CIRCUITPY_BUSIO_SPI { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_spi_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&busio_spi_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, diff --git a/shared-bindings/busio/SPI.h b/shared-bindings/busio/SPI.h index 69e582411a169..34f34c927f613 100644 --- a/shared-bindings/busio/SPI.h +++ b/shared-bindings/busio/SPI.h @@ -22,6 +22,10 @@ extern void common_hal_busio_spi_construct(busio_spi_obj_t *self, extern void common_hal_busio_spi_deinit(busio_spi_obj_t *self); extern bool common_hal_busio_spi_deinited(busio_spi_obj_t *self); +// Mark as deinit without deiniting. This is used by displayio after copying the +// object elsewhere and prevents the heap from deiniting the object. +extern void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self); + extern bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits); extern bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self); diff --git a/shared-bindings/sdcardio/SDCard.c b/shared-bindings/sdcardio/SDCard.c index e6d8453eae108..26892a79462d3 100644 --- a/shared-bindings/sdcardio/SDCard.c +++ b/shared-bindings/sdcardio/SDCard.c @@ -9,7 +9,8 @@ #include "py/objarray.h" #include "shared-bindings/sdcardio/SDCard.h" -#include "shared-module/sdcardio/SDCard.h" +#include "shared-bindings/util.h" + #include "common-hal/busio/SPI.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/microcontroller/Pin.h" @@ -58,6 +59,12 @@ //| os.listdir('/sd')""" //| +static void check_for_deinit(sdcardio_sdcard_obj_t *self) { + if (common_hal_sdcardio_sdcard_deinited(self)) { + raise_deinited_error(); + } +} + static mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_spi, ARG_cs, ARG_baudrate, NUM_ARGS }; static const mp_arg_t allowed_args[] = { @@ -72,7 +79,7 @@ static mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_arg busio_spi_obj_t *spi = validate_obj_is_spi_bus(args[ARG_spi].u_obj, MP_QSTR_spi); const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); - sdcardio_sdcard_obj_t *self = mp_obj_malloc(sdcardio_sdcard_obj_t, &sdcardio_SDCard_type); + sdcardio_sdcard_obj_t *self = mp_obj_malloc_with_finaliser(sdcardio_sdcard_obj_t, &sdcardio_SDCard_type); common_hal_sdcardio_sdcard_construct(self, spi, cs, args[ARG_baudrate].u_int); @@ -89,6 +96,7 @@ static mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_arg //| static mp_obj_t sdcardio_sdcard_count(mp_obj_t self_in) { sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + check_for_deinit(self); return mp_obj_new_int_from_ull(common_hal_sdcardio_sdcard_get_blockcount(self)); } MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_count_obj, sdcardio_sdcard_count); @@ -116,10 +124,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_deinit_obj, sdcardio_sdcard_deinit); //| static mp_obj_t _sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + check_for_deinit(self); + uint32_t start_block = mp_obj_get_int(start_block_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); - sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; int result = common_hal_sdcardio_sdcard_readblocks(self, start_block, &bufinfo); if (result < 0) { mp_raise_OSError(-result); @@ -137,6 +147,7 @@ MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, _sdcardio_sdcard_readb //| static mp_obj_t sdcardio_sdcard_sync(mp_obj_t self_in) { sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + check_for_deinit(self); int result = common_hal_sdcardio_sdcard_sync(self); if (result < 0) { mp_raise_OSError(-result); @@ -158,10 +169,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_sync_obj, sdcardio_sdcard_sync); //| static mp_obj_t _sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + check_for_deinit(self); + uint32_t start_block = mp_obj_get_int(start_block_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); - sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; int result = common_hal_sdcardio_sdcard_writeblocks(self, start_block, &bufinfo); if (result < 0) { mp_raise_OSError(-result); @@ -173,6 +186,7 @@ MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, _sdcardio_sdcard_writ static const mp_rom_map_elem_t sdcardio_sdcard_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdcardio_sdcard_count_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdcardio_sdcard_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&sdcardio_sdcard_sync_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdcardio_sdcard_writeblocks_obj) }, diff --git a/shared-bindings/sdcardio/SDCard.h b/shared-bindings/sdcardio/SDCard.h index ac27b47aa4d21..95998f5af5e90 100644 --- a/shared-bindings/sdcardio/SDCard.h +++ b/shared-bindings/sdcardio/SDCard.h @@ -13,7 +13,9 @@ extern const mp_obj_type_t sdcardio_SDCard_type; void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, const mcu_pin_obj_t *cs, int baudrate); void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self); +bool common_hal_sdcardio_sdcard_deinited(sdcardio_sdcard_obj_t *self); void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self); +void common_hal_sdcardio_sdcard_mark_deinit(sdcardio_sdcard_obj_t *self); int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self); int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self); diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index f3444241b3cca..3ffd4d002f534 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -13,6 +13,7 @@ #include "py/runtime.h" #include "shared-bindings/board/__init__.h" #include "shared-bindings/busio/I2C.h" +#include "shared-bindings/busio/SPI.h" #include "shared-bindings/displayio/Bitmap.h" #include "shared-bindings/displayio/Group.h" #include "shared-bindings/displayio/Palette.h" @@ -221,6 +222,7 @@ void reset_displays(void) { #endif memcpy(&fourwire->inline_bus, original_spi, sizeof(busio_spi_obj_t)); fourwire->bus = &fourwire->inline_bus; + // Check for other display buses that use the same spi bus and swap them too. for (uint8_t j = i + 1; j < CIRCUITPY_DISPLAY_LIMIT; j++) { if (display_buses[j].fourwire_bus.base.type == &fourwire_fourwire_type && @@ -228,6 +230,8 @@ void reset_displays(void) { display_buses[j].fourwire_bus.bus = &fourwire->inline_bus; } } + // Mark the old SPI object so it is considered deinit. + common_hal_busio_spi_mark_deinit(original_spi); } #endif #if CIRCUITPY_I2CDISPLAYBUS diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c index bd3ea62d141e5..878fed7a13c9b 100644 --- a/shared-module/sdcardio/SDCard.c +++ b/shared-module/sdcardio/SDCard.c @@ -32,18 +32,41 @@ #define TOKEN_STOP_TRAN (0xFD) #define TOKEN_DATA (0xFE) +bool common_hal_sdcardio_sdcard_deinited(sdcardio_sdcard_obj_t *self) { + // Also check SPI bus was deinited out from under us. + if (!self->bus || common_hal_busio_spi_deinited(self->bus)) { + return true; + } + return false; +} + +void common_hal_sdcardio_sdcard_mark_deinit(sdcardio_sdcard_obj_t *self) { + self->bus = NULL; +} + static void common_hal_sdcardio_check_for_deinit(sdcardio_sdcard_obj_t *self) { - if (!self->bus) { + if (common_hal_sdcardio_sdcard_deinited(self)) { raise_deinited_error(); } } static bool lock_and_configure_bus(sdcardio_sdcard_obj_t *self) { + if (common_hal_sdcardio_sdcard_deinited(self)) { + return false; + } common_hal_sdcardio_check_for_deinit(self); if (!common_hal_busio_spi_try_lock(self->bus)) { return false; } + + // Make sure we can still use the SPI bus after grabbing the lock. + // The VM might be in the process of shutting down, and there could be a race. + if (!vm_is_running() && !self->persistent_mount) { + common_hal_busio_spi_unlock(self->bus); + return false; + } + common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8); common_hal_digitalio_digitalinout_set_value(&self->cs, false); return true; @@ -97,7 +120,7 @@ static int wait_for_ready(sdcardio_sdcard_obj_t *self) { } // Note: this is never called while "in cmd25" (in fact, it's only used by `exit_cmd25`) -static bool cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { +static int cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { uint8_t cmdbuf[2] = {cmd, 0xff}; assert(!self->in_cmd25); @@ -111,7 +134,7 @@ static bool cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { return 0; } } - return -EIO; + return -MP_EIO; } @@ -160,7 +183,7 @@ static int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf } if (!response_received) { - return -EIO; + return -MP_EIO; } if (response_buf) { @@ -294,13 +317,16 @@ static mp_rom_error_text_t init_card(sdcardio_sdcard_obj_t *self) { return NULL; } -mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate) { +mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate, bool persistent_mount) { self->bus = bus; + self->persistent_mount = persistent_mount; common_hal_digitalio_digitalinout_construct(&self->cs, cs); common_hal_digitalio_digitalinout_switch_to_output(&self->cs, true, DRIVE_MODE_PUSH_PULL); self->cdv = 512; self->sectors = 0; + + // During initialization, talk to the SPI card between 100 khZ and 400 kHz. After that, can use full speed. self->baudrate = 250000; lock_bus_or_throw(self); @@ -318,18 +344,19 @@ mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate) { - mp_rom_error_text_t result = sdcardio_sdcard_construct(self, bus, cs, baudrate); + // User mounted, so persistent_mount=false. + mp_rom_error_text_t result = sdcardio_sdcard_construct(self, bus, cs, baudrate, false); if (result != NULL) { mp_raise_OSError_msg(result); } } void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) { - if (!self->bus) { + if (common_hal_sdcardio_sdcard_deinited(self)) { return; } common_hal_sdcardio_sdcard_sync(self); - self->bus = 0; + common_hal_sdcardio_sdcard_mark_deinit(self); common_hal_digitalio_digitalinout_deinit(&self->cs); } @@ -428,7 +455,7 @@ static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]); if ((cmd[0] & 0b00010001) == 0b00000001) { if ((cmd[0] & 0x1f) != 0x5) { - return -EIO; + return -MP_EIO; } else { break; } @@ -489,14 +516,10 @@ int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self) { } int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { - // deinit check is in lock_and_configure_bus() if (buf->len % 512 != 0) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Buffer must be a multiple of %d bytes"), 512); } - lock_and_configure_bus(self); - int r = sdcardio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), buf->buf, start_block, buf->len / 512); - extraclock_and_unlock_bus(self); - return r; + return sdcardio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), buf->buf, start_block, buf->len / 512); } bool sdcardio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value) { @@ -504,11 +527,8 @@ bool sdcardio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *o *out_value = 0; switch (cmd) { case MP_BLOCKDEV_IOCTL_DEINIT: - common_hal_sdcardio_sdcard_sync(self); - break; // TODO properly case MP_BLOCKDEV_IOCTL_SYNC: - common_hal_sdcardio_sdcard_sync(self); - break; + return common_hal_sdcardio_sdcard_sync(self) == 0; case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: *out_value = common_hal_sdcardio_sdcard_get_blockcount(self); break; diff --git a/shared-module/sdcardio/SDCard.h b/shared-module/sdcardio/SDCard.h index f9cd64b812532..35a9ed1bf3c12 100644 --- a/shared-module/sdcardio/SDCard.h +++ b/shared-module/sdcardio/SDCard.h @@ -23,6 +23,9 @@ typedef struct { uint32_t sectors; uint32_t next_block; bool in_cmd25; + // Automounted SD cards are usually persistent across VM's. Note this as needed to allow access + // when the VM is not running. + bool persistent_mount; } sdcardio_sdcard_obj_t; -mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate); +mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate, bool persistent_mount); diff --git a/shared-module/sdcardio/__init__.c b/shared-module/sdcardio/__init__.c index 29c890c6870c1..4a7a4d500cc3f 100644 --- a/shared-module/sdcardio/__init__.c +++ b/shared-module/sdcardio/__init__.c @@ -22,7 +22,7 @@ static mp_vfs_mount_t _sdcard_vfs; fs_user_mount_t _sdcard_usermount; static bool _init_error = false; -static bool _mounted = false; +static bool _automounted = false; #ifdef DEFAULT_SD_MOSI static busio_spi_obj_t busio_spi_obj; @@ -45,7 +45,7 @@ void automount_sd_card(void) { if (common_hal_digitalio_digitalinout_get_value(&sd_card_detect_pin) != DEFAULT_SD_CARD_INSERTED) { // No card. _init_error = false; - if (_mounted) { + if (_automounted) { // Unmount the card. mp_vfs_mount_t *cur = MP_STATE_VM(vfs_mount_table); if (cur == &_sdcard_vfs) { @@ -63,11 +63,11 @@ void automount_sd_card(void) { #ifdef DEFAULT_SD_MOSI common_hal_busio_spi_deinit(&busio_spi_obj); #endif - _mounted = false; + _automounted = false; } return; - } else if (_init_error || _mounted) { - // We've already tried and failed to init the card. Don't try again. + } else if (_init_error || _automounted) { + // We've already tried and failed to init the card, or it's still mounted. Don't try again. return; } @@ -81,10 +81,10 @@ void automount_sd_card(void) { common_hal_busio_spi_never_reset(spi_obj); #endif sdcard.base.type = &sdcardio_SDCard_type; - mp_rom_error_text_t error = sdcardio_sdcard_construct(&sdcard, spi_obj, DEFAULT_SD_CS, 25000000); + mp_rom_error_text_t error = sdcardio_sdcard_construct(&sdcard, spi_obj, DEFAULT_SD_CS, 25000000, true); if (error != NULL) { // Failed to communicate with the card. - _mounted = false; + _automounted = false; _init_error = true; #ifdef DEFAULT_SD_MOSI common_hal_busio_spi_deinit(spi_obj); @@ -104,7 +104,7 @@ void automount_sd_card(void) { // mount the block device so the VFS methods can be used FRESULT res = f_mount(&vfs->fatfs); if (res != FR_OK) { - _mounted = false; + _automounted = false; _init_error = true; common_hal_sdcardio_sdcard_deinit(&sdcard); #ifdef DEFAULT_SD_MOSI @@ -122,6 +122,6 @@ void automount_sd_card(void) { sdcard_vfs->obj = MP_OBJ_FROM_PTR(&_sdcard_usermount); sdcard_vfs->next = MP_STATE_VM(vfs_mount_table); MP_STATE_VM(vfs_mount_table) = sdcard_vfs; - _mounted = true; + _automounted = true; #endif // DEFAULT_SD_CARD_DETECT } diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index febede876bbf5..adc182497617c 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -5,7 +5,6 @@ // SPDX-License-Identifier: MIT #include "tusb.h" -// // #include "supervisor/flash.h" // For updating fatfs's cache #include "extmod/vfs.h" @@ -14,6 +13,7 @@ #include "lib/oofatfs/ff.h" #include "py/gc.h" #include "py/mpstate.h" +#include "py/runtime.h" #include "shared-module/storage/__init__.h" #include "supervisor/filesystem.h" @@ -28,7 +28,7 @@ #define SAVES_COUNT 0 #endif -#if CIRCUITPY_SDCARDIO +#if CIRCUITPY_SDCARD_USB #include "shared-module/sdcardio/__init__.h" #define SDCARD_COUNT 1 @@ -137,7 +137,8 @@ static fs_user_mount_t *get_vfs(int lun) { if (lun == SAVES_LUN) { const char *path_under_mount; fs_user_mount_t *saves = filesystem_for_path("/saves", &path_under_mount); - if (saves != root && (saves->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0 && !gc_ptr_on_heap(saves)) { + if (saves != root && + (saves->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0 && !gc_ptr_on_heap(saves)) { return saves; } } @@ -146,8 +147,15 @@ static fs_user_mount_t *get_vfs(int lun) { if (lun == SDCARD_LUN) { const char *path_under_mount; fs_user_mount_t *sdcard = filesystem_for_path("/sd", &path_under_mount); - // If "/sd" is on the root filesystem, nothing has been mounted there. - if (sdcard != root && (sdcard->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0) { + // If sdcard ("/sd") is on the root filesystem, nothing has been mounted there, so don't + // return it as a separate filesystem. + // If the SD card was automounted at startup, then it persists across VMs and its fs_user_mount_t is + // not on the heap. + // If the SD card filesystem was mounted by the user using heap objects, + // it should not be used when the VM has stopped running. + if ((sdcard != root) && + ((sdcard->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0) && + (vm_is_running() || !gc_ptr_on_heap(sdcard))) { return sdcard; } else { // Clear any ejected state so that a re-insert causes it to reappear. @@ -359,7 +367,7 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { return false; } - #if CIRCUITPY_SDCARD_USB + #ifdef SDCARD_LUN if (lun == SDCARD_LUN) { automount_sd_card(); } From 2800777933166cf35ead80ec11743deb6ca79dfa Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 31 Oct 2025 14:50:34 -0400 Subject: [PATCH 198/517] remove local tracking of never_reset in all common-hal/busio/SPI.c --- ports/analog/common-hal/busio/SPI.c | 3 --- ports/broadcom/common-hal/busio/SPI.c | 4 ---- ports/espressif/common-hal/busio/SPI.c | 3 --- ports/espressif/supervisor/port.c | 2 +- ports/mimxrt10xx/common-hal/busio/SPI.c | 3 --- ports/nordic/common-hal/busio/SPI.c | 4 ---- ports/raspberrypi/common-hal/busio/SPI.c | 5 ----- ports/silabs/common-hal/busio/SPI.c | 2 -- ports/stm/common-hal/busio/SPI.c | 3 --- 9 files changed, 1 insertion(+), 28 deletions(-) diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c index a9bf4aad8df48..de3856b23b3f1 100644 --- a/ports/analog/common-hal/busio/SPI.c +++ b/ports/analog/common-hal/busio/SPI.c @@ -42,7 +42,6 @@ typedef enum { SPI_FREE = 0, SPI_BUSY, - SPI_NEVER_RESET, } spi_status_t; // Set each bit to indicate an active SPI @@ -121,8 +120,6 @@ void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { common_hal_never_reset_pin(self->miso); common_hal_never_reset_pin(self->sck); common_hal_never_reset_pin(self->nss); - - spi_status[self->spi_id] = SPI_NEVER_RESET; } // Check SPI status, deinited or not diff --git a/ports/broadcom/common-hal/busio/SPI.c b/ports/broadcom/common-hal/busio/SPI.c index 01d61ba9d1d0d..9e4834e86f352 100644 --- a/ports/broadcom/common-hal/busio/SPI.c +++ b/ports/broadcom/common-hal/busio/SPI.c @@ -30,7 +30,6 @@ static SPI0_Type *spi[NUM_SPI] = {SPI0, NULL, NULL}; static SPI1_Type *aux_spi[NUM_SPI] = {NULL, SPI1, SPI2}; #endif -static bool never_reset_spi[NUM_SPI]; static bool spi_in_use[NUM_SPI]; void common_hal_busio_spi_construct(busio_spi_obj_t *self, @@ -99,8 +98,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset_spi[self->index] = true; - common_hal_never_reset_pin(self->clock); common_hal_never_reset_pin(self->MOSI); common_hal_never_reset_pin(self->MISO); @@ -118,7 +115,6 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; } - never_reset_spi[self->index] = false; common_hal_reset_pin(self->clock); common_hal_reset_pin(self->MOSI); diff --git a/ports/espressif/common-hal/busio/SPI.c b/ports/espressif/common-hal/busio/SPI.c index 3db4c770190de..4c07917b20b86 100644 --- a/ports/espressif/common-hal/busio/SPI.c +++ b/ports/espressif/common-hal/busio/SPI.c @@ -16,7 +16,6 @@ #define SPI_MAX_DMA_BITS (SPI_MAX_DMA_LEN * 8) #define MAX_SPI_TRANSACTIONS 10 -static bool spi_never_reset[SOC_SPI_PERIPH_NUM]; static spi_device_handle_t spi_handle[SOC_SPI_PERIPH_NUM]; static bool spi_bus_is_free(spi_host_device_t host_id) { @@ -107,7 +106,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - spi_never_reset[self->host_id] = true; common_hal_never_reset_pin(self->clock); if (self->MOSI != NULL) { common_hal_never_reset_pin(self->MOSI); @@ -139,7 +137,6 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { common_hal_reset_pin(self->clock); common_hal_busio_spi_mark_deinit(self); - spi_never_reset[self->host_id] = false; spi_bus_remove_device(spi_handle[self->host_id]); spi_bus_free(self->host_id); diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index b67fae426b73f..c629eb52932e7 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -414,7 +414,7 @@ void reset_to_bootloader(void) { } void reset_cpu(void) { - #if CIRCUITPY_DEBUG || 1 + #if CIRCUITPY_DEBUG esp_backtrace_print(100); #endif esp_restart(); diff --git a/ports/mimxrt10xx/common-hal/busio/SPI.c b/ports/mimxrt10xx/common-hal/busio/SPI.c index 9b988e0d15ef8..90695be4b4e6d 100644 --- a/ports/mimxrt10xx/common-hal/busio/SPI.c +++ b/ports/mimxrt10xx/common-hal/busio/SPI.c @@ -26,7 +26,6 @@ // arrays use 0 based numbering: SPI1 is stored at index 0 static bool reserved_spi[MP_ARRAY_SIZE(mcu_spi_banks)]; -static bool never_reset_spi[MP_ARRAY_SIZE(mcu_spi_banks)]; #if IMXRT11XX static const clock_ip_name_t s_lpspiClocks[] = LPSPI_CLOCKS; @@ -187,7 +186,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset_spi[self->clock->bank_idx - 1] = true; common_hal_never_reset_pin(self->clock->pin); if (self->mosi != NULL) { common_hal_never_reset_pin(self->mosi->pin); @@ -211,7 +209,6 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { } LPSPI_Deinit(self->spi); reserved_spi[self->clock->bank_idx - 1] = false; - never_reset_spi[self->clock->bank_idx - 1] = false; common_hal_reset_pin(self->clock->pin); common_hal_reset_pin(self->mosi->pin); diff --git a/ports/nordic/common-hal/busio/SPI.c b/ports/nordic/common-hal/busio/SPI.c index b4fc887aa342d..de54dd08a2783 100644 --- a/ports/nordic/common-hal/busio/SPI.c +++ b/ports/nordic/common-hal/busio/SPI.c @@ -59,8 +59,6 @@ static const spim_peripheral_t spim_peripherals[] = { #endif }; -static bool never_reset[MP_ARRAY_SIZE(spim_peripherals)]; - // Separate RAM area for SPIM3 transmit buffer to avoid SPIM3 hardware errata. // https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52840_Rev2%2FERR%2FnRF52840%2FRev2%2Flatest%2Fanomaly_840_198.html static uint8_t *spim3_transmit_buffer = (uint8_t *)SPIM3_BUFFER_RAM_START_ADDR; @@ -68,8 +66,6 @@ static uint8_t *spim3_transmit_buffer = (uint8_t *)SPIM3_BUFFER_RAM_START_ADDR; void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { for (size_t i = 0; i < MP_ARRAY_SIZE(spim_peripherals); i++) { if (self->spim_peripheral == &spim_peripherals[i]) { - never_reset[i] = true; - never_reset_pin_number(self->clock_pin_number); never_reset_pin_number(self->MOSI_pin_number); never_reset_pin_number(self->MISO_pin_number); diff --git a/ports/raspberrypi/common-hal/busio/SPI.c b/ports/raspberrypi/common-hal/busio/SPI.c index 21af168df3b73..aeb06d919ae45 100644 --- a/ports/raspberrypi/common-hal/busio/SPI.c +++ b/ports/raspberrypi/common-hal/busio/SPI.c @@ -19,8 +19,6 @@ #define NO_INSTANCE 0xff -static bool never_reset_spi[2]; - void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { @@ -88,8 +86,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset_spi[spi_get_index(self->peripheral)] = true; - common_hal_never_reset_pin(self->clock); common_hal_never_reset_pin(self->MOSI); common_hal_never_reset_pin(self->MISO); @@ -107,7 +103,6 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; } - never_reset_spi[spi_get_index(self->peripheral)] = false; spi_deinit(self->peripheral); common_hal_reset_pin(self->clock); diff --git a/ports/silabs/common-hal/busio/SPI.c b/ports/silabs/common-hal/busio/SPI.c index fcff2031fa267..fb3b7c4fd20c7 100644 --- a/ports/silabs/common-hal/busio/SPI.c +++ b/ports/silabs/common-hal/busio/SPI.c @@ -37,7 +37,6 @@ static SPIDRV_HandleData_t spidrv_eusart_handle; static SPIDRV_Init_t spidrv_eusart_init = SPIDRV_MASTER_EUSART1; static bool in_used = false; -static bool never_reset = false; // Construct SPI protocol, this function init SPI peripheral void common_hal_busio_spi_construct(busio_spi_obj_t *self, @@ -102,7 +101,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, // Never reset SPI when reload void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset = true; common_hal_never_reset_pin(self->mosi); common_hal_never_reset_pin(self->miso); common_hal_never_reset_pin(self->sck); diff --git a/ports/stm/common-hal/busio/SPI.c b/ports/stm/common-hal/busio/SPI.c index 347e94bef9ff9..1b86a9f55d5d4 100644 --- a/ports/stm/common-hal/busio/SPI.c +++ b/ports/stm/common-hal/busio/SPI.c @@ -19,7 +19,6 @@ #define MAX_SPI 6 static bool reserved_spi[MAX_SPI]; -static bool never_reset_spi[MAX_SPI]; #define ALL_CLOCKS 0xFF static void spi_clock_enable(uint8_t mask); @@ -215,7 +214,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset_spi[self->sck->periph_index - 1] = true; never_reset_pin_number(self->sck->pin->port, self->sck->pin->number); if (self->mosi != NULL) { never_reset_pin_number(self->mosi->pin->port, self->mosi->pin->number); @@ -239,7 +237,6 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { } spi_clock_disable(1 << (self->sck->periph_index - 1)); reserved_spi[self->sck->periph_index - 1] = false; - never_reset_spi[self->sck->periph_index - 1] = false; reset_pin_number(self->sck->pin->port, self->sck->pin->number); if (self->mosi != NULL) { From 85e145f203d1d2f1cef2169b1af7103430d156a8 Mon Sep 17 00:00:00 2001 From: chinh4thepro Date: Sun, 2 Nov 2025 00:46:55 -0400 Subject: [PATCH 199/517] ports/espressif: add pins for seeed xiao esp32s3 plus board ports/espressif/boards: add pins for seeed xiao esp32s3 plus --- .../seeed_xiao_esp32_s3_sense/mpconfigboard.h | 10 +++++----- .../boards/seeed_xiao_esp32_s3_sense/pins.c | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h index c05b6a93a1054..d7f24b255d69c 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h @@ -12,9 +12,9 @@ #define DEFAULT_UART_BUS_RX (&pin_GPIO44) #define DEFAULT_UART_BUS_TX (&pin_GPIO43) -#define DEFAULT_SPI_BUS_SCK (&pin_GPIO7) -#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO9) -#define DEFAULT_SPI_BUS_MISO (&pin_GPIO8) +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO7) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO9) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO8) -#define DEFAULT_I2C_BUS_SCL (&pin_GPIO6) -#define DEFAULT_I2C_BUS_SDA (&pin_GPIO5) +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO6) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO5) diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c index aa3a6f7b7f577..8269f1d7a951e 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c @@ -48,15 +48,30 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO8) }, { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO11) }, { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO21) }, { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO5) }, { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_TX_1), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_RX_1), MP_ROM_PTR(&pin_GPIO41) }, { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO9) }, { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_MOSI_1), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_MISO_1), MP_ROM_PTR(&pin_GPIO12) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SCK_1), MP_ROM_PTR(&pin_GPIO13) }, { MP_ROM_QSTR(MP_QSTR_SDCS), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pin_GPIO10) }, { MP_ROM_QSTR(MP_QSTR_CAM_DATA), MP_ROM_PTR(&camera_data_tuple) }, { MP_ROM_QSTR(MP_QSTR_CAM_D0), MP_ROM_PTR(&pin_GPIO15) }, { MP_ROM_QSTR(MP_QSTR_CAM_D1), MP_ROM_PTR(&pin_GPIO17) }, @@ -74,6 +89,9 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_MIC_DATA), MP_ROM_PTR(&pin_GPIO41) }, { MP_ROM_QSTR(MP_QSTR_MIC_CLK), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SD), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SCK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WS), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, From 4c4a1888b09e02ca08f0d4b76f9b2aac3db3a627 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 17 Oct 2025 12:36:18 -0700 Subject: [PATCH 200/517] Add mipidsi module for MIPI DSI displays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for MIPI DSI (Display Serial Interface) displays via a new mipidsi module. This enables high-speed serial communication with DSI displays using differential signaling. The module provides: - Bus class: Manages DSI bus with 1-4 data lanes - Display class: Handles display configuration and framebuffer Module is: - Disabled by default globally - Enabled only for ESP32-P4 (has hardware MIPI-DSI support) 🤖 Initial code generated with [Claude Code](https://claude.com/claude-code) and then hand improved. Co-Authored-By: Claude --- locale/circuitpython.pot | 17 +- ports/espressif/Makefile | 17 +- .../mpconfigboard.h | 7 +- .../espressif_esp32p4_function_ev/pins.c | 93 +++--- ports/espressif/boards/m5stack_tab5/board.c | 9 + .../boards/m5stack_tab5/mpconfigboard.h | 28 ++ .../boards/m5stack_tab5/mpconfigboard.mk | 14 + ports/espressif/boards/m5stack_tab5/pins.c | 128 ++++++++ ports/espressif/boards/m5stack_tab5/sdkconfig | 0 .../common-hal/microcontroller/Pin.c | 15 +- ports/espressif/common-hal/mipidsi/Bus.c | 52 ++++ ports/espressif/common-hal/mipidsi/Bus.h | 17 ++ ports/espressif/common-hal/mipidsi/Display.c | 254 +++++++++++++++ ports/espressif/common-hal/mipidsi/Display.h | 37 +++ ports/espressif/common-hal/mipidsi/__init__.c | 7 + ports/espressif/common-hal/mipidsi/__init__.h | 7 + ports/espressif/mpconfigport.mk | 3 + ports/espressif/supervisor/port.c | 2 +- py/circuitpy_defns.mk | 6 + py/circuitpy_mpconfig.mk | 3 + shared-bindings/mipidsi/Bus.c | 96 ++++++ shared-bindings/mipidsi/Bus.h | 15 + shared-bindings/mipidsi/Display.c | 289 ++++++++++++++++++ shared-bindings/mipidsi/Display.h | 44 +++ shared-bindings/mipidsi/__init__.c | 30 ++ shared-bindings/mipidsi/__init__.h | 7 + shared-module/displayio/__init__.h | 6 + 27 files changed, 1153 insertions(+), 50 deletions(-) create mode 100644 ports/espressif/boards/m5stack_tab5/board.c create mode 100644 ports/espressif/boards/m5stack_tab5/mpconfigboard.h create mode 100644 ports/espressif/boards/m5stack_tab5/mpconfigboard.mk create mode 100644 ports/espressif/boards/m5stack_tab5/pins.c create mode 100644 ports/espressif/boards/m5stack_tab5/sdkconfig create mode 100644 ports/espressif/common-hal/mipidsi/Bus.c create mode 100644 ports/espressif/common-hal/mipidsi/Bus.h create mode 100644 ports/espressif/common-hal/mipidsi/Display.c create mode 100644 ports/espressif/common-hal/mipidsi/Display.h create mode 100644 ports/espressif/common-hal/mipidsi/__init__.c create mode 100644 ports/espressif/common-hal/mipidsi/__init__.h create mode 100644 shared-bindings/mipidsi/Bus.c create mode 100644 shared-bindings/mipidsi/Bus.h create mode 100644 shared-bindings/mipidsi/Display.c create mode 100644 shared-bindings/mipidsi/Display.h create mode 100644 shared-bindings/mipidsi/__init__.c create mode 100644 shared-bindings/mipidsi/__init__.h diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 1e167c30353dd..35fc94513a547 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -159,10 +159,6 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" -#: py/runtime.c -msgid "%q moved from %q to %q" -msgstr "" - #: py/argcheck.c msgid "%q must be %d" msgstr "" @@ -838,6 +834,10 @@ msgstr "" msgid "Clock unit in use" msgstr "" +#: ports/espressif/common-hal/mipidsi/Display.c +msgid "Color depth must be 16 or 24" +msgstr "" + #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -932,6 +932,7 @@ msgstr "" #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "" @@ -1276,7 +1277,8 @@ msgstr "" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c #: shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c @@ -2266,6 +2268,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -3174,10 +3177,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index b6ddc27e4084c..8302726aad7a4 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -76,6 +76,7 @@ INC += \ -isystem esp-idf/components/esp_driver_uart/include \ -isystem esp-idf/components/esp_event/include \ -isystem esp-idf/components/esp_hw_support/dma/include \ + -isystem esp-idf/components/esp_hw_support/ldo/include \ -isystem esp-idf/components/esp_hw_support/include \ -isystem esp-idf/components/esp_hw_support/include/soc \ -isystem esp-idf/components/esp_hw_support/port/$(IDF_TARGET)/private_include \ @@ -171,7 +172,8 @@ REGISTRATION_FUNCTIONS = \ -u esp_libc_init_funcs \ -u include_esp_phy_override \ -u vfs_include_syscalls_impl \ - -u esp_vfs_include_nullfs_register + -u esp_vfs_include_nullfs_register \ + -u usb_serial_jtag_vfs_include_dev_init #Debugging/Optimization @@ -329,7 +331,8 @@ LDFLAGS += \ CHIP_COMPONENTS = \ - esp_driver_tsens + esp_driver_tsens \ + esp_driver_usb_serial_jtag else ifeq ($(IDF_TARGET),esp32h2) LDFLAGS += \ @@ -467,6 +470,13 @@ CFLAGS += \ -isystem esp-idf/components/esp_lcd/rgb/include endif +ifneq ($(CIRCUITPY_MIPIDSI),0) +CFLAGS += \ + -isystem esp-idf/components/esp_lcd/include \ + -isystem esp-idf/components/esp_lcd/interface \ + -isystem esp-idf/components/esp_lcd/dsi/include +endif + ifneq ($(CIRCUITPY_ESPCAMERA),0) SRC_CAMERA := \ $(wildcard common-hal/espcamera/*.c) \ @@ -757,6 +767,9 @@ endif ifneq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),0) ESP_IDF_COMPONENTS_LINK += esp_lcd endif +ifneq ($(CIRCUITPY_MIPIDSI),0) + ESP_IDF_COMPONENTS_LINK += esp_lcd +endif ifneq ($(CIRCUITPY_PARALLELDISPLAYBUS),0) ESP_IDF_COMPONENTS_LINK += esp_lcd endif diff --git a/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h b/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h index 646fb62e5623e..1bbf3a250fcd2 100644 --- a/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h +++ b/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h @@ -1,6 +1,6 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT @@ -16,5 +16,10 @@ #define DEFAULT_UART_BUS_RX (&pin_GPIO38) #define DEFAULT_UART_BUS_TX (&pin_GPIO37) +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO8) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO7) + // Use the second USB device (numbered 0 and 1) #define CIRCUITPY_USB_DEVICE_INSTANCE 1 + +#define CIRCUITPY_USB_HOST_INSTANCE 0 diff --git a/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c b/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c index 3bb64f434d02f..7a3026f914dd9 100644 --- a/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c +++ b/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c @@ -1,6 +1,6 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT @@ -9,47 +9,72 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, - { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, - { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, - { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, - { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, - { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + // Header Block J1 + { MP_ROM_QSTR(MP_QSTR_I2C_SDA), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_I2C_SCL), MP_ROM_PTR(&pin_GPIO8) }, { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, - { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, - { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, - { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, - { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, - { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, - { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, - { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, - { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, - { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_IO35), MP_ROM_PTR(&pin_GPIO35) }, - { MP_ROM_QSTR(MP_QSTR_IO36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_IO23), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_IO37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO38) }, { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, - { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, - { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, - { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, - { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, - { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, - { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, - { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_C6_WAKEUP), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO36), MP_ROM_PTR(&pin_GPIO36) }, + + { MP_ROM_QSTR(MP_QSTR_IO32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_IO24), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_IO25), MP_ROM_PTR(&pin_GPIO25) }, + + { MP_ROM_QSTR(MP_QSTR_IO33), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_IO26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_C6_EN), MP_ROM_PTR(&pin_GPIO54) }, + { MP_ROM_QSTR(MP_QSTR_IO54), MP_ROM_PTR(&pin_GPIO54) }, + { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, + + { MP_ROM_QSTR(MP_QSTR_PA_CTRL), MP_ROM_PTR(&pin_GPIO53) }, + { MP_ROM_QSTR(MP_QSTR_IO53), MP_ROM_PTR(&pin_GPIO53) }, { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, { MP_ROM_QSTR(MP_QSTR_IO47), MP_ROM_PTR(&pin_GPIO47) }, - { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, - { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_IO27), MP_ROM_PTR(&pin_GPIO27) }, + + // I2S + { MP_ROM_QSTR(MP_QSTR_I2S_DSDIN), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCK), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_I2S_ASDOUT), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SCLK), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_ROM_PTR(&pin_GPIO13) }, + + // Ethernet + { MP_ROM_QSTR(MP_QSTR_RMII_RXDV), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_RMII_RXD0), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_RMII_RXD1), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_MDC), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_RMII_TXD0), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_RMII_TXD1), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_RMII_TXEN), MP_ROM_PTR(&pin_GPIO49) }, + { MP_ROM_QSTR(MP_QSTR_RMII_CLK), MP_ROM_PTR(&pin_GPIO50) }, + { MP_ROM_QSTR(MP_QSTR_PHY_RSTN), MP_ROM_PTR(&pin_GPIO51) }, + { MP_ROM_QSTR(MP_QSTR_MDIO), MP_ROM_PTR(&pin_GPIO52) }, - { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, - { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + // SD Card + { MP_ROM_QSTR(MP_QSTR_SD_DATA0), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SD_DATA1), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_SD_DATA2), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SD_DATA3), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_SD_CMD), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_SD_PWRN), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/m5stack_tab5/board.c b/ports/espressif/boards/m5stack_tab5/board.c new file mode 100644 index 0000000000000..a3a9eec047145 --- /dev/null +++ b/ports/espressif/boards/m5stack_tab5/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/m5stack_tab5/mpconfigboard.h b/ports/espressif/boards/m5stack_tab5/mpconfigboard.h new file mode 100644 index 0000000000000..f85f1fe24b8ab --- /dev/null +++ b/ports/espressif/boards/m5stack_tab5/mpconfigboard.h @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "M5Stack Tab5" +#define MICROPY_HW_MCU_NAME "ESP32P4" + +// I2C bus for touch, IMU, RTC, power monitor, and GPIO expanders +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO32, .sda = &pin_GPIO31}} + +// UART +#define DEFAULT_UART_BUS_RX (&pin_GPIO38) +#define DEFAULT_UART_BUS_TX (&pin_GPIO37) + +// SPI on M5-Bus +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO5) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO19) + +// Use the second USB device (numbered 0 and 1) +#define CIRCUITPY_USB_DEVICE_INSTANCE 1 diff --git a/ports/espressif/boards/m5stack_tab5/mpconfigboard.mk b/ports/espressif/boards/m5stack_tab5/mpconfigboard.mk new file mode 100644 index 0000000000000..4871a259e7a48 --- /dev/null +++ b/ports/espressif/boards/m5stack_tab5/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x303A +USB_PID = 0x832B +USB_PRODUCT = "M5Stack Tab5" +USB_MANUFACTURER = "M5Stack" + +IDF_TARGET = esp32p4 + +CIRCUITPY_ESP_FLASH_SIZE = 16MB +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m + +CIRCUITPY_ESP_PSRAM_SIZE = 32MB +CIRCUITPY_ESP_PSRAM_MODE = hpi +CIRCUITPY_ESP_PSRAM_FREQ = 200m diff --git a/ports/espressif/boards/m5stack_tab5/pins.c b/ports/espressif/boards/m5stack_tab5/pins.c new file mode 100644 index 0000000000000..67012cc670df2 --- /dev/null +++ b/ports/espressif/boards/m5stack_tab5/pins.c @@ -0,0 +1,128 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // M5-Bus and general GPIO + { MP_ROM_QSTR(MP_QSTR_G2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_G3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_G4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_G6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_G7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_G16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_G17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_G35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_G45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_G47), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_G48), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_G51), MP_ROM_PTR(&pin_GPIO51) }, + { MP_ROM_QSTR(MP_QSTR_G52), MP_ROM_PTR(&pin_GPIO52) }, + + // SPI (on M5-Bus) + { MP_ROM_QSTR(MP_QSTR_G5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_G18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_G19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO19) }, + + // UART (on M5-Bus) + { MP_ROM_QSTR(MP_QSTR_G37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_TXD0), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_G38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_RXD0), MP_ROM_PTR(&pin_GPIO38) }, + + // I2C (Touch, IMU, RTC, Power Monitor, GPIO Expanders) + { MP_ROM_QSTR(MP_QSTR_G31), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_G32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO32) }, + + // Touch Panel + { MP_ROM_QSTR(MP_QSTR_G23), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_TP_INT), MP_ROM_PTR(&pin_GPIO23) }, + + // LCD + { MP_ROM_QSTR(MP_QSTR_G22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_GPIO22) }, + + // Audio ES8388/ES7210 + { MP_ROM_QSTR(MP_QSTR_G26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DSDIN), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_G27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SCLK), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_G28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_I2S_ASDOUT), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_G29), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCK), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_G30), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_ROM_PTR(&pin_GPIO30) }, + + // Camera + { MP_ROM_QSTR(MP_QSTR_G36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_CAM_MCLK), MP_ROM_PTR(&pin_GPIO36) }, + + // microSD Card (SDIO mode) + { MP_ROM_QSTR(MP_QSTR_G39), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT0), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_G40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT1), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_G41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT2), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_G42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT3), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_G43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_G44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_SD_CMD), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO44) }, + + // RS485 + { MP_ROM_QSTR(MP_QSTR_G20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_RS485_TX), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_G21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_RS485_RX), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_G34), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_RS485_DIR), MP_ROM_PTR(&pin_GPIO34) }, + + // HY2.0-4P PORT.A + { MP_ROM_QSTR(MP_QSTR_G53), MP_ROM_PTR(&pin_GPIO53) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_Y), MP_ROM_PTR(&pin_GPIO53) }, + { MP_ROM_QSTR(MP_QSTR_G54), MP_ROM_PTR(&pin_GPIO54) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_W), MP_ROM_PTR(&pin_GPIO54) }, + + // ESP32-C6 SDIO interface + { MP_ROM_QSTR(MP_QSTR_G8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_D3), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_G9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_D2), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_G10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_D1), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_G11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_D0), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_G12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_CK), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_G13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_CMD), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_G14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_C6_IO2), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_G15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_C6_RESET), MP_ROM_PTR(&pin_GPIO15) }, + + // I2C and SPI objects + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/m5stack_tab5/sdkconfig b/ports/espressif/boards/m5stack_tab5/sdkconfig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/common-hal/microcontroller/Pin.c b/ports/espressif/common-hal/microcontroller/Pin.c index 922cde763e363..3ccd8dcbb693b 100644 --- a/ports/espressif/common-hal/microcontroller/Pin.c +++ b/ports/espressif/common-hal/microcontroller/Pin.c @@ -77,6 +77,15 @@ static uint64_t _in_use_pin_mask; #define GPIO_SEL_47 ((uint64_t)PIN_BIT(47)) /*!< Pin 47 selected */ #define GPIO_SEL_48 ((uint64_t)PIN_BIT(48)) /*!< Pin 48 selected */ #endif +#if SOC_GPIO_PIN_COUNT > 49 +#define GPIO_SEL_49 ((uint64_t)PIN_BIT(49)) /*!< Pin 49 selected */ +#define GPIO_SEL_50 ((uint64_t)PIN_BIT(50)) /*!< Pin 50 selected */ +#define GPIO_SEL_51 ((uint64_t)PIN_BIT(51)) /*!< Pin 51 selected */ +#define GPIO_SEL_52 ((uint64_t)PIN_BIT(52)) /*!< Pin 52 selected */ +#define GPIO_SEL_53 ((uint64_t)PIN_BIT(53)) /*!< Pin 53 selected */ +#define GPIO_SEL_54 ((uint64_t)PIN_BIT(54)) /*!< Pin 54 selected */ +#define GPIO_SEL_55 ((uint64_t)PIN_BIT(55)) /*!< Pin 55 selected */ +#endif // Bit mask of all pins that should never EVER be reset. // Typically these are SPI flash and PSRAM control pins, and communication pins. @@ -202,10 +211,10 @@ static const uint64_t pin_mask_reset_forbidden = GPIO_SEL_32 | GPIO_SEL_33 | GPIO_SEL_34 | - #if CIRCUITPY_ESP_USB_SERIAL_JTAG + #if CIRCUITPY_ESP_USB_SERIAL_JTAG || (defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) && CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) // Never ever reset serial/JTAG communication pins. - GPIO_SEL_50 | // USB D- - GPIO_SEL_51 | // USB D+ + GPIO_SEL_24 | // USB D- + GPIO_SEL_25 | // USB D+ #endif #if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) && CONFIG_ESP_CONSOLE_UART_DEFAULT && CONFIG_ESP_CONSOLE_UART_NUM == 0 // Never reset debug UART/console pins. diff --git a/ports/espressif/common-hal/mipidsi/Bus.c b/ports/espressif/common-hal/mipidsi/Bus.c new file mode 100644 index 0000000000000..7f9dc01b773ec --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/Bus.c @@ -0,0 +1,52 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/mipidsi/Bus.h" +#include "bindings/espidf/__init__.h" +#include "py/runtime.h" +#include + +void common_hal_mipidsi_bus_construct(mipidsi_bus_obj_t *self, mp_uint_t frequency, uint8_t num_lanes) { + self->frequency = frequency; + self->num_data_lanes = num_lanes; + self->bus_handle = NULL; + + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + // Create the DSI bus + esp_lcd_dsi_bus_config_t bus_config = { + .bus_id = 0, + .num_data_lanes = num_lanes, + .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, + .lane_bit_rate_mbps = frequency / 1000000, // Convert Hz to MHz + }; + + CHECK_ESP_RESULT(esp_lcd_new_dsi_bus(&bus_config, &self->bus_handle)); +} + +void common_hal_mipidsi_bus_deinit(mipidsi_bus_obj_t *self) { + if (common_hal_mipidsi_bus_deinited(self)) { + return; + } + + // Delete the DSI bus + if (self->bus_handle != NULL) { + esp_lcd_del_dsi_bus(self->bus_handle); + self->bus_handle = NULL; + } + + self->frequency = 0; + self->num_data_lanes = 0; +} + +bool common_hal_mipidsi_bus_deinited(mipidsi_bus_obj_t *self) { + return self->bus_handle == NULL; +} diff --git a/ports/espressif/common-hal/mipidsi/Bus.h b/ports/espressif/common-hal/mipidsi/Bus.h new file mode 100644 index 0000000000000..0ffd333e52dd8 --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/Bus.h @@ -0,0 +1,17 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include + +typedef struct { + mp_obj_base_t base; + mp_uint_t frequency; + esp_lcd_dsi_bus_handle_t bus_handle; + uint8_t num_data_lanes; +} mipidsi_bus_obj_t; diff --git a/ports/espressif/common-hal/mipidsi/Display.c b/ports/espressif/common-hal/mipidsi/Display.c new file mode 100644 index 0000000000000..eff06f02bf7d4 --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/Display.c @@ -0,0 +1,254 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/mipidsi/Display.h" +#include "shared-bindings/mipidsi/Bus.h" +#include "shared-bindings/pwmio/PWMOut.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/time/__init__.h" +#include "bindings/espidf/__init__.h" +#include +#include +#include "py/runtime.h" + +// Cache write-back function (should be from rom/cache.h but it's not always available) +extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); + +void common_hal_mipidsi_display_construct(mipidsi_display_obj_t *self, + mipidsi_bus_obj_t *bus, + const uint8_t *init_sequence, + size_t init_sequence_len, + mp_uint_t virtual_channel, + mp_uint_t width, + mp_uint_t height, + mp_int_t rotation, + mp_uint_t color_depth, + const mcu_pin_obj_t *backlight_pin, + mp_float_t brightness, + mp_uint_t native_frames_per_second, + bool backlight_on_high, + mp_uint_t hsync_pulse_width, + mp_uint_t hsync_back_porch, + mp_uint_t hsync_front_porch, + mp_uint_t vsync_pulse_width, + mp_uint_t vsync_back_porch, + mp_uint_t vsync_front_porch, + mp_uint_t pixel_clock_frequency) { + self->bus = bus; + self->virtual_channel = virtual_channel; + self->width = width; + self->height = height; + self->rotation = rotation; + self->color_depth = color_depth; + self->native_frames_per_second = native_frames_per_second; + self->backlight_on_high = backlight_on_high; + self->framebuffer = NULL; + self->dbi_io_handle = NULL; + self->dpi_panel_handle = NULL; + + // Create the DBI interface for sending commands + esp_lcd_dbi_io_config_t dbi_config = { + .virtual_channel = virtual_channel, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + + CHECK_ESP_RESULT(esp_lcd_new_panel_io_dbi(bus->bus_handle, &dbi_config, &self->dbi_io_handle)); + + // Determine the pixel format based on color depth + lcd_color_format_t color_format; + if (color_depth == 16) { + color_format = LCD_COLOR_FMT_RGB565; + } else if (color_depth == 24) { + color_format = LCD_COLOR_FMT_RGB888; + } else { + common_hal_mipidsi_display_deinit(self); + mp_raise_ValueError(MP_ERROR_TEXT("Color depth must be 16 or 24")); + } + + // Create the DPI panel for sending pixel data + esp_lcd_dpi_panel_config_t dpi_config = { + .virtual_channel = virtual_channel, + .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, + .dpi_clock_freq_mhz = pixel_clock_frequency / 1000000, + .in_color_format = color_format, + .num_fbs = 1, + .video_timing = { + .h_size = width, + .v_size = height, + .hsync_pulse_width = hsync_pulse_width, + .hsync_back_porch = hsync_back_porch, + .hsync_front_porch = hsync_front_porch, + .vsync_pulse_width = vsync_pulse_width, + .vsync_back_porch = vsync_back_porch, + .vsync_front_porch = vsync_front_porch, + }, + .flags = { + .use_dma2d = false, + .disable_lp = false, + }, + }; + + esp_err_t ret = esp_lcd_new_panel_dpi(bus->bus_handle, &dpi_config, &self->dpi_panel_handle); + if (ret != ESP_OK) { + common_hal_mipidsi_display_deinit(self); + CHECK_ESP_RESULT(ret); + } + + // Get the framebuffer allocated by the driver + void *fb = NULL; + ret = esp_lcd_dpi_panel_get_frame_buffer(self->dpi_panel_handle, 1, &fb); + if (ret != ESP_OK || fb == NULL) { + common_hal_mipidsi_display_deinit(self); + CHECK_ESP_RESULT(ret); + } + + self->framebuffer = (uint8_t *)fb; + self->framebuffer_size = width * height * (color_depth / 8); + + // Send initialization sequence (format matches busdisplay) + #define DELAY 0x80 + uint32_t i = 0; + while (i < init_sequence_len) { + const uint8_t *cmd = init_sequence + i; + uint8_t data_size = *(cmd + 1); + bool delay = (data_size & DELAY) != 0; + data_size &= ~DELAY; + const uint8_t *data = cmd + 2; + esp_lcd_panel_io_tx_param(self->dbi_io_handle, cmd[0], data, data_size); + + uint16_t delay_length_ms = 0; + if (delay) { + data_size++; + delay_length_ms = *(cmd + 1 + data_size); + if (delay_length_ms == 255) { + delay_length_ms = 500; + } + } + common_hal_time_delay_ms(delay_length_ms); + i += 2 + data_size; + } + + // Initialize the panel after sending init commands + ret = esp_lcd_panel_init(self->dpi_panel_handle); + if (ret != ESP_OK) { + common_hal_mipidsi_display_deinit(self); + CHECK_ESP_RESULT(ret); + } + + // Setup backlight PWM if pin is provided + self->backlight_inout.base.type = &mp_type_NoneType; + if (backlight_pin != NULL && common_hal_mcu_pin_is_free(backlight_pin)) { + #if (CIRCUITPY_PWMIO) + pwmout_result_t result = common_hal_pwmio_pwmout_construct(&self->backlight_pwm, backlight_pin, 0, 50000, false); + if (result != PWMOUT_OK) { + self->backlight_inout.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(&self->backlight_inout, backlight_pin); + common_hal_never_reset_pin(backlight_pin); + } else { + self->backlight_pwm.base.type = &pwmio_pwmout_type; + common_hal_pwmio_pwmout_never_reset(&self->backlight_pwm); + } + #else + self->backlight_inout.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(&self->backlight_inout, backlight_pin); + common_hal_never_reset_pin(backlight_pin); + #endif + + // Set initial brightness + #if (CIRCUITPY_PWMIO) + if (self->backlight_pwm.base.type == &pwmio_pwmout_type) { + common_hal_pwmio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t)(brightness * 0xFFFF)); + } else + #endif + if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { + bool on = brightness > 0; + if (!backlight_on_high) { + on = !on; + } + common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, on); + } + } +} + +void common_hal_mipidsi_display_deinit(mipidsi_display_obj_t *self) { + if (common_hal_mipidsi_display_deinited(self)) { + return; + } + + // Cleanup backlight + #if (CIRCUITPY_PWMIO) + if (self->backlight_pwm.base.type == &pwmio_pwmout_type) { + common_hal_pwmio_pwmout_deinit(&self->backlight_pwm); + } else + #endif + if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { + common_hal_digitalio_digitalinout_deinit(&self->backlight_inout); + } + + // Delete the DPI panel + if (self->dpi_panel_handle != NULL) { + esp_lcd_panel_del(self->dpi_panel_handle); + self->dpi_panel_handle = NULL; + } + + // Delete the DBI interface + if (self->dbi_io_handle != NULL) { + esp_lcd_panel_io_del(self->dbi_io_handle); + self->dbi_io_handle = NULL; + } + + self->bus = NULL; + self->framebuffer = NULL; +} + +bool common_hal_mipidsi_display_deinited(mipidsi_display_obj_t *self) { + return self->dpi_panel_handle == NULL; +} + +void common_hal_mipidsi_display_refresh(mipidsi_display_obj_t *self) { + // Write back the cache to ensure framebuffer changes are visible + Cache_WriteBack_Addr((uint32_t)(self->framebuffer), self->framebuffer_size); + + // The DPI panel will automatically refresh from the framebuffer + // No explicit refresh call is needed as the DSI hardware continuously + // sends data from the framebuffer to the display +} + +int common_hal_mipidsi_display_get_width(mipidsi_display_obj_t *self) { + return self->width; +} + +int common_hal_mipidsi_display_get_height(mipidsi_display_obj_t *self) { + return self->height; +} + +int common_hal_mipidsi_display_get_row_stride(mipidsi_display_obj_t *self) { + return self->width * (self->color_depth / 8); +} + +int common_hal_mipidsi_display_get_color_depth(mipidsi_display_obj_t *self) { + return self->color_depth; +} + +int common_hal_mipidsi_display_get_native_frames_per_second(mipidsi_display_obj_t *self) { + return self->native_frames_per_second; +} + +bool common_hal_mipidsi_display_get_grayscale(mipidsi_display_obj_t *self) { + return false; +} + +mp_int_t common_hal_mipidsi_display_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + mipidsi_display_obj_t *self = (mipidsi_display_obj_t *)self_in; + + bufinfo->buf = self->framebuffer; + bufinfo->len = self->framebuffer_size; + bufinfo->typecode = 'B'; + + return 0; +} diff --git a/ports/espressif/common-hal/mipidsi/Display.h b/ports/espressif/common-hal/mipidsi/Display.h new file mode 100644 index 0000000000000..61e6cb73f6d75 --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/Display.h @@ -0,0 +1,37 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/mipidsi/Bus.h" +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/digitalio/DigitalInOut.h" +#include "common-hal/pwmio/PWMOut.h" +#include +#include + +typedef struct { + mp_obj_base_t base; + mipidsi_bus_obj_t *bus; + mp_uint_t virtual_channel; + mp_uint_t width; + mp_uint_t height; + mp_int_t rotation; + mp_uint_t color_depth; + mp_uint_t native_frames_per_second; + uint8_t *framebuffer; + esp_lcd_panel_io_handle_t dbi_io_handle; + esp_lcd_panel_handle_t dpi_panel_handle; + size_t framebuffer_size; + union { + digitalio_digitalinout_obj_t backlight_inout; + #if CIRCUITPY_PWMIO + pwmio_pwmout_obj_t backlight_pwm; + #endif + }; + bool backlight_on_high; +} mipidsi_display_obj_t; diff --git a/ports/espressif/common-hal/mipidsi/__init__.c b/ports/espressif/common-hal/mipidsi/__init__.c new file mode 100644 index 0000000000000..173f2146e87ac --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/mipidsi/__init__.h" diff --git a/ports/espressif/common-hal/mipidsi/__init__.h b/ports/espressif/common-hal/mipidsi/__init__.h new file mode 100644 index 0000000000000..972a7c082fd7b --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index f133e86c6e068..c3585c7e39984 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -312,6 +312,9 @@ CIRCUITPY_PARALLELDISPLAYBUS = 0 # Library doesn't support P4 yet it seems CIRCUITPY_ESPCAMERA = 0 +# P4 has MIPI-DSI +CIRCUITPY_MIPIDSI = 1 + #### esp32s2 ########################################################## else ifeq ($(IDF_TARGET),esp32s2) # Modules diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index c629eb52932e7..192a91717169a 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -244,7 +244,7 @@ safe_mode_t port_init(void) { #endif // Send the ROM output out of the UART. This includes early logs. - #if DEBUG + #if DEBUG && (defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) && CONFIG_ESP_CONSOLE_UART_DEFAULT) esp_rom_install_uart_printf(); #endif diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index f4ac2350ffbf1..56c836cb3a490 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -294,6 +294,9 @@ endif ifeq ($(CIRCUITPY_MICROCONTROLLER),1) SRC_PATTERNS += microcontroller/% endif +ifeq ($(CIRCUITPY_MIPIDSI),1) +SRC_PATTERNS += mipidsi/% +endif ifeq ($(CIRCUITPY_MSGPACK),1) SRC_PATTERNS += msgpack/% endif @@ -531,6 +534,9 @@ SRC_COMMON_HAL_ALL = \ mdns/__init__.c \ mdns/Server.c \ mdns/RemoteService.c \ + mipidsi/Bus.c \ + mipidsi/Display.c \ + mipidsi/__init__.c \ neopixel_write/__init__.c \ nvm/ByteArray.c \ nvm/__init__.c \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 09ef4361427da..9bbe5691dfb3b 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -405,6 +405,9 @@ CFLAGS += -DCIRCUITPY_MEMORYMONITOR=$(CIRCUITPY_MEMORYMONITOR) CIRCUITPY_MICROCONTROLLER ?= 1 CFLAGS += -DCIRCUITPY_MICROCONTROLLER=$(CIRCUITPY_MICROCONTROLLER) +CIRCUITPY_MIPIDSI ?= 0 +CFLAGS += -DCIRCUITPY_MIPIDSI=$(CIRCUITPY_MIPIDSI) + CIRCUITPY_MSGPACK ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_MSGPACK=$(CIRCUITPY_MSGPACK) diff --git a/shared-bindings/mipidsi/Bus.c b/shared-bindings/mipidsi/Bus.c new file mode 100644 index 0000000000000..2f2ce046fe06c --- /dev/null +++ b/shared-bindings/mipidsi/Bus.c @@ -0,0 +1,96 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/mipidsi/Bus.h" +#include "shared-bindings/util.h" +#include "shared/runtime/context_manager_helpers.h" + +//| class Bus: +//| def __init__( +//| self, +//| *, +//| frequency: int = 500_000_000, +//| num_lanes: int = 2, +//| ) -> None: +//| """Create a MIPI DSI Bus object. +//| +//| This creates a DSI bus interface. The specific pins used are determined by the board. +//| DSI supports 1-4 data lanes. +//| +//| :param int frequency: the high speed clock frequency in Hz (default 500 MHz) +//| :param int num_lanes: the number of data lanes to use (default 2, range 1-4) +//| """ +//| + +static mp_obj_t mipidsi_bus_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_frequency, ARG_num_lanes }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 500000000} }, + { MP_QSTR_num_lanes, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 2} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mipidsi_bus_obj_t *self = mp_obj_malloc(mipidsi_bus_obj_t, &mipidsi_bus_type); + + mp_uint_t frequency = (mp_uint_t)mp_arg_validate_int_min(args[ARG_frequency].u_int, 1, MP_QSTR_frequency); + uint8_t num_lanes = (uint8_t)mp_arg_validate_int_range(args[ARG_num_lanes].u_int, 1, 4, MP_QSTR_num_lanes); + + common_hal_mipidsi_bus_construct(self, frequency, num_lanes); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Free the resources (pins, timers, etc.) associated with this +//| `mipidsi.Bus` instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +//| +static mp_obj_t mipidsi_bus_deinit(mp_obj_t self_in) { + mipidsi_bus_obj_t *self = (mipidsi_bus_obj_t *)self_in; + common_hal_mipidsi_bus_deinit(self); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_bus_deinit_obj, mipidsi_bus_deinit); + +//| def __enter__(self) -> Bus: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +static mp_obj_t mipidsi_bus_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_mipidsi_bus_deinit(args[0]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mipidsi_bus___exit___obj, 4, 4, mipidsi_bus_obj___exit__); + +static const mp_rom_map_elem_t mipidsi_bus_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mipidsi_bus_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mipidsi_bus___exit___obj) }, +}; +static MP_DEFINE_CONST_DICT(mipidsi_bus_locals_dict, mipidsi_bus_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mipidsi_bus_type, + MP_QSTR_Bus, + MP_TYPE_FLAG_NONE, + make_new, mipidsi_bus_make_new, + locals_dict, &mipidsi_bus_locals_dict + ); diff --git a/shared-bindings/mipidsi/Bus.h b/shared-bindings/mipidsi/Bus.h new file mode 100644 index 0000000000000..f2fd13f7fc165 --- /dev/null +++ b/shared-bindings/mipidsi/Bus.h @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/mipidsi/Bus.h" + +extern const mp_obj_type_t mipidsi_bus_type; + +void common_hal_mipidsi_bus_construct(mipidsi_bus_obj_t *self, mp_uint_t frequency, uint8_t num_lanes); +void common_hal_mipidsi_bus_deinit(mipidsi_bus_obj_t *self); +bool common_hal_mipidsi_bus_deinited(mipidsi_bus_obj_t *self); diff --git a/shared-bindings/mipidsi/Display.c b/shared-bindings/mipidsi/Display.c new file mode 100644 index 0000000000000..a553c7ca8fd55 --- /dev/null +++ b/shared-bindings/mipidsi/Display.c @@ -0,0 +1,289 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objtype.h" + +#include "shared-bindings/mipidsi/Display.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" + +//| class Display: +//| def __init__( +//| self, +//| bus: Bus, +//| init_sequence: ReadableBuffer, +//| *, +//| width: int, +//| height: int, +//| hsync_pulse_width: int, +//| hsync_back_porch: int, +//| hsync_front_porch: int, +//| vsync_pulse_width: int, +//| vsync_back_porch: int, +//| vsync_front_porch: int, +//| pixel_clock_frequency: int, +//| virtual_channel: int = 0, +//| rotation: int = 0, +//| color_depth: int = 16, +//| backlight_pin: Optional[microcontroller.Pin] = None, +//| brightness: float = 1.0, +//| native_frames_per_second: int = 60, +//| backlight_on_high: bool = True, +//| ) -> None: +//| """Create a MIPI DSI Display object connected to the given bus. +//| +//| This allocates a framebuffer and configures the DSI display to use the +//| specified virtual channel for communication. +//| +//| The framebuffer pixel format varies depending on color_depth: +//| +//| * 16 - Each two bytes is a pixel in RGB565 format. +//| * 24 - Each three bytes is a pixel in RGB888 format. +//| +//| A Display is often used in conjunction with a +//| `framebufferio.FramebufferDisplay`. +//| +//| :param Bus bus: the DSI bus to use +//| :param ~circuitpython_typing.ReadableBuffer init_sequence: Byte-packed initialization sequence for the display +//| :param int width: the width of the framebuffer in pixels +//| :param int height: the height of the framebuffer in pixels +//| :param int hsync_pulse_width: horizontal sync pulse width in pixel clocks +//| :param int hsync_back_porch: horizontal back porch in pixel clocks +//| :param int hsync_front_porch: horizontal front porch in pixel clocks +//| :param int vsync_pulse_width: vertical sync pulse width in lines +//| :param int vsync_back_porch: vertical back porch in lines +//| :param int vsync_front_porch: vertical front porch in lines +//| :param int pixel_clock_frequency: pixel clock frequency in Hz +//| :param int virtual_channel: the DSI virtual channel (0-3) +//| :param int rotation: the rotation of the display in degrees clockwise (0, 90, 180, 270) +//| :param int color_depth: the color depth of the framebuffer in bits (16 or 24) +//| :param microcontroller.Pin backlight_pin: Pin connected to the display's backlight +//| :param float brightness: Initial display brightness (0.0 to 1.0) +//| :param int native_frames_per_second: Number of display refreshes per second +//| :param bool backlight_on_high: If True, pulling the backlight pin high turns the backlight on +//| """ +//| + +static mp_obj_t mipidsi_display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_hsync_pulse_width, ARG_hsync_back_porch, + ARG_hsync_front_porch, ARG_vsync_pulse_width, ARG_vsync_back_porch, ARG_vsync_front_porch, + ARG_pixel_clock_frequency, ARG_virtual_channel, ARG_rotation, + ARG_color_depth, ARG_backlight_pin, ARG_brightness, ARG_native_frames_per_second, + ARG_backlight_on_high }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_init_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_hsync_pulse_width, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_hsync_back_porch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_hsync_front_porch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_vsync_pulse_width, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_vsync_back_porch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_vsync_front_porch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_pixel_clock_frequency, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_virtual_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_rotation, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_color_depth, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16} }, + { MP_QSTR_backlight_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_brightness, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, + { MP_QSTR_native_frames_per_second, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 60} }, + { MP_QSTR_backlight_on_high, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mipidsi_display_obj_t *self = &allocate_display_bus_or_raise()->mipidsi; + self->base.type = &mipidsi_display_type; + + mipidsi_bus_obj_t *bus = mp_arg_validate_type(args[ARG_bus].u_obj, &mipidsi_bus_type, MP_QSTR_bus); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_init_sequence].u_obj, &bufinfo, MP_BUFFER_READ); + + const mcu_pin_obj_t *backlight_pin = + validate_obj_is_free_pin_or_none(args[ARG_backlight_pin].u_obj, MP_QSTR_backlight_pin); + + mp_float_t brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + + mp_int_t rotation = args[ARG_rotation].u_int; + if (rotation % 90 != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("Display rotation must be in 90 degree increments")); + } + + mp_uint_t virtual_channel = (mp_uint_t)mp_arg_validate_int_range(args[ARG_virtual_channel].u_int, 0, 3, MP_QSTR_virtual_channel); + mp_uint_t width = (mp_uint_t)mp_arg_validate_int_min(args[ARG_width].u_int, 0, MP_QSTR_width); + mp_uint_t height = (mp_uint_t)mp_arg_validate_int_min(args[ARG_height].u_int, 0, MP_QSTR_height); + mp_uint_t color_depth = args[ARG_color_depth].u_int; + + if (color_depth != 8 && color_depth != 16 && color_depth != 24) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_color_depth); + } + + common_hal_mipidsi_display_construct(self, bus, bufinfo.buf, bufinfo.len, virtual_channel, width, height, + rotation, color_depth, MP_OBJ_TO_PTR(backlight_pin), brightness, + args[ARG_native_frames_per_second].u_int, + args[ARG_backlight_on_high].u_bool, + args[ARG_hsync_pulse_width].u_int, + args[ARG_hsync_back_porch].u_int, + args[ARG_hsync_front_porch].u_int, + args[ARG_vsync_pulse_width].u_int, + args[ARG_vsync_back_porch].u_int, + args[ARG_vsync_front_porch].u_int, + args[ARG_pixel_clock_frequency].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +// Helper to ensure we have the native super class instead of a subclass. +static mipidsi_display_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native_display = mp_obj_cast_to_native_base(display_obj, &mipidsi_display_type); + mp_obj_assert_native_inited(native_display); + return MP_OBJ_TO_PTR(native_display); +} + +//| def deinit(self) -> None: +//| """Free the resources (pins, timers, etc.) associated with this +//| `mipidsi.Display` instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +//| +static mp_obj_t mipidsi_display_deinit(mp_obj_t self_in) { + mipidsi_display_obj_t *self = native_display(self_in); + common_hal_mipidsi_display_deinit(self); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_display_deinit_obj, mipidsi_display_deinit); + +static void check_for_deinit(mipidsi_display_obj_t *self) { + if (common_hal_mipidsi_display_deinited(self)) { + raise_deinited_error(); + } +} + +//| width: int +//| """The width of the framebuffer, in pixels.""" +static mp_obj_t mipidsi_display_get_width(mp_obj_t self_in) { + mipidsi_display_obj_t *self = native_display(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_mipidsi_display_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_display_get_width_obj, mipidsi_display_get_width); +MP_PROPERTY_GETTER(mipidsi_display_width_obj, + (mp_obj_t)&mipidsi_display_get_width_obj); + +//| height: int +//| """The height of the framebuffer, in pixels.""" +//| +//| +static mp_obj_t mipidsi_display_get_height(mp_obj_t self_in) { + mipidsi_display_obj_t *self = native_display(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_mipidsi_display_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_display_get_height_obj, mipidsi_display_get_height); + +MP_PROPERTY_GETTER(mipidsi_display_height_obj, + (mp_obj_t)&mipidsi_display_get_height_obj); + +//| color_depth: int +//| """The color depth of the framebuffer.""" +static mp_obj_t mipidsi_display_get_color_depth(mp_obj_t self_in) { + mipidsi_display_obj_t *self = native_display(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_mipidsi_display_get_color_depth(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_display_get_color_depth_obj, mipidsi_display_get_color_depth); +MP_PROPERTY_GETTER(mipidsi_display_color_depth_obj, + (mp_obj_t)&mipidsi_display_get_color_depth_obj); + + +static const mp_rom_map_elem_t mipidsi_display_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mipidsi_display_deinit_obj) }, + + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&mipidsi_display_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&mipidsi_display_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_color_depth), MP_ROM_PTR(&mipidsi_display_color_depth_obj) }, +}; +static MP_DEFINE_CONST_DICT(mipidsi_display_locals_dict, mipidsi_display_locals_dict_table); + +static void mipidsi_display_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) { + common_hal_mipidsi_display_get_buffer(self_in, bufinfo, 0); +} + +// These versions exist so that the prototype matches the protocol, +// avoiding a type cast that can hide errors +static void mipidsi_display_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) { + (void)dirty_row_bitmap; + common_hal_mipidsi_display_refresh(self_in); +} + +static void mipidsi_display_deinit_proto(mp_obj_t self_in) { + common_hal_mipidsi_display_deinit(self_in); +} + +static int mipidsi_display_get_width_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_width(self_in); +} + +static int mipidsi_display_get_height_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_height(self_in); +} + +static int mipidsi_display_get_color_depth_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_color_depth(self_in); +} + +static bool mipidsi_display_get_grayscale_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_grayscale(self_in); +} + +static int mipidsi_display_get_bytes_per_cell_proto(mp_obj_t self_in) { + return 1; +} + +static int mipidsi_display_get_native_frames_per_second_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_native_frames_per_second(self_in); +} + +static bool mipidsi_display_get_pixels_in_byte_share_row_proto(mp_obj_t self_in) { + return true; +} + +static int mipidsi_display_get_row_stride_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_row_stride(self_in); +} + +static const framebuffer_p_t mipidsi_display_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer) + .get_bufinfo = mipidsi_display_get_bufinfo, + .get_width = mipidsi_display_get_width_proto, + .get_height = mipidsi_display_get_height_proto, + .get_color_depth = mipidsi_display_get_color_depth_proto, + .get_grayscale = mipidsi_display_get_grayscale_proto, + .get_row_stride = mipidsi_display_get_row_stride_proto, + .get_bytes_per_cell = mipidsi_display_get_bytes_per_cell_proto, + .get_native_frames_per_second = mipidsi_display_get_native_frames_per_second_proto, + .get_pixels_in_byte_share_row = mipidsi_display_get_pixels_in_byte_share_row_proto, + .swapbuffers = mipidsi_display_swapbuffers, + .deinit = mipidsi_display_deinit_proto, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mipidsi_display_type, + MP_QSTR_Display, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + locals_dict, &mipidsi_display_locals_dict, + make_new, mipidsi_display_make_new, + buffer, common_hal_mipidsi_display_get_buffer, + protocol, &mipidsi_display_proto + ); diff --git a/shared-bindings/mipidsi/Display.h b/shared-bindings/mipidsi/Display.h new file mode 100644 index 0000000000000..1cec788f16cd9 --- /dev/null +++ b/shared-bindings/mipidsi/Display.h @@ -0,0 +1,44 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/mipidsi/Display.h" +#include "shared-bindings/mipidsi/Bus.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t mipidsi_display_type; + +void common_hal_mipidsi_display_construct(mipidsi_display_obj_t *self, + mipidsi_bus_obj_t *bus, + const uint8_t *init_sequence, + size_t init_sequence_len, + mp_uint_t virtual_channel, + mp_uint_t width, + mp_uint_t height, + mp_int_t rotation, + mp_uint_t color_depth, + const mcu_pin_obj_t *backlight_pin, + mp_float_t brightness, + mp_uint_t native_frames_per_second, + bool backlight_on_high, + mp_uint_t hsync_pulse_width, + mp_uint_t hsync_back_porch, + mp_uint_t hsync_front_porch, + mp_uint_t vsync_pulse_width, + mp_uint_t vsync_back_porch, + mp_uint_t vsync_front_porch, + mp_uint_t pixel_clock_frequency); +void common_hal_mipidsi_display_deinit(mipidsi_display_obj_t *self); +bool common_hal_mipidsi_display_deinited(mipidsi_display_obj_t *self); +void common_hal_mipidsi_display_refresh(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_width(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_height(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_row_stride(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_color_depth(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_native_frames_per_second(mipidsi_display_obj_t *self); +bool common_hal_mipidsi_display_get_grayscale(mipidsi_display_obj_t *self); +mp_int_t common_hal_mipidsi_display_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); diff --git a/shared-bindings/mipidsi/__init__.c b/shared-bindings/mipidsi/__init__.c new file mode 100644 index 0000000000000..0b6b856592918 --- /dev/null +++ b/shared-bindings/mipidsi/__init__.c @@ -0,0 +1,30 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/mipidsi/Bus.h" +#include "shared-bindings/mipidsi/Display.h" + +//| """Low-level routines for interacting with MIPI DSI""" + +static const mp_rom_map_elem_t mipidsi_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mipidsi) }, + { MP_ROM_QSTR(MP_QSTR_Bus), MP_ROM_PTR(&mipidsi_bus_type) }, + { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&mipidsi_display_type) }, +}; + +static MP_DEFINE_CONST_DICT(mipidsi_module_globals, mipidsi_module_globals_table); + +const mp_obj_module_t mipidsi_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mipidsi_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mipidsi, mipidsi_module); diff --git a/shared-bindings/mipidsi/__init__.h b/shared-bindings/mipidsi/__init__.h new file mode 100644 index 0000000000000..972a7c082fd7b --- /dev/null +++ b/shared-bindings/mipidsi/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 29b8c64c97240..f2f4691b313a7 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -38,6 +38,9 @@ #if CIRCUITPY_DOTCLOCKFRAMEBUFFER #include "common-hal/dotclockframebuffer/DotClockFramebuffer.h" #endif +#if CIRCUITPY_MIPIDSI +#include "shared-bindings/mipidsi/Display.h" +#endif // Port unique frame buffers. #if CIRCUITPY_VIDEOCORE #include "bindings/videocore/Framebuffer.h" @@ -81,6 +84,9 @@ typedef struct { #if CIRCUITPY_AURORA_EPAPER aurora_epaper_framebuffer_obj_t aurora_epaper; #endif + #if CIRCUITPY_MIPIDSI + mipidsi_display_obj_t mipidsi; + #endif }; } primary_display_bus_t; From eb84f5a6b3984cedcd68a525ab07b0f8e55ab764 Mon Sep 17 00:00:00 2001 From: chinh4thepro Date: Mon, 3 Nov 2025 19:29:47 -0500 Subject: [PATCH 201/517] ports/espressif: add MT* pin aliases to seeed xiao esp32s3 sense board --- ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c index 8269f1d7a951e..222b17aa3372f 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c @@ -68,6 +68,10 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO8) }, { MP_ROM_QSTR(MP_QSTR_MOSI_1), MP_ROM_PTR(&pin_GPIO11) }, { MP_ROM_QSTR(MP_QSTR_MISO_1), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_MTCK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_MTDO), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_MTDI), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_MTMS), MP_ROM_PTR(&pin_GPIO42) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_SCK_1), MP_ROM_PTR(&pin_GPIO13) }, { MP_ROM_QSTR(MP_QSTR_SDCS), MP_ROM_PTR(&pin_GPIO21) }, @@ -89,9 +93,6 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_MIC_DATA), MP_ROM_PTR(&pin_GPIO41) }, { MP_ROM_QSTR(MP_QSTR_MIC_CLK), MP_ROM_PTR(&pin_GPIO42) }, - { MP_ROM_QSTR(MP_QSTR_I2S_SD), MP_ROM_PTR(&pin_GPIO38) }, - { MP_ROM_QSTR(MP_QSTR_I2S_SCK), MP_ROM_PTR(&pin_GPIO39) }, - { MP_ROM_QSTR(MP_QSTR_I2S_WS), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, From 672f6855410e1eea71e7bedc97eafc6eff496d27 Mon Sep 17 00:00:00 2001 From: chinh4thepro Date: Mon, 3 Nov 2025 19:30:48 -0500 Subject: [PATCH 202/517] ports/espressif: add back the I2S pin aliases --- ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c index 222b17aa3372f..aa0d72b4d8699 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c @@ -93,6 +93,9 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_MIC_DATA), MP_ROM_PTR(&pin_GPIO41) }, { MP_ROM_QSTR(MP_QSTR_MIC_CLK), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SD), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SCK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WS), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, From 785d5991bdfd955edf2c1f50e147c393e3d6d8ed Mon Sep 17 00:00:00 2001 From: chinh4thepro Date: Mon, 3 Nov 2025 22:34:54 -0500 Subject: [PATCH 203/517] seeed_xiao_esp32_s3_sense: stylize pins.c --- .../boards/seeed_xiao_esp32_s3_sense/pins.c | 90 +++++++++++-------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c index aa0d72b4d8699..fcf48c83659a3 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c @@ -31,71 +31,91 @@ static const mp_rom_obj_tuple_t camera_data_tuple = { static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO1) }, - { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO2) }, - { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO4) }, - { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO5) }, - { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SD), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_CAM_VSYNC), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SCK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_MTCK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_CAM_SCL), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WS), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_MTDO), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_MTDI), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_MIC_DATA), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_RX_1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_MTMS), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_MIC_CLK), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_TX_1), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CAM_XCLK), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_SCK_1), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_CAM_PCLK), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_MISO_1), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D5), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO11) }, - { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO5) }, - { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO6) }, - { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, - { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, - { MP_ROM_QSTR(MP_QSTR_TX_1), MP_ROM_PTR(&pin_GPIO42) }, - { MP_ROM_QSTR(MP_QSTR_RX_1), MP_ROM_PTR(&pin_GPIO41) }, - { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO8) }, { MP_ROM_QSTR(MP_QSTR_MOSI_1), MP_ROM_PTR(&pin_GPIO11) }, - { MP_ROM_QSTR(MP_QSTR_MISO_1), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_MTCK), MP_ROM_PTR(&pin_GPIO39) }, - { MP_ROM_QSTR(MP_QSTR_MTDO), MP_ROM_PTR(&pin_GPIO40) }, - { MP_ROM_QSTR(MP_QSTR_MTDI), MP_ROM_PTR(&pin_GPIO41) }, - { MP_ROM_QSTR(MP_QSTR_MTMS), MP_ROM_PTR(&pin_GPIO42) }, - { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO7) }, - { MP_ROM_QSTR(MP_QSTR_SCK_1), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D6), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO21) }, { MP_ROM_QSTR(MP_QSTR_SDCS), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CAM_DATA), MP_ROM_PTR(&camera_data_tuple) }, { MP_ROM_QSTR(MP_QSTR_CAM_D0), MP_ROM_PTR(&pin_GPIO15) }, { MP_ROM_QSTR(MP_QSTR_CAM_D1), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_CAM_D2), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_CAM_D3), MP_ROM_PTR(&pin_GPIO16) }, { MP_ROM_QSTR(MP_QSTR_CAM_D4), MP_ROM_PTR(&pin_GPIO14) }, - { MP_ROM_QSTR(MP_QSTR_CAM_D5), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_CAM_D6), MP_ROM_PTR(&pin_GPIO11) }, { MP_ROM_QSTR(MP_QSTR_CAM_D7), MP_ROM_PTR(&pin_GPIO48) }, - { MP_ROM_QSTR(MP_QSTR_CAM_XCLK), MP_ROM_PTR(&pin_GPIO10) }, { MP_ROM_QSTR(MP_QSTR_CAM_HREF), MP_ROM_PTR(&pin_GPIO47) }, - { MP_ROM_QSTR(MP_QSTR_CAM_PCLK), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_CAM_VSYNC), MP_ROM_PTR(&pin_GPIO38) }, - { MP_ROM_QSTR(MP_QSTR_CAM_SCL), MP_ROM_PTR(&pin_GPIO39) }, - { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO40) }, - { MP_ROM_QSTR(MP_QSTR_MIC_DATA), MP_ROM_PTR(&pin_GPIO41) }, - { MP_ROM_QSTR(MP_QSTR_MIC_CLK), MP_ROM_PTR(&pin_GPIO42) }, - { MP_ROM_QSTR(MP_QSTR_I2S_SD), MP_ROM_PTR(&pin_GPIO38) }, - { MP_ROM_QSTR(MP_QSTR_I2S_SCK), MP_ROM_PTR(&pin_GPIO39) }, - { MP_ROM_QSTR(MP_QSTR_I2S_WS), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, From 4c037682432b536c250213b380180a97afc2d122 Mon Sep 17 00:00:00 2001 From: chinh4thepro Date: Mon, 3 Nov 2025 22:39:08 -0500 Subject: [PATCH 204/517] seeed_xiao_esp32_s3_sense: add back pin alias --- ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c index fcf48c83659a3..fbb6014685ca4 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c @@ -49,6 +49,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, From f469e93d53b6b6bc5c16a3b0291d996c0be4a4dc Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 3 Nov 2025 11:27:07 -0800 Subject: [PATCH 205/517] Fix Zephyr build --- ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml | 1 + 7 files changed, 7 insertions(+) diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 222a325e346b6..35966d6f9e2c6 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -66,6 +66,7 @@ mdns = false memorymap = false memorymonitor = false microcontroller = true +mipidsi = false msgpack = false neopixel_write = false nvm = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 41631c3d6c02c..e3b6cc2248f2e 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -66,6 +66,7 @@ mdns = false memorymap = false memorymonitor = false microcontroller = true +mipidsi = false msgpack = false neopixel_write = false nvm = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index ea009aa36e1ca..328105d715059 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -66,6 +66,7 @@ mdns = false memorymap = false memorymonitor = false microcontroller = true +mipidsi = false msgpack = false neopixel_write = false nvm = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index a96d1bad1d2a6..2bc6e3e03a249 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -66,6 +66,7 @@ mdns = false memorymap = false memorymonitor = false microcontroller = true +mipidsi = false msgpack = false neopixel_write = false nvm = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index e4946decb190b..2a2e16fd16afe 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -66,6 +66,7 @@ mdns = false memorymap = false memorymonitor = false microcontroller = true +mipidsi = false msgpack = false neopixel_write = false nvm = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 7519d8817687b..5042eb972b143 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -66,6 +66,7 @@ mdns = false memorymap = false memorymonitor = false microcontroller = true +mipidsi = false msgpack = false neopixel_write = false nvm = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index e029020982d20..cfe48a90ceea4 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -66,6 +66,7 @@ mdns = false memorymap = false memorymonitor = false microcontroller = true +mipidsi = false msgpack = false neopixel_write = false nvm = false From 7b452dc29a37b0976e0e6e8bb4ef38716d1b666e Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 4 Nov 2025 17:23:52 +0100 Subject: [PATCH 206/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 4 ++-- locale/ru.po | 4 ++-- locale/tr.po | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index fa1cb2d40b738..e59cc974e0e76 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -4876,8 +4876,8 @@ msgstr "" #~ msgstr "IV musí být dlouhé %d bajtů" #~ msgid "" -#~ "Incompatible .mpy file. Please update all .mpy files. See http://adafru." -#~ "it/mpy-update for more info." +#~ "Incompatible .mpy file. Please update all .mpy files. See http://" +#~ "adafru.it/mpy-update for more info." #~ msgstr "" #~ "Nekompatibilní soubor .mpy. Aktualizujte prosím všechny soubory .mpy. " #~ "Další informace naleznete na adrese http://adafru.it/mpy-update." diff --git a/locale/ru.po b/locale/ru.po index af7c8bca194d2..83bfaf324d7f6 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -5149,8 +5149,8 @@ msgstr "zi должен иметь форму (n_section, 2)" #~ msgstr "IV должен быть длиной %d байт" #~ msgid "" -#~ "Incompatible .mpy file. Please update all .mpy files. See http://adafru." -#~ "it/mpy-update for more info." +#~ "Incompatible .mpy file. Please update all .mpy files. See http://" +#~ "adafru.it/mpy-update for more info." #~ msgstr "" #~ "Несовместимый файл .mpy. Пожалуйста, обновите все файлы .mpy. См. http://" #~ "adafru.it/mpy-update для получения дополнительной информации." diff --git a/locale/tr.po b/locale/tr.po index 758137eae21f8..4e03f59cbaffb 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -4818,8 +4818,8 @@ msgstr "" #~ msgstr "IV %d bayt uzunluğunda olmalı" #~ msgid "" -#~ "Incompatible .mpy file. Please update all .mpy files. See http://adafru." -#~ "it/mpy-update for more info." +#~ "Incompatible .mpy file. Please update all .mpy files. See http://" +#~ "adafru.it/mpy-update for more info." #~ msgstr "" #~ "Uyumsuz .mpy dosyası. Lütfen tüm .mpy dosyalarını güncelleyin. Daha fazla " #~ "bilgi için http://adafru.it/mpy-update ." From d2dcfecb89420de5ad1495b7d5fa6d7bb5417f23 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 4 Nov 2025 14:01:24 -0500 Subject: [PATCH 207/517] espressif_esp32p4_function_ev/pins.c: add whitespace to clearly show pin aliases --- .../boards/espressif_esp32p4_function_ev/pins.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c b/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c index 7a3026f914dd9..165ce711b736a 100644 --- a/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c +++ b/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c @@ -12,18 +12,25 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { // Header Block J1 { MP_ROM_QSTR(MP_QSTR_I2C_SDA), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_I2C_SCL), MP_ROM_PTR(&pin_GPIO8) }, { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_IO23), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_IO37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO38) }, { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, { MP_ROM_QSTR(MP_QSTR_IO22), MP_ROM_PTR(&pin_GPIO22) }, { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_C6_WAKEUP), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, @@ -36,12 +43,15 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_IO33), MP_ROM_PTR(&pin_GPIO33) }, { MP_ROM_QSTR(MP_QSTR_IO26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_C6_EN), MP_ROM_PTR(&pin_GPIO54) }, { MP_ROM_QSTR(MP_QSTR_IO54), MP_ROM_PTR(&pin_GPIO54) }, + { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, { MP_ROM_QSTR(MP_QSTR_PA_CTRL), MP_ROM_PTR(&pin_GPIO53) }, { MP_ROM_QSTR(MP_QSTR_IO53), MP_ROM_PTR(&pin_GPIO53) }, + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, { MP_ROM_QSTR(MP_QSTR_IO47), MP_ROM_PTR(&pin_GPIO47) }, { MP_ROM_QSTR(MP_QSTR_IO27), MP_ROM_PTR(&pin_GPIO27) }, From 292d209fae5750df85537f2f4c33936b2ea8c5a2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 4 Nov 2025 16:29:30 -0800 Subject: [PATCH 208/517] Better reasoned allocations --- ports/espressif/common-hal/mipidsi/Bus.c | 17 +++++++++++ ports/espressif/common-hal/mipidsi/Bus.h | 4 +++ ports/espressif/common-hal/mipidsi/Display.c | 4 ++- shared-bindings/mipidsi/Bus.c | 30 ++++++-------------- shared-bindings/mipidsi/Display.c | 2 +- shared-module/displayio/__init__.c | 17 ++++++++++- 6 files changed, 50 insertions(+), 24 deletions(-) diff --git a/ports/espressif/common-hal/mipidsi/Bus.c b/ports/espressif/common-hal/mipidsi/Bus.c index 7f9dc01b773ec..dbb559ea30f22 100644 --- a/ports/espressif/common-hal/mipidsi/Bus.c +++ b/ports/espressif/common-hal/mipidsi/Bus.c @@ -14,6 +14,10 @@ void common_hal_mipidsi_bus_construct(mipidsi_bus_obj_t *self, mp_uint_t frequen self->num_data_lanes = num_lanes; self->bus_handle = NULL; + if (self->use_count > 0) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_mipidsi); + } + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; esp_ldo_channel_config_t ldo_mipi_phy_config = { .chan_id = 3, @@ -33,6 +37,9 @@ void common_hal_mipidsi_bus_construct(mipidsi_bus_obj_t *self, mp_uint_t frequen } void common_hal_mipidsi_bus_deinit(mipidsi_bus_obj_t *self) { + if (self->use_count > 0) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_Bus); + } if (common_hal_mipidsi_bus_deinited(self)) { return; } @@ -50,3 +57,13 @@ void common_hal_mipidsi_bus_deinit(mipidsi_bus_obj_t *self) { bool common_hal_mipidsi_bus_deinited(mipidsi_bus_obj_t *self) { return self->bus_handle == NULL; } + +void mipidsi_bus_increment_use_count(mipidsi_bus_obj_t *self) { + self->use_count++; +} +void mipidsi_bus_decrement_use_count(mipidsi_bus_obj_t *self) { + self->use_count--; + if (self->use_count == 0) { + common_hal_mipidsi_bus_deinit(self); + } +} diff --git a/ports/espressif/common-hal/mipidsi/Bus.h b/ports/espressif/common-hal/mipidsi/Bus.h index 0ffd333e52dd8..b6257fd7223dd 100644 --- a/ports/espressif/common-hal/mipidsi/Bus.h +++ b/ports/espressif/common-hal/mipidsi/Bus.h @@ -14,4 +14,8 @@ typedef struct { mp_uint_t frequency; esp_lcd_dsi_bus_handle_t bus_handle; uint8_t num_data_lanes; + uint8_t use_count; // Up to 4 displays } mipidsi_bus_obj_t; + +void mipidsi_bus_increment_use_count(mipidsi_bus_obj_t *self); +void mipidsi_bus_decrement_use_count(mipidsi_bus_obj_t *self); diff --git a/ports/espressif/common-hal/mipidsi/Display.c b/ports/espressif/common-hal/mipidsi/Display.c index eff06f02bf7d4..dac16162e91ff 100644 --- a/ports/espressif/common-hal/mipidsi/Display.c +++ b/ports/espressif/common-hal/mipidsi/Display.c @@ -67,7 +67,7 @@ void common_hal_mipidsi_display_construct(mipidsi_display_obj_t *self, color_format = LCD_COLOR_FMT_RGB888; } else { common_hal_mipidsi_display_deinit(self); - mp_raise_ValueError(MP_ERROR_TEXT("Color depth must be 16 or 24")); + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_color_depth); } // Create the DPI panel for sending pixel data @@ -173,6 +173,7 @@ void common_hal_mipidsi_display_construct(mipidsi_display_obj_t *self, common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, on); } } + mipidsi_bus_increment_use_count(self->bus); } void common_hal_mipidsi_display_deinit(mipidsi_display_obj_t *self) { @@ -202,6 +203,7 @@ void common_hal_mipidsi_display_deinit(mipidsi_display_obj_t *self) { self->dbi_io_handle = NULL; } + mipidsi_bus_decrement_use_count(self->bus); self->bus = NULL; self->framebuffer = NULL; } diff --git a/shared-bindings/mipidsi/Bus.c b/shared-bindings/mipidsi/Bus.c index 2f2ce046fe06c..bac3cd3c75e1b 100644 --- a/shared-bindings/mipidsi/Bus.c +++ b/shared-bindings/mipidsi/Bus.c @@ -28,6 +28,13 @@ //| :param int num_lanes: the number of data lanes to use (default 2, range 1-4) //| """ //| +// +// +// All MCUs we support only have one DSI bus but it can be shared between multiple displays. One +// display may live longer than the VM, so we need to allocate the bus outside the VM. To simplify +// memory tracking, we use a global object for the bus. +// +static mipidsi_bus_obj_t _mipidsi_bus_obj; static mp_obj_t mipidsi_bus_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_frequency, ARG_num_lanes }; @@ -38,7 +45,8 @@ static mp_obj_t mipidsi_bus_make_new(const mp_obj_type_t *type, size_t n_args, s mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mipidsi_bus_obj_t *self = mp_obj_malloc(mipidsi_bus_obj_t, &mipidsi_bus_type); + _mipidsi_bus_obj.base.type = &mipidsi_bus_type; + mipidsi_bus_obj_t *self = &_mipidsi_bus_obj; mp_uint_t frequency = (mp_uint_t)mp_arg_validate_int_min(args[ARG_frequency].u_int, 1, MP_QSTR_frequency); uint8_t num_lanes = (uint8_t)mp_arg_validate_int_range(args[ARG_num_lanes].u_int, 1, 4, MP_QSTR_num_lanes); @@ -62,28 +70,8 @@ static mp_obj_t mipidsi_bus_deinit(mp_obj_t self_in) { static MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_bus_deinit_obj, mipidsi_bus_deinit); -//| def __enter__(self) -> Bus: -//| """No-op used by Context Managers.""" -//| ... -//| -// Provided by context manager helper. - -//| def __exit__(self) -> None: -//| """Automatically deinitializes the hardware when exiting a context. See -//| :ref:`lifetime-and-contextmanagers` for more info.""" -//| ... -//| -static mp_obj_t mipidsi_bus_obj___exit__(size_t n_args, const mp_obj_t *args) { - (void)n_args; - common_hal_mipidsi_bus_deinit(args[0]); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mipidsi_bus___exit___obj, 4, 4, mipidsi_bus_obj___exit__); - static const mp_rom_map_elem_t mipidsi_bus_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mipidsi_bus_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mipidsi_bus___exit___obj) }, }; static MP_DEFINE_CONST_DICT(mipidsi_bus_locals_dict, mipidsi_bus_locals_dict_table); diff --git a/shared-bindings/mipidsi/Display.c b/shared-bindings/mipidsi/Display.c index a553c7ca8fd55..ad4522d653c7f 100644 --- a/shared-bindings/mipidsi/Display.c +++ b/shared-bindings/mipidsi/Display.c @@ -124,7 +124,7 @@ static mp_obj_t mipidsi_display_make_new(const mp_obj_type_t *type, size_t n_arg mp_uint_t height = (mp_uint_t)mp_arg_validate_int_min(args[ARG_height].u_int, 0, MP_QSTR_height); mp_uint_t color_depth = args[ARG_color_depth].u_int; - if (color_depth != 8 && color_depth != 16 && color_depth != 24) { + if (color_depth != 16 && color_depth != 24) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_color_depth); } diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 3ffd4d002f534..747fdbec5a75a 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -41,6 +41,10 @@ #include "shared-module/aurora_epaper/aurora_framebuffer.h" #endif +#if CIRCUITPY_MIPIDSI +#include "shared-bindings/mipidsi/Display.h" +#endif + #ifdef BOARD_USE_INTERNAL_SPI #include "supervisor/spi_flash_api.h" #endif @@ -64,7 +68,7 @@ displayio_buffer_transform_t null_transform = { .transpose_xy = false }; -#if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE || CIRCUITPY_PICODVI +#if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE || CIRCUITPY_PICODVI || CIRCUITPY_MIPIDSI static bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) { for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { if (displays[i].display_base.type == &framebufferio_framebufferdisplay_type) { @@ -180,6 +184,10 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { } else if (bus_type == &picodvi_framebuffer_type) { common_hal_picodvi_framebuffer_deinit(&display_buses[i].picodvi); #endif + #if CIRCUITPY_MIPIDSI + } else if (bus_type == &mipidsi_display_type) { + common_hal_mipidsi_display_deinit(&display_buses[i].mipidsi); + #endif } display_buses[i].bus_base.type = &mp_type_NoneType; } @@ -333,6 +341,13 @@ void reset_displays(void) { // Set to None, gets deinit'd up by display_base display_buses[i].bus_base.type = &mp_type_NoneType; #endif + #if CIRCUITPY_MIPIDSI + } else if (display_bus_type == &mipidsi_display_type) { + mipidsi_display_obj_t *display = &display_buses[i].mipidsi; + if (!any_display_uses_this_framebuffer(&display->base)) { + common_hal_mipidsi_display_deinit(display); + } + #endif } else { // Not an active display bus. continue; From 000fa5ab5f3a3cdf077399af8a2c8ac9830aaefc Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 4 Nov 2025 16:36:14 -0800 Subject: [PATCH 209/517] Use latest macos in CI --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f837d793bddc..ddd4a2eb93697 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -107,7 +107,7 @@ jobs: cp-version: ${{ needs.scheduler.outputs.cp-version }} mpy-cross-mac: - runs-on: macos-13 + runs-on: macos-latest needs: scheduler if: needs.scheduler.outputs.ports != '{}' env: From 91e36f1b38f33719062168e3a7707edfff630948 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 5 Nov 2025 12:10:55 -0500 Subject: [PATCH 210/517] stop building intel and universal macos mpy-cross --- .github/workflows/build.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ddd4a2eb93697..fbd3af440c6b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -131,33 +131,18 @@ jobs: gcc --version python3 --version msgfmt --version - - name: Build mpy-cross - run: make -C mpy-cross -j4 - - uses: actions/upload-artifact@v4 - with: - name: mpy-cross-macos-x64 - path: mpy-cross/build/mpy-cross - name: Build mpy-cross (arm64) run: make -C mpy-cross -j4 -f Makefile.m1 V=2 - uses: actions/upload-artifact@v4 with: name: mpy-cross-macos-arm64 path: mpy-cross/build-arm64/mpy-cross-arm64 - - name: Make universal binary - run: lipo -create -output mpy-cross-macos-universal mpy-cross/build/mpy-cross mpy-cross/build-arm64/mpy-cross-arm64 - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: mpy-cross-macos-universal - path: mpy-cross-macos-universal - name: Upload to S3 if: >- (github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository_owner == 'adafruit') || (github.event_name == 'release' && (github.event.action == 'published' || github.event.action == 'rerequested')) run: | - [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross-macos-universal s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-universal --no-progress --region us-east-1 [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/build-arm64/mpy-cross-arm64 s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-arm64 --no-progress --region us-east-1 - [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/build/mpy-cross s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-x64 --no-progress --region us-east-1 env: AWS_PAGER: '' AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} From 7c9f9f6d3b40ad2b7efcade4f3c7fc4635112eea Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 5 Nov 2025 18:18:29 +0100 Subject: [PATCH 211/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 17 ++++++++--------- locale/el.po | 20 +++++++++++--------- locale/hi.po | 17 ++++++++--------- locale/ko.po | 22 ++++++++++++---------- locale/ru.po | 23 ++++++++++++++--------- locale/tr.po | 17 ++++++++--------- 6 files changed, 61 insertions(+), 55 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index e59cc974e0e76..44adf48833626 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -170,10 +170,6 @@ msgstr "Délka %q musí být <= %d" msgid "%q length must be >= %d" msgstr "Délka %q musí být >= %d" -#: py/runtime.c -msgid "%q moved from %q to %q" -msgstr "" - #: py/argcheck.c msgid "%q must be %d" msgstr "%q musí být %d" @@ -854,6 +850,10 @@ msgstr "Jádro kódu CircuitPython tvrdě havarovalo. Jejda!\n" msgid "Clock unit in use" msgstr "Jednotka hodin je používána" +#: ports/espressif/common-hal/mipidsi/Display.c +msgid "Color depth must be 16 or 24" +msgstr "" + #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -950,6 +950,7 @@ msgstr "Displej musí mít 16bitový barevný prostor." #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "Otočení displeje musí být po 90 stupních" @@ -1296,7 +1297,8 @@ msgstr "" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c #: shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c @@ -2290,6 +2292,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "Není možné vytvořit zámek" @@ -3203,10 +3206,6 @@ msgstr "" msgid "file write is not available" msgstr "zápis do souboru není dostupný" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "První argument musí být zavolatelný" diff --git a/locale/el.po b/locale/el.po index 91624ceb08672..01f199d5c6a14 100644 --- a/locale/el.po +++ b/locale/el.po @@ -174,10 +174,6 @@ msgstr "%q μήκος πρέπει να είναι <= %d" msgid "%q length must be >= %d" msgstr "%q μήκος πρέπει να είναι >= %d" -#: py/runtime.c -msgid "%q moved from %q to %q" -msgstr "%q μετακινήθηκε από το %q στο %q" - #: py/argcheck.c msgid "%q must be %d" msgstr "%q πρέπει να είναι %d" @@ -859,6 +855,10 @@ msgstr "Ο πυρήνας της CircuitPython κατέρευσε. Οουπς!\n msgid "Clock unit in use" msgstr "Μονάδα ρολογιού ήδη σε χρήση" +#: ports/espressif/common-hal/mipidsi/Display.c +msgid "Color depth must be 16 or 24" +msgstr "" + #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -957,6 +957,7 @@ msgstr "Η οθόνη πρέπει να έχει 16 bit χρωματική ευ #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "Η περιστροφή της οθόνη πρέπει να γίνεται σε βήματα 90 μοιρών" @@ -1302,7 +1303,8 @@ msgstr "" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c #: shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c @@ -2294,6 +2296,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -3202,10 +3205,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" @@ -4583,6 +4582,9 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "%q moved from %q to %q" +#~ msgstr "%q μετακινήθηκε από το %q στο %q" + #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c απαιτεί ακέραιο ή χαρακτήρα" diff --git a/locale/hi.po b/locale/hi.po index 1ab48338ebaba..ea538c2f6f3dc 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -161,10 +161,6 @@ msgstr "" msgid "%q length must be >= %d" msgstr "" -#: py/runtime.c -msgid "%q moved from %q to %q" -msgstr "" - #: py/argcheck.c msgid "%q must be %d" msgstr "" @@ -840,6 +836,10 @@ msgstr "" msgid "Clock unit in use" msgstr "" +#: ports/espressif/common-hal/mipidsi/Display.c +msgid "Color depth must be 16 or 24" +msgstr "" + #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -934,6 +934,7 @@ msgstr "" #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "" @@ -1278,7 +1279,8 @@ msgstr "" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c #: shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c @@ -2268,6 +2270,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -3176,10 +3179,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 35bfed4de08e9..96d8003ff9324 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -172,11 +172,6 @@ msgstr "%q 길이는 <= %d>여야 합니다" msgid "%q length must be >= %d" msgstr "%q 길이는 >= %d이어야 합니다" -#: py/runtime.c -#, fuzzy -msgid "%q moved from %q to %q" -msgstr "%q가 %q에서 %q로 이동했습니다" - #: py/argcheck.c msgid "%q must be %d" msgstr "%q는 %d이어야 합니다" @@ -885,6 +880,10 @@ msgstr "CircuitPython 핵심 코드가 심하게 충돌했습니다. 앗!\n" msgid "Clock unit in use" msgstr "시계 장치가 사용 중입니다" +#: ports/espressif/common-hal/mipidsi/Display.c +msgid "Color depth must be 16 or 24" +msgstr "" + #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -980,6 +979,7 @@ msgstr "디스플레이는 16 비트 색 공간을 가져야 합니다." #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "디스플레이 회전은 90도씩 증가해야 합니다" @@ -1330,7 +1330,8 @@ msgstr "출력 함수로 인해 종료되었다" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c #: shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c @@ -2344,6 +2345,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -3252,10 +3254,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" @@ -4633,6 +4631,10 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, fuzzy +#~ msgid "%q moved from %q to %q" +#~ msgstr "%q가 %q에서 %q로 이동했습니다" + #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c 전수(int)또는 캐릭터(char)필요합니다" diff --git a/locale/ru.po b/locale/ru.po index 83bfaf324d7f6..3b957b347c133 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -174,10 +174,6 @@ msgstr "Длинна %q должна быть <= %d" msgid "%q length must be >= %d" msgstr "Длинна %q должна быть >= %d" -#: py/runtime.c -msgid "%q moved from %q to %q" -msgstr "%q переместился из %q в %q" - #: py/argcheck.c msgid "%q must be %d" msgstr "%q должно быть %d" @@ -862,6 +858,10 @@ msgstr "Основной код CircuitPython сильно разбился. У msgid "Clock unit in use" msgstr "Источник тактирования уже используется" +#: ports/espressif/common-hal/mipidsi/Display.c +msgid "Color depth must be 16 or 24" +msgstr "" + #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -960,6 +960,7 @@ msgstr "Дисплей должен иметь 16 битное цветовое #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "Поворот дисплея должен осуществляться с шагом 90 градусов" @@ -1317,7 +1318,8 @@ msgstr "Прерывается функцией выхода" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c #: shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c @@ -2324,6 +2326,7 @@ msgid "Unable to allocate to the heap." msgstr "Невозможно выделить место в куче." #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "Не удается создать блокировку" @@ -3257,10 +3260,6 @@ msgstr "Файл должен быть файлом, открытым в бай msgid "file write is not available" msgstr "Запись файлов недоступна" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "Файловая система должна предусматривать метод монтирования" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "Первый аргумент должен быть вызываемым" @@ -4650,6 +4649,12 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" +#~ msgid "%q moved from %q to %q" +#~ msgstr "%q переместился из %q в %q" + +#~ msgid "filesystem must provide mount method" +#~ msgstr "Файловая система должна предусматривать метод монтирования" + #~ msgid "start/end indices" #~ msgstr "Начальные/конечные индексы" diff --git a/locale/tr.po b/locale/tr.po index 4e03f59cbaffb..54155a605438a 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -172,10 +172,6 @@ msgstr "%q boyutu <= %d olmalıdır" msgid "%q length must be >= %d" msgstr "%q boyutu >= %d olmalıdır" -#: py/runtime.c -msgid "%q moved from %q to %q" -msgstr "" - #: py/argcheck.c msgid "%q must be %d" msgstr "%q, %d olmalıdır" @@ -854,6 +850,10 @@ msgstr "CircuitPython kor kodu patladı. Haydaaa!\n" msgid "Clock unit in use" msgstr "Saat ünitesi kullanımda" +#: ports/espressif/common-hal/mipidsi/Display.c +msgid "Color depth must be 16 or 24" +msgstr "" + #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -948,6 +948,7 @@ msgstr "Ekran 16 bitlik bir renk uzayına sahip olmalıdır." #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "Ekran dönüşü 90 derecelik artışlarla olmalıdır" @@ -1296,7 +1297,8 @@ msgstr "" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c #: shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c @@ -2290,6 +2292,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -3198,10 +3201,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" From 91d4181a38c8dccff7fba060d512ed850d55010e Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 5 Nov 2025 12:58:05 -0500 Subject: [PATCH 212/517] update frozen libraries --- frozen/Adafruit_CircuitPython_AHTx0 | 2 +- frozen/Adafruit_CircuitPython_APDS9960 | 2 +- frozen/Adafruit_CircuitPython_BLE | 2 +- frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center | 2 +- frozen/Adafruit_CircuitPython_Bitmap_Font | 2 +- frozen/Adafruit_CircuitPython_BusDevice | 2 +- frozen/Adafruit_CircuitPython_CircuitPlayground | 2 +- frozen/Adafruit_CircuitPython_ConnectionManager | 2 +- frozen/Adafruit_CircuitPython_Crickit | 2 +- frozen/Adafruit_CircuitPython_DRV2605 | 2 +- frozen/Adafruit_CircuitPython_DS3231 | 2 +- frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 | 2 +- frozen/Adafruit_CircuitPython_Display_Text | 2 +- frozen/Adafruit_CircuitPython_DotStar | 2 +- frozen/Adafruit_CircuitPython_ESP32SPI | 2 +- frozen/Adafruit_CircuitPython_FakeRequests | 2 +- frozen/Adafruit_CircuitPython_FocalTouch | 2 +- frozen/Adafruit_CircuitPython_HID | 2 +- frozen/Adafruit_CircuitPython_HTTPServer | 2 +- frozen/Adafruit_CircuitPython_IRRemote | 2 +- frozen/Adafruit_CircuitPython_IS31FL3731 | 2 +- frozen/Adafruit_CircuitPython_ImageLoad | 2 +- frozen/Adafruit_CircuitPython_LC709203F | 2 +- frozen/Adafruit_CircuitPython_LED_Animation | 2 +- frozen/Adafruit_CircuitPython_LIS3DH | 2 +- frozen/Adafruit_CircuitPython_LSM6DS | 2 +- frozen/Adafruit_CircuitPython_MIDI | 2 +- frozen/Adafruit_CircuitPython_MPU6050 | 2 +- frozen/Adafruit_CircuitPython_Motor | 2 +- frozen/Adafruit_CircuitPython_NeoPixel | 2 +- frozen/Adafruit_CircuitPython_PCF8563 | 2 +- frozen/Adafruit_CircuitPython_Pixel_Framebuf | 2 +- frozen/Adafruit_CircuitPython_PortalBase | 2 +- frozen/Adafruit_CircuitPython_ProgressBar | 2 +- frozen/Adafruit_CircuitPython_RFM69 | 2 +- frozen/Adafruit_CircuitPython_RFM9x | 2 +- frozen/Adafruit_CircuitPython_Register | 2 +- frozen/Adafruit_CircuitPython_Requests | 2 +- frozen/Adafruit_CircuitPython_SD | 2 +- frozen/Adafruit_CircuitPython_SHT4x | 2 +- frozen/Adafruit_CircuitPython_SSD1306 | 2 +- frozen/Adafruit_CircuitPython_SSD1680 | 2 +- frozen/Adafruit_CircuitPython_ST7789 | 2 +- frozen/Adafruit_CircuitPython_SimpleIO | 2 +- frozen/Adafruit_CircuitPython_SimpleMath | 2 +- frozen/Adafruit_CircuitPython_Thermistor | 2 +- frozen/Adafruit_CircuitPython_Ticks | 2 +- frozen/Adafruit_CircuitPython_UC8151D | 2 +- frozen/Adafruit_CircuitPython_Wave | 2 +- frozen/Adafruit_CircuitPython_Wiznet5k | 2 +- frozen/Adafruit_CircuitPython_asyncio | 2 +- frozen/Adafruit_CircuitPython_framebuf | 2 +- frozen/Adafruit_CircuitPython_seesaw | 2 +- 53 files changed, 53 insertions(+), 53 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_AHTx0 b/frozen/Adafruit_CircuitPython_AHTx0 index ff95dd5f3d018..8c61ed111fc83 160000 --- a/frozen/Adafruit_CircuitPython_AHTx0 +++ b/frozen/Adafruit_CircuitPython_AHTx0 @@ -1 +1 @@ -Subproject commit ff95dd5f3d0186c5cdc8bd8cb34ac22ac2e2225d +Subproject commit 8c61ed111fc83e4e1703cf5e014e645f4dbbef32 diff --git a/frozen/Adafruit_CircuitPython_APDS9960 b/frozen/Adafruit_CircuitPython_APDS9960 index 00127a75d22f0..f05a7239131dc 160000 --- a/frozen/Adafruit_CircuitPython_APDS9960 +++ b/frozen/Adafruit_CircuitPython_APDS9960 @@ -1 +1 @@ -Subproject commit 00127a75d22f035096ea9317ad57c74c6a9b4232 +Subproject commit f05a7239131dc05df949e49c1bb5732529a864bf diff --git a/frozen/Adafruit_CircuitPython_BLE b/frozen/Adafruit_CircuitPython_BLE index 1acb303cc7f63..6744b6869e4c3 160000 --- a/frozen/Adafruit_CircuitPython_BLE +++ b/frozen/Adafruit_CircuitPython_BLE @@ -1 +1 @@ -Subproject commit 1acb303cc7f63a752c9fb87655d2ec478e564be2 +Subproject commit 6744b6869e4c3112610132bcac23535ae5ef3983 diff --git a/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center b/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center index 476082b43c9e5..e162713efa578 160000 --- a/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center +++ b/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center @@ -1 +1 @@ -Subproject commit 476082b43c9e5971da20a320a05546a8285d4891 +Subproject commit e162713efa578b9967f7ec921b129362036571b3 diff --git a/frozen/Adafruit_CircuitPython_Bitmap_Font b/frozen/Adafruit_CircuitPython_Bitmap_Font index 5ca3f55f2e393..13c6a39a58e28 160000 --- a/frozen/Adafruit_CircuitPython_Bitmap_Font +++ b/frozen/Adafruit_CircuitPython_Bitmap_Font @@ -1 +1 @@ -Subproject commit 5ca3f55f2e39302c787ca93f95276e8269024038 +Subproject commit 13c6a39a58e28030f3651a90e116c1ab30f2035b diff --git a/frozen/Adafruit_CircuitPython_BusDevice b/frozen/Adafruit_CircuitPython_BusDevice index afe91665e4389..baa6bcafa2251 160000 --- a/frozen/Adafruit_CircuitPython_BusDevice +++ b/frozen/Adafruit_CircuitPython_BusDevice @@ -1 +1 @@ -Subproject commit afe91665e438947bd3d88ba4a0f937ec58ff1035 +Subproject commit baa6bcafa22512ac56f343c7d124f3b029861c33 diff --git a/frozen/Adafruit_CircuitPython_CircuitPlayground b/frozen/Adafruit_CircuitPython_CircuitPlayground index d093fed40590a..65be0763beda7 160000 --- a/frozen/Adafruit_CircuitPython_CircuitPlayground +++ b/frozen/Adafruit_CircuitPython_CircuitPlayground @@ -1 +1 @@ -Subproject commit d093fed40590af312e44b1efa8d88ecaef9aaed4 +Subproject commit 65be0763beda780d3a1a8c4c49b033628bc54d28 diff --git a/frozen/Adafruit_CircuitPython_ConnectionManager b/frozen/Adafruit_CircuitPython_ConnectionManager index 95f39faaa647b..2c85f3b98d081 160000 --- a/frozen/Adafruit_CircuitPython_ConnectionManager +++ b/frozen/Adafruit_CircuitPython_ConnectionManager @@ -1 +1 @@ -Subproject commit 95f39faaa647b4215f615603368a453742423a09 +Subproject commit 2c85f3b98d08102d2494195074ad836fc3020610 diff --git a/frozen/Adafruit_CircuitPython_Crickit b/frozen/Adafruit_CircuitPython_Crickit index efeb183228ff9..722f7937bfb0c 160000 --- a/frozen/Adafruit_CircuitPython_Crickit +++ b/frozen/Adafruit_CircuitPython_Crickit @@ -1 +1 @@ -Subproject commit efeb183228ff9640aec5938f9c2305766579dc25 +Subproject commit 722f7937bfb0c02340dcf737ebf37cc4ecf86b83 diff --git a/frozen/Adafruit_CircuitPython_DRV2605 b/frozen/Adafruit_CircuitPython_DRV2605 index f120d56222166..616d61c7495e5 160000 --- a/frozen/Adafruit_CircuitPython_DRV2605 +++ b/frozen/Adafruit_CircuitPython_DRV2605 @@ -1 +1 @@ -Subproject commit f120d56222166af85b33e8e9c70eff6aec2e4828 +Subproject commit 616d61c7495e5dadc6b77ea9fce07a3861580534 diff --git a/frozen/Adafruit_CircuitPython_DS3231 b/frozen/Adafruit_CircuitPython_DS3231 index a5d94eee49d32..62cc4dc49b587 160000 --- a/frozen/Adafruit_CircuitPython_DS3231 +++ b/frozen/Adafruit_CircuitPython_DS3231 @@ -1 +1 @@ -Subproject commit a5d94eee49d324bad474847749c3d481a1f7c908 +Subproject commit 62cc4dc49b587fad935368ed60b9ba1433250fdc diff --git a/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 b/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 index 4b382e8986db3..89463c9bd81aa 160000 --- a/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 +++ b/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 @@ -1 +1 @@ -Subproject commit 4b382e8986db36eaef558fec67be543205f268b2 +Subproject commit 89463c9bd81aaf43a14fd4f3c7bdbb75d4e48b40 diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index 7d1f187aac8e8..727a1022e140b 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit 7d1f187aac8e899e791324cc78633bf4f32c984b +Subproject commit 727a1022e140b971a2f4bde5e6571dd327f6785d diff --git a/frozen/Adafruit_CircuitPython_DotStar b/frozen/Adafruit_CircuitPython_DotStar index 4b0ba649e5abd..8d19e1b23cbe6 160000 --- a/frozen/Adafruit_CircuitPython_DotStar +++ b/frozen/Adafruit_CircuitPython_DotStar @@ -1 +1 @@ -Subproject commit 4b0ba649e5abdebead5b9a47a6c695d67c2c25fa +Subproject commit 8d19e1b23cbe6c1d17a29f321d06b16d21909b92 diff --git a/frozen/Adafruit_CircuitPython_ESP32SPI b/frozen/Adafruit_CircuitPython_ESP32SPI index 063b90c8706dd..3fcea236876b0 160000 --- a/frozen/Adafruit_CircuitPython_ESP32SPI +++ b/frozen/Adafruit_CircuitPython_ESP32SPI @@ -1 +1 @@ -Subproject commit 063b90c8706ddef97cc4abf9cb78e0cc09ff3c6c +Subproject commit 3fcea236876b05d09ebf95f43cec6016667ccf84 diff --git a/frozen/Adafruit_CircuitPython_FakeRequests b/frozen/Adafruit_CircuitPython_FakeRequests index 020121e90c630..ff942eaae8351 160000 --- a/frozen/Adafruit_CircuitPython_FakeRequests +++ b/frozen/Adafruit_CircuitPython_FakeRequests @@ -1 +1 @@ -Subproject commit 020121e90c6306147f91b8079b75f3d14ff86138 +Subproject commit ff942eaae835144f7269d8206adf506c99f699f4 diff --git a/frozen/Adafruit_CircuitPython_FocalTouch b/frozen/Adafruit_CircuitPython_FocalTouch index 2fb86313db340..f20c13bdffa9b 160000 --- a/frozen/Adafruit_CircuitPython_FocalTouch +++ b/frozen/Adafruit_CircuitPython_FocalTouch @@ -1 +1 @@ -Subproject commit 2fb86313db3408e57b1fbfbc56359ccb4f16f38b +Subproject commit f20c13bdffa9b586c648f331851f427368a995ae diff --git a/frozen/Adafruit_CircuitPython_HID b/frozen/Adafruit_CircuitPython_HID index d06b8b812caef..788e46ca2cb2f 160000 --- a/frozen/Adafruit_CircuitPython_HID +++ b/frozen/Adafruit_CircuitPython_HID @@ -1 +1 @@ -Subproject commit d06b8b812caef3ae2eebb662f4e57ca306ce3219 +Subproject commit 788e46ca2cb2febddac83e0c660972598d6a8a27 diff --git a/frozen/Adafruit_CircuitPython_HTTPServer b/frozen/Adafruit_CircuitPython_HTTPServer index c43147a016ffd..e234a4940504b 160000 --- a/frozen/Adafruit_CircuitPython_HTTPServer +++ b/frozen/Adafruit_CircuitPython_HTTPServer @@ -1 +1 @@ -Subproject commit c43147a016ffd13c57a0923730bc6a83afefb4ad +Subproject commit e234a4940504b4fb21c7338d774ec273260ef672 diff --git a/frozen/Adafruit_CircuitPython_IRRemote b/frozen/Adafruit_CircuitPython_IRRemote index b92d69304212e..98bd8fad8cd65 160000 --- a/frozen/Adafruit_CircuitPython_IRRemote +++ b/frozen/Adafruit_CircuitPython_IRRemote @@ -1 +1 @@ -Subproject commit b92d69304212ee57a5f008317fcc4ebaf75ddebb +Subproject commit 98bd8fad8cd65f481b8808e5de8cdbf62d0dd300 diff --git a/frozen/Adafruit_CircuitPython_IS31FL3731 b/frozen/Adafruit_CircuitPython_IS31FL3731 index a0d701892d8be..1babff02ea87f 160000 --- a/frozen/Adafruit_CircuitPython_IS31FL3731 +++ b/frozen/Adafruit_CircuitPython_IS31FL3731 @@ -1 +1 @@ -Subproject commit a0d701892d8bef096d80f1117bee718cecb380ff +Subproject commit 1babff02ea87f5c4863aed20be0da553d76e9600 diff --git a/frozen/Adafruit_CircuitPython_ImageLoad b/frozen/Adafruit_CircuitPython_ImageLoad index 135b0e4478b34..67532099f7a57 160000 --- a/frozen/Adafruit_CircuitPython_ImageLoad +++ b/frozen/Adafruit_CircuitPython_ImageLoad @@ -1 +1 @@ -Subproject commit 135b0e4478b34e1271e6bd87fa6d8efa0bef64b5 +Subproject commit 67532099f7a574f08955b31efb7c1ca5cc3f143c diff --git a/frozen/Adafruit_CircuitPython_LC709203F b/frozen/Adafruit_CircuitPython_LC709203F index b007bcae07b34..7fe15ca666bd3 160000 --- a/frozen/Adafruit_CircuitPython_LC709203F +++ b/frozen/Adafruit_CircuitPython_LC709203F @@ -1 +1 @@ -Subproject commit b007bcae07b346fd28aaee770dcabc9dde698c67 +Subproject commit 7fe15ca666bd3730a17e13bb29ff884092345b5f diff --git a/frozen/Adafruit_CircuitPython_LED_Animation b/frozen/Adafruit_CircuitPython_LED_Animation index 8af05705962e8..515553f0b6cb1 160000 --- a/frozen/Adafruit_CircuitPython_LED_Animation +++ b/frozen/Adafruit_CircuitPython_LED_Animation @@ -1 +1 @@ -Subproject commit 8af05705962e8bb7d2f8003e6a70916a9a51b863 +Subproject commit 515553f0b6cb1290b92965f8e4e8beab9e83bcf1 diff --git a/frozen/Adafruit_CircuitPython_LIS3DH b/frozen/Adafruit_CircuitPython_LIS3DH index 640b18ec1bfd7..a3c33eff7c03e 160000 --- a/frozen/Adafruit_CircuitPython_LIS3DH +++ b/frozen/Adafruit_CircuitPython_LIS3DH @@ -1 +1 @@ -Subproject commit 640b18ec1bfd71e0a70f7ff3b8784043cd2d2671 +Subproject commit a3c33eff7c03e7c1f1e896a08f4878f5db6a6cbf diff --git a/frozen/Adafruit_CircuitPython_LSM6DS b/frozen/Adafruit_CircuitPython_LSM6DS index 2f50836f4bf0d..e7da74fd8d7fd 160000 --- a/frozen/Adafruit_CircuitPython_LSM6DS +++ b/frozen/Adafruit_CircuitPython_LSM6DS @@ -1 +1 @@ -Subproject commit 2f50836f4bf0d9e48e4b8e046ba4d4167ad6dbdc +Subproject commit e7da74fd8d7fddd9515e975be5479596283a719c diff --git a/frozen/Adafruit_CircuitPython_MIDI b/frozen/Adafruit_CircuitPython_MIDI index c4e693c2d4904..2bc5554857727 160000 --- a/frozen/Adafruit_CircuitPython_MIDI +++ b/frozen/Adafruit_CircuitPython_MIDI @@ -1 +1 @@ -Subproject commit c4e693c2d4904d885cf842efc25687ccaccbabfa +Subproject commit 2bc555485772743b70f620fe939048020924a605 diff --git a/frozen/Adafruit_CircuitPython_MPU6050 b/frozen/Adafruit_CircuitPython_MPU6050 index 05a0c3b72279d..bb5100733f339 160000 --- a/frozen/Adafruit_CircuitPython_MPU6050 +++ b/frozen/Adafruit_CircuitPython_MPU6050 @@ -1 +1 @@ -Subproject commit 05a0c3b72279db9fa2431308a77e6ab7ba040c8a +Subproject commit bb5100733f339dcad24df7d32eeeb492023b5059 diff --git a/frozen/Adafruit_CircuitPython_Motor b/frozen/Adafruit_CircuitPython_Motor index 89facc69a405a..610c42f118704 160000 --- a/frozen/Adafruit_CircuitPython_Motor +++ b/frozen/Adafruit_CircuitPython_Motor @@ -1 +1 @@ -Subproject commit 89facc69a405ae83702ce566414adc39d46068f1 +Subproject commit 610c42f1187045fb962807ac8d895e66e2612298 diff --git a/frozen/Adafruit_CircuitPython_NeoPixel b/frozen/Adafruit_CircuitPython_NeoPixel index 0ba2f2122a54a..1a9523574b68c 160000 --- a/frozen/Adafruit_CircuitPython_NeoPixel +++ b/frozen/Adafruit_CircuitPython_NeoPixel @@ -1 +1 @@ -Subproject commit 0ba2f2122a54a71b1bc3576f87b1ba7dfc9db11e +Subproject commit 1a9523574b68cc205c151aaf080909348df7417c diff --git a/frozen/Adafruit_CircuitPython_PCF8563 b/frozen/Adafruit_CircuitPython_PCF8563 index 74bb72d1c607e..3f40c877acbbd 160000 --- a/frozen/Adafruit_CircuitPython_PCF8563 +++ b/frozen/Adafruit_CircuitPython_PCF8563 @@ -1 +1 @@ -Subproject commit 74bb72d1c607e44cf0d5349c466acd34863c11b4 +Subproject commit 3f40c877acbbda0ef82336c18f3620ce1b9013f5 diff --git a/frozen/Adafruit_CircuitPython_Pixel_Framebuf b/frozen/Adafruit_CircuitPython_Pixel_Framebuf index 1db789cf99429..d355df47c0d5c 160000 --- a/frozen/Adafruit_CircuitPython_Pixel_Framebuf +++ b/frozen/Adafruit_CircuitPython_Pixel_Framebuf @@ -1 +1 @@ -Subproject commit 1db789cf99429e27d740279000788edc794d9d0d +Subproject commit d355df47c0d5c1f80da01c86d585223988f30a33 diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase index d26e2324de496..25fc43dd67ae9 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit d26e2324de496761e0aa72abc30ba07cdce8814b +Subproject commit 25fc43dd67ae95a8e62173e90c3069502194873a diff --git a/frozen/Adafruit_CircuitPython_ProgressBar b/frozen/Adafruit_CircuitPython_ProgressBar index 6ba9d9d991ada..9fa23112cea1a 160000 --- a/frozen/Adafruit_CircuitPython_ProgressBar +++ b/frozen/Adafruit_CircuitPython_ProgressBar @@ -1 +1 @@ -Subproject commit 6ba9d9d991ada6c0cea6a32bd64595cfd37e06b2 +Subproject commit 9fa23112cea1a8db2b9b87cf2156cc4b039440a7 diff --git a/frozen/Adafruit_CircuitPython_RFM69 b/frozen/Adafruit_CircuitPython_RFM69 index 07be137bf5bda..62cb5cf6fbeb9 160000 --- a/frozen/Adafruit_CircuitPython_RFM69 +++ b/frozen/Adafruit_CircuitPython_RFM69 @@ -1 +1 @@ -Subproject commit 07be137bf5bda7a0469225c9cbb09b9a0aa08791 +Subproject commit 62cb5cf6fbeb9943bf7b2db4fc614f9b40830bf3 diff --git a/frozen/Adafruit_CircuitPython_RFM9x b/frozen/Adafruit_CircuitPython_RFM9x index 609aafb018b1c..5b8a9ae0ace6b 160000 --- a/frozen/Adafruit_CircuitPython_RFM9x +++ b/frozen/Adafruit_CircuitPython_RFM9x @@ -1 +1 @@ -Subproject commit 609aafb018b1cf5b7f60f2a7c961b827dce7468e +Subproject commit 5b8a9ae0ace6bd5f3a0860c95c3c389a09e4b59b diff --git a/frozen/Adafruit_CircuitPython_Register b/frozen/Adafruit_CircuitPython_Register index 96d0a4774f552..98faa16a0dd6c 160000 --- a/frozen/Adafruit_CircuitPython_Register +++ b/frozen/Adafruit_CircuitPython_Register @@ -1 +1 @@ -Subproject commit 96d0a4774f5525b926c131618e436b8e5c218a2f +Subproject commit 98faa16a0dd6c63a2726b291a53fde760a0fcabb diff --git a/frozen/Adafruit_CircuitPython_Requests b/frozen/Adafruit_CircuitPython_Requests index 5e646b244cf36..1479169b59d06 160000 --- a/frozen/Adafruit_CircuitPython_Requests +++ b/frozen/Adafruit_CircuitPython_Requests @@ -1 +1 @@ -Subproject commit 5e646b244cf36f879f15aaf77a270e4c7e6e8336 +Subproject commit 1479169b59d069b15384da64645f1e2d711a4679 diff --git a/frozen/Adafruit_CircuitPython_SD b/frozen/Adafruit_CircuitPython_SD index ee4d73293c8d0..dfbb9fd6ae297 160000 --- a/frozen/Adafruit_CircuitPython_SD +++ b/frozen/Adafruit_CircuitPython_SD @@ -1 +1 @@ -Subproject commit ee4d73293c8d059cd0c8bcf46758e62f5393cbee +Subproject commit dfbb9fd6ae297d6246554ea44e6c318970de98af diff --git a/frozen/Adafruit_CircuitPython_SHT4x b/frozen/Adafruit_CircuitPython_SHT4x index 26a0a407d43bd..9da248ca94426 160000 --- a/frozen/Adafruit_CircuitPython_SHT4x +++ b/frozen/Adafruit_CircuitPython_SHT4x @@ -1 +1 @@ -Subproject commit 26a0a407d43bd6208deffdf577e214d899855c0e +Subproject commit 9da248ca944264cbc4278c1732f901f3e1229231 diff --git a/frozen/Adafruit_CircuitPython_SSD1306 b/frozen/Adafruit_CircuitPython_SSD1306 index d75b4d593cd18..2d7fd1fd8f7bb 160000 --- a/frozen/Adafruit_CircuitPython_SSD1306 +++ b/frozen/Adafruit_CircuitPython_SSD1306 @@ -1 +1 @@ -Subproject commit d75b4d593cd184cbea5e237f5212cd9122d46263 +Subproject commit 2d7fd1fd8f7bb1b83d60926a28ab01ffaf67fa3b diff --git a/frozen/Adafruit_CircuitPython_SSD1680 b/frozen/Adafruit_CircuitPython_SSD1680 index d6aa01c4f8fa1..c22e6d097b44c 160000 --- a/frozen/Adafruit_CircuitPython_SSD1680 +++ b/frozen/Adafruit_CircuitPython_SSD1680 @@ -1 +1 @@ -Subproject commit d6aa01c4f8fa1004430bfcdd4db2219183425693 +Subproject commit c22e6d097b44c6e9612ff6b866ebec569796e6f5 diff --git a/frozen/Adafruit_CircuitPython_ST7789 b/frozen/Adafruit_CircuitPython_ST7789 index 0f7269267c0d1..1060cf1753df0 160000 --- a/frozen/Adafruit_CircuitPython_ST7789 +++ b/frozen/Adafruit_CircuitPython_ST7789 @@ -1 +1 @@ -Subproject commit 0f7269267c0d17ada34926333bbda4021e5d7cb3 +Subproject commit 1060cf1753df0024a95070132045357ddd9ce559 diff --git a/frozen/Adafruit_CircuitPython_SimpleIO b/frozen/Adafruit_CircuitPython_SimpleIO index d5278d246bcf6..054d2643744fe 160000 --- a/frozen/Adafruit_CircuitPython_SimpleIO +++ b/frozen/Adafruit_CircuitPython_SimpleIO @@ -1 +1 @@ -Subproject commit d5278d246bcf658ef5d44e7658c956fac29bd9e1 +Subproject commit 054d2643744fe78fed3c4c8b371ced26c8ab2ebe diff --git a/frozen/Adafruit_CircuitPython_SimpleMath b/frozen/Adafruit_CircuitPython_SimpleMath index 33f82828598a3..da27f05235713 160000 --- a/frozen/Adafruit_CircuitPython_SimpleMath +++ b/frozen/Adafruit_CircuitPython_SimpleMath @@ -1 +1 @@ -Subproject commit 33f82828598a3a10c73dfa50601fef4beac40be8 +Subproject commit da27f05235713bc8e79cf3a005d11bab920e13bb diff --git a/frozen/Adafruit_CircuitPython_Thermistor b/frozen/Adafruit_CircuitPython_Thermistor index 2b45967cc5283..f109e9d847b7f 160000 --- a/frozen/Adafruit_CircuitPython_Thermistor +++ b/frozen/Adafruit_CircuitPython_Thermistor @@ -1 +1 @@ -Subproject commit 2b45967cc5283e71b7826f6a158d8c8556dde287 +Subproject commit f109e9d847b7f8ba8545a2b6be8d0c3dd6a8a280 diff --git a/frozen/Adafruit_CircuitPython_Ticks b/frozen/Adafruit_CircuitPython_Ticks index 6e159f899b017..83695404ab734 160000 --- a/frozen/Adafruit_CircuitPython_Ticks +++ b/frozen/Adafruit_CircuitPython_Ticks @@ -1 +1 @@ -Subproject commit 6e159f899b017e920a6058a6b16735af8a6e852e +Subproject commit 83695404ab734eb60d32c9e96784b9bd90c58a1a diff --git a/frozen/Adafruit_CircuitPython_UC8151D b/frozen/Adafruit_CircuitPython_UC8151D index 4ebf9c2854376..b9bd61a0bbef1 160000 --- a/frozen/Adafruit_CircuitPython_UC8151D +++ b/frozen/Adafruit_CircuitPython_UC8151D @@ -1 +1 @@ -Subproject commit 4ebf9c2854376a06766a6ae4732a4537a766fb75 +Subproject commit b9bd61a0bbef1f4705abb831739d4888f1dcec95 diff --git a/frozen/Adafruit_CircuitPython_Wave b/frozen/Adafruit_CircuitPython_Wave index 6fba948b024ec..d302cd78d29ef 160000 --- a/frozen/Adafruit_CircuitPython_Wave +++ b/frozen/Adafruit_CircuitPython_Wave @@ -1 +1 @@ -Subproject commit 6fba948b024ec210b3cf1f1b068b3eebc82fe8d4 +Subproject commit d302cd78d29ef821faa1c18482a0b20fdca1d4ee diff --git a/frozen/Adafruit_CircuitPython_Wiznet5k b/frozen/Adafruit_CircuitPython_Wiznet5k index 736241c7a22f8..ea7b97c5b1faa 160000 --- a/frozen/Adafruit_CircuitPython_Wiznet5k +++ b/frozen/Adafruit_CircuitPython_Wiznet5k @@ -1 +1 @@ -Subproject commit 736241c7a22f86dcf8ff73a77c4536cedfdc4cdd +Subproject commit ea7b97c5b1faa2db4b54d7f6d9a61b545b49f383 diff --git a/frozen/Adafruit_CircuitPython_asyncio b/frozen/Adafruit_CircuitPython_asyncio index 24818f817f511..e69ac03dccfd8 160000 --- a/frozen/Adafruit_CircuitPython_asyncio +++ b/frozen/Adafruit_CircuitPython_asyncio @@ -1 +1 @@ -Subproject commit 24818f817f5118f59aa696a04776049c179c0f4f +Subproject commit e69ac03dccfd87ccaf3655dc751331ff922f525f diff --git a/frozen/Adafruit_CircuitPython_framebuf b/frozen/Adafruit_CircuitPython_framebuf index 0fedf2f308ed6..8a94ddb7257be 160000 --- a/frozen/Adafruit_CircuitPython_framebuf +++ b/frozen/Adafruit_CircuitPython_framebuf @@ -1 +1 @@ -Subproject commit 0fedf2f308ed6b3e8261661e4810e613f33d7171 +Subproject commit 8a94ddb7257bebfb856fe476d9b851dfa63ce443 diff --git a/frozen/Adafruit_CircuitPython_seesaw b/frozen/Adafruit_CircuitPython_seesaw index 94c541f45313d..d01642926b80a 160000 --- a/frozen/Adafruit_CircuitPython_seesaw +++ b/frozen/Adafruit_CircuitPython_seesaw @@ -1 +1 @@ -Subproject commit 94c541f45313dc7eb98a4cd1a6c3af39f001cc49 +Subproject commit d01642926b80a46762f1dd76dcdd5aefb0d95bc8 From 3f0b1b8c9d9fc41b29ef5fd36cb8283b75c48198 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 14 Nov 2025 12:20:00 -0800 Subject: [PATCH 213/517] Fix mipidsi cache flush The old way didn't flush all caches and left artifacts. Fixes #10708 --- ports/espressif/common-hal/mipidsi/Display.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/espressif/common-hal/mipidsi/Display.c b/ports/espressif/common-hal/mipidsi/Display.c index dac16162e91ff..cc45db72375a2 100644 --- a/ports/espressif/common-hal/mipidsi/Display.c +++ b/ports/espressif/common-hal/mipidsi/Display.c @@ -213,8 +213,9 @@ bool common_hal_mipidsi_display_deinited(mipidsi_display_obj_t *self) { } void common_hal_mipidsi_display_refresh(mipidsi_display_obj_t *self) { - // Write back the cache to ensure framebuffer changes are visible - Cache_WriteBack_Addr((uint32_t)(self->framebuffer), self->framebuffer_size); + // Drawing the framebuffer we got from the IDF will flush the cache(s) so + // DMA can see our changes. It won't cause an extra copy. + esp_lcd_panel_draw_bitmap(self->dpi_panel_handle, 0, 0, self->width, self->height, self->framebuffer); // The DPI panel will automatically refresh from the framebuffer // No explicit refresh call is needed as the DSI hardware continuously From 82ff9f1c1e252917dafc0cb5a613fb489efcdd2b Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 19 Nov 2025 15:23:54 -0800 Subject: [PATCH 214/517] Enable OV3660 camera support on XIAO S3 Fixes #10718 --- ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig index 919f4d9f345ff..2d98e8225429b 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig @@ -5,15 +5,13 @@ # Component config # # -# LWIP # -# end of LWIP # Camera configuration # -CONFIG_OV2640_SUPPORT=y # CONFIG_OV7725_SUPPORT is not set -# CONFIG_OV3660_SUPPORT is not set +CONFIG_OV2640_SUPPORT=y # end of Camera configuration + # end of Component config # end of Espressif IoT Development Framework Configuration From b35fbdc624c19ae8b4b034d59e5b175b705b4594 Mon Sep 17 00:00:00 2001 From: Hans Maerki Date: Thu, 20 Nov 2025 21:24:47 +0000 Subject: [PATCH 215/517] Allow microcontroller.nvm to be bigger than 8k Signed-off-by: Hans Maerki --- shared-bindings/nvm/ByteArray.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/nvm/ByteArray.c b/shared-bindings/nvm/ByteArray.c index be01c1f706414..b278300626737 100644 --- a/shared-bindings/nvm/ByteArray.c +++ b/shared-bindings/nvm/ByteArray.c @@ -35,7 +35,7 @@ //| static mp_obj_t nvm_bytearray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { nvm_bytearray_obj_t *self = MP_OBJ_TO_PTR(self_in); - uint16_t len = common_hal_nvm_bytearray_get_length(self); + uint32_t len = common_hal_nvm_bytearray_get_length(self); switch (op) { case MP_UNARY_OP_BOOL: return mp_obj_new_bool(len != 0); From 70a3af8cf86b31cd6a20e318cd7af222b558944c Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 17 Nov 2025 12:10:37 -0800 Subject: [PATCH 216/517] Update to Zephyr v4.3.0 --- .../actions/deps/ports/zephyr-cp/action.yml | 6 + ports/zephyr-cp/README.md | 4 +- .../boards/nrf7002dk_nrf5340_cpuapp.conf | 2 +- .../zephyr-cp/cptools/build_circuitpython.py | 21 +- ports/zephyr-cp/cptools/compat2driver.py | 742 ++++++++++++++++-- ports/zephyr-cp/cptools/cpbuild.py | 89 ++- ports/zephyr-cp/cptools/gen_compat2driver.py | 2 +- ports/zephyr-cp/cptools/zephyr2cp.py | 93 ++- ports/zephyr-cp/prj.conf | 2 +- ports/zephyr-cp/zephyr-config/west.yml | 2 +- 10 files changed, 838 insertions(+), 125 deletions(-) diff --git a/.github/actions/deps/ports/zephyr-cp/action.yml b/.github/actions/deps/ports/zephyr-cp/action.yml index cfa1177598e4d..75ff232562c4d 100644 --- a/.github/actions/deps/ports/zephyr-cp/action.yml +++ b/.github/actions/deps/ports/zephyr-cp/action.yml @@ -3,6 +3,12 @@ name: Fetch Zephyr port deps runs: using: composite steps: + - name: Get libusb + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y libusb-1.0-0-dev libudev-dev + shell: bash - name: Setup Zephyr project uses: zephyrproject-rtos/action-zephyr-setup@v1 with: diff --git a/ports/zephyr-cp/README.md b/ports/zephyr-cp/README.md index 4d08936cad66c..1fba1d7d3f7f4 100644 --- a/ports/zephyr-cp/README.md +++ b/ports/zephyr-cp/README.md @@ -15,11 +15,11 @@ pip install west west init -l zephyr-config west update west zephyr-export -pip install -r lib/zephyr/scripts/requirements.txt +pip install -r zephyr/scripts/requirements.txt west sdk install ``` -Now to build from the top level: +Now to build from `ports/zephyr-cp`: ```sh make BOARD=nordic_nrf7002dk diff --git a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf index 6f01624bb3a4a..c61851fad2d24 100644 --- a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf @@ -1,7 +1,7 @@ CONFIG_NETWORKING=y CONFIG_WIFI=y -CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y CONFIG_MBEDTLS_USE_PSA_CRYPTO=n CONFIG_BT=y diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 61e103e9fe109..fd1ca7dc43ffc 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -1,16 +1,16 @@ import asyncio -import colorlog -import sys import logging import os import pathlib -import tomllib -import tomlkit -import yaml import pickle +import sys -import cpbuild import board_tools +import colorlog +import cpbuild +import tomlkit +import tomllib +import yaml logger = logging.getLogger(__name__) @@ -219,13 +219,13 @@ async def build_circuitpython(): circuitpython_flags.append(f"-DCIRCUITPY_ENABLE_MPY_NATIVE={1 if enable_mpy_native else 0}") circuitpython_flags.append(f"-DCIRCUITPY_FULL_BUILD={1 if full_build else 0}") circuitpython_flags.append(f"-DCIRCUITPY_USB_HOST={1 if usb_host else 0}") - circuitpython_flags.append(f'-DCIRCUITPY_BOARD_ID=\\"{board}\\"') + circuitpython_flags.append(f"-DCIRCUITPY_BOARD_ID='\"{board}\"'") circuitpython_flags.append(f"-DCIRCUITPY_TUSB_MEM_ALIGN={tusb_mem_align}") circuitpython_flags.append(f"-DCIRCUITPY_TRANSLATE_OBJECT={1 if lto else 0}") circuitpython_flags.append("-DINTERNAL_FLASH_FILESYSTEM") circuitpython_flags.append("-DLONGINT_IMPL_MPZ") circuitpython_flags.append("-DCIRCUITPY_SSL_MBEDTLS") - circuitpython_flags.append('-DFFCONF_H=\\"lib/oofatfs/ffconf.h\\"') + circuitpython_flags.append("-DFFCONF_H='\"lib/oofatfs/ffconf.h\"'") circuitpython_flags.extend(("-I", srcdir)) circuitpython_flags.extend(("-I", srcdir / "lib/tinyusb/src")) circuitpython_flags.extend(("-I", srcdir / "supervisor/shared/usb")) @@ -448,10 +448,7 @@ async def build_circuitpython(): if "ssl" in enabled_modules: # TODO: Figure out how to get these paths from zephyr - circuitpython_flags.append('-DMBEDTLS_CONFIG_FILE=\\"config-tls-generic.h\\"') - circuitpython_flags.extend( - ("-isystem", portdir / "modules" / "crypto" / "tinycrypt" / "lib" / "include") - ) + circuitpython_flags.append("-DMBEDTLS_CONFIG_FILE='\"config-mbedtls.h\"'") circuitpython_flags.extend( ("-isystem", portdir / "modules" / "crypto" / "mbedtls" / "include") ) diff --git a/ports/zephyr-cp/cptools/compat2driver.py b/ports/zephyr-cp/cptools/compat2driver.py index 2b818c4c23762..dff09787b2881 100644 --- a/ports/zephyr-cp/cptools/compat2driver.py +++ b/ports/zephyr-cp/cptools/compat2driver.py @@ -1,18 +1,29 @@ # This file was generated by gen_compat2driver.py COMPAT_TO_DRIVER = { # adc + "adi_ad4050_adc": "adc", + "adi_ad4052_adc": "adc", "adi_ad4114_adc": "adc", + "adi_ad4130_adc": "adc", + "adi_ad4170_adc": "adc", + "adi_ad4190_adc": "adc", + "adi_ad4195_adc": "adc", "adi_ad559x_adc": "adc", + "adi_ad7124_adc": "adc", "adi_max32_adc": "adc", "ambiq_adc": "adc", "atmel_sam0_adc": "adc", "atmel_sam_adc": "adc", "atmel_sam_afec": "adc", + "ene_kb106x_adc": "adc", "ene_kb1200_adc": "adc", "espressif_esp32_adc": "adc", "gd_gd32_adc": "adc", + "infineon_autanalog_sar_adc": "adc", "infineon_cat1_adc": "adc", + "infineon_hppass_sar_adc": "adc", "infineon_xmc4xxx_adc": "adc", + "ite_it51xxx_adc": "adc", "ite_it8xxx2_adc": "adc", "lltc_ltc2451": "adc", "maxim_max11102": "adc", @@ -26,6 +37,7 @@ "maxim_max11117": "adc", "maxim_max11253": "adc", "maxim_max11254": "adc", + "microchip_mcp356xr": "adc", "microchip_xec_adc": "adc", "nordic_nrf_adc": "adc", "nordic_nrf_saadc": "adc", @@ -37,13 +49,19 @@ "nxp_lpc_lpadc": "adc", "nxp_mcux_12b1msps_sar": "adc", "nxp_s32_adc_sar": "adc", + "nxp_sar_adc": "adc", "nxp_vf610_adc": "adc", "raspberrypi_pico_adc": "adc", + "realtek_rts5912_adc": "adc", "renesas_ra_adc": "adc", + "renesas_rx_adc": "adc", + "renesas_rz_adc": "adc", + "renesas_rz_adc_c": "adc", "renesas_smartbond_adc": "adc", "renesas_smartbond_sdadc": "adc", "silabs_gecko_adc": "adc", - "silabs_gecko_iadc": "adc", + "silabs_iadc": "adc", + "silabs_siwx91x_adc": "adc", "st_stm32_adc": "adc", "st_stm32wb0_adc": "adc", "telink_b91_adc": "adc", @@ -55,10 +73,15 @@ "ti_ads1114": "adc", "ti_ads1115": "adc", "ti_ads1119": "adc", + "ti_ads114s06": "adc", "ti_ads114s08": "adc", + "ti_ads124s06": "adc", + "ti_ads124s08": "adc", "ti_ads131m02": "adc", "ti_ads7052": "adc", + "ti_am335x_adc": "adc", "ti_cc13xx_cc26xx_adc": "adc", + "ti_cc23x0_adc": "adc", "ti_cc32xx_adc": "adc", "ti_lmp90077": "adc", "ti_lmp90078": "adc", @@ -72,26 +95,42 @@ "ti_tla2022": "adc", "ti_tla2024": "adc", "vnd_adc": "adc", + "wch_adc": "adc", "zephyr_adc_emul": "adc", # # audio + "ambiq_pdm": "audio", + "cirrus_cs43l22": "audio", + "dlg_da7212": "audio", + "maxim_max98091": "audio", "nxp_dmic": "audio", + "nxp_micfil": "audio", "st_mpxxdtyy": "audio", + "ti_pcm1681": "audio", "ti_tas6422dac": "audio", + "ti_tlv320aic3110": "audio", "ti_tlv320dac": "audio", "wolfson_wm8904": "audio", + "wolfson_wm8962": "audio", + # + # audio/mic_privacy/intel + "intel_adsp_mic_privacy": "audio/mic_privacy/intel", # # auxdisplay + "gpio_7_segment": "auxdisplay", "hit_hd44780": "auxdisplay", "jhd_jhd1313": "auxdisplay", "noritake_itron": "auxdisplay", "ptc_pt6314": "auxdisplay", "sparkfun_serlcd": "auxdisplay", + "titanmec_tm1637": "auxdisplay", # # bbram "ite_it8xxx2_bbram": "bbram", "microchip_xec_bbram": "bbram", + "motorola_mc146818_bbram": "bbram", "nuvoton_npcx_bbram": "bbram", + "realtek_rts5912_bbram": "bbram", "st_stm32_bbram": "bbram", "zephyr_bbram_emul": "bbram", # @@ -106,6 +145,7 @@ "renesas_bt_hci_da1453x": "bluetooth/hci", "renesas_bt_hci_da1469x": "bluetooth/hci", "silabs_bt_hci_efr32": "bluetooth/hci", + "silabs_siwx91x_bt_hci": "bluetooth/hci", "st_hci_spi_v1": "bluetooth/hci", "st_hci_spi_v2": "bluetooth/hci", "st_hci_stm32wb0": "bluetooth/hci", @@ -117,7 +157,11 @@ "zephyr_bt_hci_uart": "bluetooth/hci", "zephyr_bt_hci_userchan": "bluetooth/hci", # + # cache + "bflb_l1c": "cache", + # # can + "adi_max32_can": "can", "atmel_sam0_can": "can", "atmel_sam_can": "can", "espressif_esp32_twai": "can", @@ -132,6 +176,7 @@ "nxp_s32_canxl": "can", "renesas_ra_canfd": "can", "renesas_rcar_can": "can", + "renesas_rz_canfd": "can", "st_stm32_bxcan": "can", "st_stm32_fdcan": "can", "st_stm32h7_fdcan": "can", @@ -145,9 +190,13 @@ # # charger "maxim_max20335_charger": "charger", + "nxp_pca9422_charger": "charger", + "nxp_pf1550_charger": "charger", "sbs_sbs_charger": "charger", "ti_bq24190": "charger", "ti_bq25180": "charger", + "ti_bq25713": "charger", + "x_powers_axp2101_charger": "charger", # # clock_control "adi_max32_gcr": "clock_control", @@ -156,10 +205,20 @@ "arm_scmi_clock": "clock_control", "aspeed_ast10x0_clock": "clock_control", "atmel_sam_pmc": "clock_control", - "espressif_esp32_rtc": "clock_control", + "bflb_bl60x_clock_controller": "clock_control", + "bflb_bl61x_clock_controller": "clock_control", + "bflb_bl70x_clock_controller": "clock_control", + "espressif_esp32_clock": "clock_control", "fixed_clock": "clock_control", "gd_gd32_cctl": "clock_control", + "infineon_fixed_clock": "clock_control", + "infineon_fixed_factor_clock": "clock_control", + "infineon_peri_div": "clock_control", "intel_agilex5_clock": "clock_control", + "ite_it51xxx_ecpm": "clock_control", + "microchip_sam_d5x_e5x_clock": "clock_control", + "microchip_sam_pmc": "clock_control", + "microchip_sama7g5_sckc": "clock_control", "microchip_xec_pcr": "clock_control", "nordic_nrf54h_hfxo": "clock_control", "nordic_nrf_auxpll": "clock_control", @@ -167,7 +226,9 @@ "nordic_nrf_fll16m": "clock_control", "nordic_nrf_hsfll_global": "clock_control", "nordic_nrf_hsfll_local": "clock_control", + "nordic_nrf_iron_hsfll_local": "clock_control", "nordic_nrf_lfclk": "clock_control", + "nordic_nrfs_audiopll": "clock_control", "nuvoton_npcm_pcc": "clock_control", "nuvoton_npcx_pcc": "clock_control", "nuvoton_numaker_scc": "clock_control", @@ -179,6 +240,7 @@ "nxp_kinetis_sim": "clock_control", "nxp_lpc11u6x_syscon": "clock_control", "nxp_lpc_syscon": "clock_control", + "nxp_mc_cgm": "clock_control", "nxp_s32_clock": "clock_control", "nxp_scg_k4": "clock_control", "openisa_rv32m1_pcc": "clock_control", @@ -187,10 +249,20 @@ "realtek_rts5912_sccon": "clock_control", "renesas_r8a7795_cpg_mssr": "clock_control", "renesas_r8a779f0_cpg_mssr": "clock_control", + "renesas_ra_cgc_subclk": "clock_control", + "renesas_rx_cgc_pclk": "clock_control", + "renesas_rx_cgc_pll": "clock_control", + "renesas_rx_cgc_root_clock": "clock_control", + "renesas_rz_cgc": "clock_control", + "renesas_rz_cpg": "clock_control", + "renesas_rza2m_cpg": "clock_control", + "sifli_sf32lb_hxt48": "clock_control", + "sifli_sf32lb_rcc_clk": "clock_control", "silabs_series_clock": "clock_control", "silabs_si32_ahb": "clock_control", "silabs_si32_apb": "clock_control", "silabs_si32_pll": "clock_control", + "silabs_siwx91x_clock": "clock_control", "smartbond_clock": "clock_control", "st_stm32_clock_mco": "clock_control", "st_stm32_clock_mux": "clock_control", @@ -198,8 +270,15 @@ "wch_rcc": "clock_control", # # comparator + "ite_it51xxx_vcmp": "comparator", "nordic_nrf_comp": "comparator", "nordic_nrf_lpcomp": "comparator", + "nxp_cmp": "comparator", + "renesas_ra_acmphs": "comparator", + "renesas_ra_lvd": "comparator", + "renesas_rx_lvd": "comparator", + "silabs_acmp": "comparator", + "st_stm32_comp": "comparator", "zephyr_fake_comp": "comparator", # # coredump @@ -208,21 +287,27 @@ # counter "adi_max32_counter": "counter", "adi_max32_rtc_counter": "counter", + "adi_max32_wut": "counter", "ambiq_counter": "counter", "andestech_atcpit100": "counter", "arm_cmsdk_dtimer": "counter", "arm_cmsdk_timer": "counter", "atmel_sam0_tc32": "counter", "atmel_sam_tc": "counter", + "espressif_esp32_counter": "counter", "espressif_esp32_rtc_timer": "counter", - "espressif_esp32_timer": "counter", "gd_gd32_timer": "counter", "infineon_cat1_counter": "counter", + "infineon_tcpwm_counter": "counter", + "ite_it51xxx_counter": "counter", + "ite_it8xxx2_counter": "counter", "maxim_ds3231": "counter", "microchip_mcp7940n": "counter", "microchip_xec_timer": "counter", + "neorv32_gptmr": "counter", "nordic_nrf_rtc": "counter", "nordic_nrf_timer": "counter", + "nxp_ftm": "counter", "nxp_imx_epit": "counter", "nxp_imx_gpt": "counter", "nxp_imx_qtmr": "counter", @@ -231,37 +316,58 @@ "nxp_lpc_ctimer": "counter", "nxp_lpc_rtc": "counter", "nxp_lpc_rtc_highres": "counter", + "nxp_lpit": "counter", "nxp_mrt": "counter", "nxp_pit": "counter", "nxp_rtc": "counter", "nxp_s32_sys_timer": "counter", + "nxp_stm": "counter", "nxp_tpm_timer": "counter", "raspberrypi_pico_timer": "counter", + "realtek_rts5912_slwtimer": "counter", + "realtek_rts5912_timer": "counter", "renesas_ra_agt_counter": "counter", + "renesas_rz_cmtw_counter": "counter", + "renesas_rz_gtm_counter": "counter", "renesas_smartbond_timer": "counter", "silabs_gecko_rtcc": "counter", - "silabs_gecko_stimer": "counter", "snps_dw_timers": "counter", "st_stm32_counter": "counter", + "ti_cc23x0_lgpt": "counter", + "ti_cc23x0_rtc": "counter", + "ti_mspm0_timer_counter": "counter", "xlnx_xps_timer_1_00_a": "counter", "zephyr_native_posix_counter": "counter", + "zephyr_native_sim_counter": "counter", + # + # crc + "renesas_ra_crc": "crc", # # crypto "atmel_ataes132a": "crypto", + "espressif_esp32_aes": "crypto", + "espressif_esp32_sha": "crypto", "intel_adsp_sha": "crypto", + "ite_it51xxx_sha": "crypto", "ite_it8xxx2_sha": "crypto", "ite_it8xxx2_sha_v2": "crypto", "microchip_xec_symcr": "crypto", "nordic_nrf_ecb": "crypto", "nuvoton_npcx_sha": "crypto", "nxp_mcux_dcp": "crypto", + "realtek_rts5912_sha": "crypto", "renesas_smartbond_crypto": "crypto", "silabs_si32_aes": "crypto", "st_stm32_aes": "crypto", "st_stm32_cryp": "crypto", + "st_stm32_hash": "crypto", + "ti_cc23x0_aes": "crypto", # # dac "adi_ad559x_dac": "dac", + "adi_ad5601": "dac", + "adi_ad5611": "dac", + "adi_ad5621": "dac", "adi_ad5628": "dac", "adi_ad5648": "dac", "adi_ad5668": "dac", @@ -279,15 +385,20 @@ "adi_max22017_dac": "dac", "atmel_sam0_dac": "dac", "atmel_sam_dac": "dac", + "atmel_samd5x_dac": "dac", "espressif_esp32_dac": "dac", "gd_gd32_dac": "dac", "microchip_mcp4725": "dac", "microchip_mcp4728": "dac", + "nxp_dac12": "dac", "nxp_gau_dac": "dac", "nxp_kinetis_dac": "dac", "nxp_kinetis_dac32": "dac", "nxp_lpdac": "dac", + "renesas_ra_dac": "dac", + "silabs_vdac": "dac", "st_stm32_dac": "dac", + "ti_dac161s997": "dac", "ti_dacx0501": "dac", "vnd_dac": "dac", # @@ -303,9 +414,16 @@ # dai/intel/ssp "intel_ssp_dai": "dai/intel/ssp", # + # dai/nxp/micfil + "nxp_dai_micfil": "dai/nxp/micfil", + # # dai/nxp/sai "nxp_dai_sai": "dai/nxp/sai", # + # debug + "nordic_coresight_nrf": "debug", + "silabs_pti": "debug", + # # disk "st_stm32_sdmmc": "disk", "zephyr_flash_disk": "disk", @@ -317,36 +435,45 @@ "nvme_controller": "disk/nvme", # # display + "chipone_co5300": "display", "frida_nt35510": "display", "galaxycore_gc9x01x": "display", + "himax_hx8379c": "display", "himax_hx8394": "display", "ilitek_ili9806e_dsi": "display", "intel_multiboot_framebuffer": "display", "istech_ist3931": "display", + "jdi_lpm013m126": "display", "led_strip_matrix": "display", "maxim_max7219": "display", "nxp_dcnano_lcdif": "display", "nxp_imx_elcdif": "display", + "nxp_imx_lcdifv3": "display", "orisetech_otm8009a": "display", "raydium_rm67162": "display", "raydium_rm68200": "display", "renesas_ra_glcdc": "display", "renesas_smartbond_display": "display", "sharp_ls0xx": "display", + "sitronix_st75256": "display", + "sitronix_st7701": "display", "sitronix_st7735r": "display", "sitronix_st7789v": "display", "sitronix_st7796s": "display", "solomon_ssd1322": "display", "st_stm32_ltdc": "display", + "waveshare_7inch_dsi_lcd_c": "display", "zephyr_dummy_dc": "display", + "zephyr_hub12": "display", "zephyr_sdl_dc": "display", # # dma "adi_max32_dma": "dma", "altr_msgdma": "dma", - "andestech_atcdmac300": "dma", + "andestech_atcdmacx00": "dma", "atmel_sam0_dmac": "dma", "atmel_sam_xdmac": "dma", + "bflb_dma": "dma", "brcm_iproc_pax_dma_v1": "dma", "brcm_iproc_pax_dma_v2": "dma", "espressif_esp32_gdma": "dma", @@ -362,6 +489,7 @@ "intel_lpss": "dma", "intel_sedi_dma": "dma", "microchip_xec_dmac": "dma", + "nuvoton_npcx_gdma": "dma", "nxp_lpc_dma": "dma", "nxp_mcux_edma": "dma", "nxp_pxp": "dma", @@ -369,9 +497,15 @@ "nxp_smartdma": "dma", "nxp_sof_host_dma": "dma", "raspberrypi_pico_dma": "dma", + "renesas_ra_dma": "dma", + "renesas_rz_dmac": "dma", + "renesas_rz_dmac_b": "dma", "renesas_smartbond_dma": "dma", + "sifli_sf32lb_dmac": "dma", + "silabs_gpdma": "dma", "silabs_ldma": "dma", "silabs_si32_dma": "dma", + "silabs_siwx91x_dma": "dma", "snps_designware_dma": "dma", "snps_designware_dma_axi": "dma", "st_stm32_bdma": "dma", @@ -380,6 +514,8 @@ "st_stm32_dma_v2bis": "dma", "st_stm32_dmamux": "dma", "st_stm32u5_dma": "dma", + "ti_cc23x0_dma": "dma", + "wch_wch_dma": "dma", "xlnx_axi_dma_1_00_a": "dma", "xlnx_eth_dma": "dma", "zephyr_dma_emul": "dma", @@ -389,40 +525,50 @@ # # edac "intel_ibecc": "edac", + "xlnx_zynqmp_ddrc_2_40a": "edac", # # eeprom "atmel_at24": "eeprom", "fujitsu_mb85rcxx": "eeprom", "fujitsu_mb85rsxx": "eeprom", + "infineon_fm25xxx": "eeprom", "microchip_xec_eeprom": "eeprom", "nxp_lpc11u6x_eeprom": "eeprom", "st_stm32_eeprom": "eeprom", - "ti_tmp116_eeprom": "eeprom", + "ti_tmp11x_eeprom": "eeprom", "zephyr_emu_eeprom": "eeprom", "zephyr_fake_eeprom": "eeprom", "zephyr_sim_eeprom": "eeprom", # # entropy "adi_max32_trng": "entropy", + "adi_maxq10xx_trng": "entropy", + "ambiq_puf_trng": "entropy", "atmel_sam_trng": "entropy", + "brcm_iproc_rng200": "entropy", "espressif_esp32_trng": "entropy", "litex_prbs": "entropy", "neorv32_trng": "entropy", + "nordic_nrf_cracen_ctrdrbg": "entropy", "nordic_nrf_rng": "entropy", "nuvoton_npcx_drbg": "entropy", + "nxp_ele_trng": "entropy", "nxp_imx_caam": "entropy", "nxp_kinetis_rnga": "entropy", "nxp_kinetis_trng": "entropy", "nxp_lpc_rng": "entropy", "openisa_rv32m1_trng": "entropy", "renesas_smartbond_trng": "entropy", + "sensry_sy1xx_trng": "entropy", "silabs_gecko_semailbox": "entropy", "silabs_gecko_trng": "entropy", - "st_stm32_rng": "entropy", + "silabs_siwx91x_rng": "entropy", "telink_b91_trng": "entropy", "ti_cc13xx_cc26xx_trng": "entropy", + "virtio_device4": "entropy", "zephyr_bt_hci_entropy": "entropy", "zephyr_native_posix_rng": "entropy", + "zephyr_native_sim_rng": "entropy", "zephyr_psa_crypto_rng": "entropy", # # espi @@ -435,6 +581,7 @@ "nuvoton_npcx_espi": "espi", "nuvoton_npcx_espi_taf": "espi", "nuvoton_npcx_host_sub": "espi", + "realtek_rts5912_espi": "espi", "zephyr_espi_emul_controller": "espi", # # ethernet @@ -448,26 +595,36 @@ "litex_liteeth": "ethernet", "microchip_enc28j60": "ethernet", "microchip_enc424j600": "ethernet", - "microchip_ksz8794": "ethernet", - "microchip_ksz8863": "ethernet", "microchip_lan865x": "ethernet", "microchip_lan9250": "ethernet", "nuvoton_numaker_ethernet": "ethernet", - "nxp_imx_netc_psi": "ethernet", + "nxp_enet": "ethernet", + "nxp_enet1g": "ethernet", + "nxp_enet_mac": "ethernet", "nxp_s32_gmac": "ethernet", "nxp_s32_netc_psi": "ethernet", "nxp_s32_netc_vsi": "ethernet", "renesas_ra_ethernet": "ethernet", + "sensry_sy1xx_mac": "ethernet", "siemens_ivshmem_eth": "ethernet", "silabs_gecko_ethernet": "ethernet", "smsc_lan91c111": "ethernet", + "smsc_lan91c111_mdio": "ethernet", "smsc_lan9220": "ethernet", "snps_designware_ethernet": "ethernet", "snps_ethernet_cyclonev": "ethernet", "st_stm32_ethernet": "ethernet", "ti_stellaris_ethernet": "ethernet", + "virtio_net": "ethernet", "vnd_ethernet": "ethernet", "wiznet_w5500": "ethernet", + "xlnx_axi_ethernet_1_00_a": "ethernet", + # + # ethernet/dsa + "microchip_ksz8463": "ethernet/dsa", + "microchip_ksz8794": "ethernet/dsa", + "microchip_ksz8863": "ethernet/dsa", + "nxp_netc_switch": "ethernet/dsa", # # ethernet/dwc_xgmac "snps_dwcxgmac": "ethernet/dwc_xgmac", @@ -476,11 +633,13 @@ "nxp_enet_qos": "ethernet/eth_nxp_enet_qos", "nxp_enet_qos_mac": "ethernet/eth_nxp_enet_qos", # - # ethernet/nxp_enet - "nxp_enet": "ethernet/nxp_enet", - "nxp_enet1g": "ethernet/nxp_enet", - "nxp_enet_mac": "ethernet/nxp_enet", - "nxp_kinetis_ethernet": "ethernet/nxp_enet", + # ethernet/intel + "intel_eth_plat": "ethernet/intel", + "intel_igc_mac": "ethernet/intel", + # + # ethernet/nxp_imx_netc + "nxp_imx_netc_blk_ctrl": "ethernet/nxp_imx_netc", + "nxp_imx_netc_psi": "ethernet/nxp_imx_netc", # # ethernet/phy "adi_adin1100_phy": "ethernet/phy", @@ -488,21 +647,29 @@ "davicom_dm8806_phy": "ethernet/phy", "ethernet_phy": "ethernet/phy", "microchip_ksz8081": "ethernet/phy", + "microchip_ksz9131": "ethernet/phy", "microchip_t1s_phy": "ethernet/phy", "microchip_vsc8541": "ethernet/phy", "nxp_tja1103": "ethernet/phy", + "nxp_tja11xx": "ethernet/phy", "qca_ar8031": "ethernet/phy", "realtek_rtl8211f": "ethernet/phy", "ti_dp83825": "ethernet/phy", + "ti_dp83867": "ethernet/phy", # # firmware/scmi "arm_scmi_shmem": "firmware/scmi", # + # firmware/tisci + "ti_k2g_sci": "firmware/tisci", + # # flash "adi_max32_flash_controller": "flash", - "altr_nios2_qspi_nor": "flash", + "adi_max32_spixf_nor": "flash", "ambiq_flash_controller": "flash", "andestech_qspi_nor": "flash", + "andestech_qspi_nor_xip": "flash", + "atmel_at25xv021a": "flash", "atmel_at45": "flash", "atmel_sam0_nvmctrl": "flash", "atmel_sam_flash_controller": "flash", @@ -513,14 +680,19 @@ "infineon_cat1_flash_controller": "flash", "infineon_cat1_qspi_flash": "flash", "infineon_xmc4xxx_flash_controller": "flash", + "ite_it51xxx_manual_flash_1k": "flash", "ite_it8xxx2_flash_controller": "flash", + "jedec_mspi_nor": "flash", "jedec_spi_nor": "flash", + "microchip_nvmctrl_g1_flash": "flash", "mspi_atxp032": "flash", + "mspi_is25xx0xx": "flash", "nordic_mram": "flash", "nordic_nrf51_flash_controller": "flash", "nordic_nrf52_flash_controller": "flash", "nordic_nrf53_flash_controller": "flash", "nordic_nrf91_flash_controller": "flash", + "nordic_nrf_mramc": "flash", "nordic_qspi_nor": "flash", "nordic_rram_controller": "flash", "nuvoton_npcx_fiu_nor": "flash", @@ -538,15 +710,25 @@ "nxp_kinetis_ftfe": "flash", "nxp_kinetis_ftfl": "flash", "nxp_msf1": "flash", + "nxp_s32_qspi_hyperflash": "flash", "nxp_s32_qspi_nor": "flash", + "nxp_xspi_nor": "flash", "openisa_rv32m1_ftfe": "flash", "raspberrypi_pico_flash_controller": "flash", + "realtek_rts5912_flash_controller": "flash", "renesas_ra_flash_hp_controller": "flash", + "renesas_ra_mram_controller": "flash", "renesas_ra_ospi_b_nor": "flash", "renesas_ra_qspi_nor": "flash", + "renesas_rx_flash": "flash", + "renesas_rz_qspi_spibsc": "flash", + "renesas_rz_qspi_xspi": "flash", "renesas_smartbond_flash_controller": "flash", + "sifli_sf32lb_mpi_qspi_nor": "flash", "silabs_gecko_flash_controller": "flash", + "silabs_series2_flash_controller": "flash", "silabs_si32_flash_controller": "flash", + "silabs_siwx91x_flash_controller": "flash", "st_stm32_flash_controller": "flash", "st_stm32_ospi_nor": "flash", "st_stm32_qspi_nor": "flash", @@ -556,6 +738,7 @@ "st_stm32wba_flash_controller": "flash", "telink_b91_flash_controller": "flash", "ti_cc13xx_cc26xx_flash_controller": "flash", + "ti_cc23x0_flash_controller": "flash", "zephyr_mspi_emul_flash": "flash", "zephyr_sim_flash": "flash", # @@ -568,12 +751,24 @@ "renesas_slg47115": "fpga", "xlnx_fpga": "fpga", # + # fuel_gauge/axp2101 + "x_powers_axp2101_fuel_gauge": "fuel_gauge/axp2101", + # # fuel_gauge/bq27z746 "ti_bq27z746": "fuel_gauge/bq27z746", # + # fuel_gauge/bq40z50 + "ti_bq40z50": "fuel_gauge/bq40z50", + # # fuel_gauge/composite "zephyr_fuel_gauge_composite": "fuel_gauge/composite", # + # fuel_gauge/lc709203f + "onnn_lc709203f": "fuel_gauge/lc709203f", + # + # fuel_gauge/ltc2959 + "adi_ltc2959": "fuel_gauge/ltc2959", + # # fuel_gauge/max17048 "maxim_max17048": "fuel_gauge/max17048", # @@ -581,12 +776,16 @@ "sbs_sbs_gauge": "fuel_gauge/sbs_gauge", "sbs_sbs_gauge_new_api": "fuel_gauge/sbs_gauge", # + # fuel_gauge/sy24561 + "silergy_sy24561": "fuel_gauge/sy24561", + # # gnss "gnss_nmea_generic": "gnss", "luatos_air530z": "gnss", "quectel_lc26g": "gnss", "quectel_lc76g": "gnss", "quectel_lc86g": "gnss", + "u_blox_f9p": "gnss", "u_blox_m8": "gnss", "zephyr_gnss_emul": "gnss", # @@ -594,26 +793,35 @@ "adi_ad559x_gpio": "gpio", "adi_adp5585_gpio": "gpio", "adi_max14906_gpio": "gpio", + "adi_max14915_gpio": "gpio", "adi_max14916_gpio": "gpio", + "adi_max14917_gpio": "gpio", "adi_max22017_gpio": "gpio", "adi_max22190_gpio": "gpio", + "adi_max22199_gpio": "gpio", "adi_max32_gpio": "gpio", + "aesc_gpio": "gpio", "altr_pio_1_0": "gpio", "ambiq_gpio_bank": "gpio", "andestech_atcgpio100": "gpio", "arm_cmsdk_gpio": "gpio", + "arm_mmio32_gpio": "gpio", "atmel_sam0_gpio": "gpio", "atmel_sam4l_gpio": "gpio", "atmel_sam_gpio": "gpio", "awinic_aw9523b_gpio": "gpio", + "bflb_bl60x_70x_gpio": "gpio", + "bflb_bl61x_gpio": "gpio", "brcm_bcm2711_gpio": "gpio", "brcm_brcmstb_gpio": "gpio", "brcm_iproc_gpio": "gpio", "cypress_cy8c95xx_gpio_port": "gpio", "cypress_psoc6_gpio": "gpio", "efinix_sapphire_gpio": "gpio", + "ene_kb106x_gpio": "gpio", "ene_kb1200_gpio": "gpio", "espressif_esp32_gpio": "gpio", + "espressif_esp32_lpgpio": "gpio", "fcs_fxl6408": "gpio", "gaisler_grgpio": "gpio", "gd_gd32_gpio": "gpio", @@ -622,6 +830,7 @@ "infineon_xmc4xxx_gpio": "gpio", "intel_gpio": "gpio", "intel_sedi_gpio": "gpio", + "ite_it51xxx_gpio": "gpio", "ite_it8801_gpio": "gpio", "ite_it8xxx2_gpio": "gpio", "ite_it8xxx2_gpio_v2": "gpio", @@ -638,10 +847,13 @@ "microchip_mcp23s18": "gpio", "microchip_mec5_gpio": "gpio", "microchip_mpfs_gpio": "gpio", + "microchip_port_g1_gpio": "gpio", + "microchip_sam_pio4": "gpio", "microchip_xec_gpio": "gpio", "microchip_xec_gpio_v2": "gpio", "neorv32_gpio": "gpio", "nordic_npm1300_gpio": "gpio", + "nordic_npm1304_gpio": "gpio", "nordic_npm2100_gpio": "gpio", "nordic_npm6001_gpio": "gpio", "nordic_nrf_gpio": "gpio", @@ -656,27 +868,35 @@ "nxp_kinetis_gpio": "gpio", "nxp_lpc11u6x_gpio": "gpio", "nxp_lpc_gpio_port": "gpio", + "nxp_pca6408": "gpio", + "nxp_pca6416": "gpio", "nxp_pca9538": "gpio", "nxp_pca9539": "gpio", "nxp_pca9554": "gpio", "nxp_pca9555": "gpio", "nxp_pca95xx": "gpio", + "nxp_pcal6408": "gpio", "nxp_pcal6408a": "gpio", + "nxp_pcal6416": "gpio", "nxp_pcal6416a": "gpio", "nxp_pcal6524": "gpio", "nxp_pcal6534": "gpio", + "nxp_pcal9538": "gpio", + "nxp_pcal9539": "gpio", + "nxp_pcal9722": "gpio", "nxp_pcf857x": "gpio", - "nxp_s32_gpio": "gpio", "nxp_sc18im704_gpio": "gpio", + "nxp_siul2_gpio": "gpio", "openisa_rv32m1_gpio": "gpio", "quicklogic_eos_s3_gpio": "gpio", - "raspberrypi_pico_gpio": "gpio", + "raspberrypi_pico_gpio_port": "gpio", "raspberrypi_rp1_gpio": "gpio", "realtek_rts5912_gpio": "gpio", - "renesas_ra_gpio": "gpio", "renesas_ra_gpio_ioport": "gpio", "renesas_rcar_gpio": "gpio", + "renesas_rx_gpio": "gpio", "renesas_rz_gpio": "gpio", + "renesas_rza2m_gpio": "gpio", "renesas_rzt2m_gpio": "gpio", "renesas_smartbond_gpio": "gpio", "richtek_rt1718s": "gpio", @@ -685,18 +905,25 @@ "semtech_sx1509b": "gpio", "sensry_sy1xx_gpio": "gpio", "sifive_gpio0": "gpio", + "sifli_sf32lb_gpio": "gpio", "silabs_gecko_gpio_port": "gpio", + "silabs_gpio": "gpio", "silabs_si32_gpio": "gpio", + "silabs_siwx91x_gpio": "gpio", + "silabs_siwx91x_gpio_uulp": "gpio", "snps_creg_gpio": "gpio", "snps_designware_gpio": "gpio", + "st_mfxstm32l152": "gpio", "st_stm32_gpio": "gpio", "st_stmpe1600": "gpio", "telink_b91_gpio": "gpio", - "ti_ads114s0x_gpio": "gpio", + "ti_ads1x4s0x_gpio": "gpio", "ti_cc13xx_cc26xx_gpio": "gpio", + "ti_cc23x0_gpio": "gpio", "ti_cc32xx_gpio": "gpio", "ti_davinci_gpio": "gpio", "ti_lmp90xxx_gpio": "gpio", + "ti_mspm0_gpio": "gpio", "ti_sn74hc595": "gpio", "ti_stellaris_gpio": "gpio", "ti_tca6424a": "gpio", @@ -715,6 +942,7 @@ # # hdlc_rcp_if "nxp_hdlc_rcp_if": "hdlc_rcp_if", + "spi_hdlc_rcp_if": "hdlc_rcp_if", "uart_hdlc_rcp_if": "hdlc_rcp_if", # # hwinfo @@ -741,6 +969,7 @@ "atmel_sam_i2c_twihs": "i2c", "atmel_sam_i2c_twim": "i2c", "brcm_iproc_i2c": "i2c", + "cdns_i2c": "i2c", "ene_kb1200_i2c": "i2c", "espressif_esp32_i2c": "i2c", "fsl_imx21_i2c": "i2c", @@ -748,11 +977,14 @@ "gpio_i2c": "i2c", "gpio_i2c_switch": "i2c", "infineon_cat1_i2c": "i2c", + "infineon_cat1_i2c_pdl": "i2c", "infineon_xmc4xxx_i2c": "i2c", "intel_sedi_i2c": "i2c", "ite_enhance_i2c": "i2c", + "ite_it51xxx_i2c": "i2c", "ite_it8xxx2_i2c": "i2c", "litex_i2c": "i2c", + "litex_litei2c": "i2c", "microchip_mpfs_i2c": "i2c", "microchip_xec_i2c": "i2c", "microchip_xec_i2c_v2": "i2c", @@ -760,27 +992,38 @@ "nuvoton_npcx_i2c_ctrl": "i2c", "nuvoton_npcx_i2c_port": "i2c", "nuvoton_numaker_i2c": "i2c", + "nxp_ii2c": "i2c", "nxp_kinetis_i2c": "i2c", "nxp_lpc11u6x_i2c": "i2c", "nxp_lpc_i2c": "i2c", "nxp_lpi2c": "i2c", "nxp_sc18im704_i2c": "i2c", "openisa_rv32m1_lpi2c": "i2c", + "realtek_rts5912_i2c": "i2c", + "renesas_ra_i2c_sci": "i2c", + "renesas_ra_i2c_sci_b": "i2c", "renesas_ra_iic": "i2c", "renesas_rcar_i2c": "i2c", + "renesas_rx_i2c": "i2c", + "renesas_rz_iic": "i2c", + "renesas_rz_riic": "i2c", "renesas_smartbond_i2c": "i2c", + "sensry_sy1xx_i2c": "i2c", "sifive_i2c0": "i2c", "silabs_gecko_i2c": "i2c", + "silabs_i2c": "i2c", "st_stm32_i2c_v1": "i2c", "st_stm32_i2c_v2": "i2c", "telink_b91_i2c": "i2c", "ti_cc13xx_cc26xx_i2c": "i2c", + "ti_cc23x0_i2c": "i2c", "ti_cc32xx_i2c": "i2c", "ti_omap_i2c": "i2c", "ti_tca9544a": "i2c", "ti_tca9546a": "i2c", "ti_tca9548a": "i2c", "vnd_i2c": "i2c", + "wch_i2c": "i2c", "xlnx_xps_iic_2_00_a": "i2c", "xlnx_xps_iic_2_1": "i2c", "zephyr_i2c_emul_controller": "i2c", @@ -789,26 +1032,38 @@ "zephyr_i2c_target_eeprom": "i2c/target", # # i2s + "ambiq_i2s": "i2s", "atmel_sam_ssc": "i2s", "espressif_esp32_i2s": "i2s", "nxp_lpc_i2s": "i2s", "nxp_mcux_i2s": "i2s", + "renesas_ra_i2s_ssie": "i2s", + "silabs_siwx91x_i2s": "i2s", "st_stm32_i2s": "i2s", + "st_stm32_sai": "i2s", "vnd_i2s": "i2s", # # i3c + "adi_max32_i3c": "i3c", "cdns_i3c": "i3c", + "ite_it51xxx_i3cm": "i3c", + "ite_it51xxx_i3cs": "i3c", "nuvoton_npcx_i3c": "i3c", "nxp_mcux_i3c": "i3c", + "renesas_ra_i3c": "i3c", + "snps_designware_i3c": "i3c", "st_stm32_i3c": "i3c", "vnd_i3c": "i3c", # # ieee802154 "atmel_rf2xx": "ieee802154", "decawave_dw1000": "ieee802154", + "espressif_esp32_ieee802154": "ieee802154", "nordic_nrf_ieee802154": "ieee802154", "nxp_kw41z_ieee802154": "ieee802154", "nxp_mcr20a": "ieee802154", + "nxp_mcxw_ieee802154": "ieee802154", + "st_stm32wba_ieee802154": "ieee802154", "telink_b91_zb": "ieee802154", "ti_cc1200": "ieee802154", "ti_cc13xx_cc26xx_ieee802154": "ieee802154", @@ -819,8 +1074,11 @@ # input "adc_keys": "input", "analog_axis": "input", + "arduino_modulino_buttons": "input", + "chipsemi_chsc5x": "input", "chipsemi_chsc6x": "input", "cirque_pinnacle": "input", + "cypress_cy8cmbr3xxx": "input", "espressif_esp32_touch": "input", "focaltech_ft5336": "input", "futaba_sbus": "input", @@ -830,17 +1088,27 @@ "gpio_qdec": "input", "hynitron_cst816s": "input", "ilitek_ili2132a": "input", + "ite_it51xxx_kbd": "input", "ite_it8801_kbd": "input", "ite_it8xxx2_kbd": "input", "microchip_cap12xx": "input", "microchip_xec_kbd": "input", "nintendo_nunchuk": "input", "nuvoton_npcx_kbd": "input", + "nxp_mcux_kpp": "input", "pixart_pat912x": "input", "pixart_paw32xx": "input", "pixart_pmw3610": "input", + "realtek_rts5912_kbd": "input", + "renesas_ra_ctsu": "input", + "renesas_ra_ctsu_button": "input", + "renesas_ra_ctsu_slider": "input", + "renesas_ra_ctsu_wheel": "input", + "renesas_rx_ctsu": "input", "sitronix_cf1133": "input", + "st_stm32_tsc": "input", "st_stmpe811": "input", + "vishay_vs1838b": "input", "xptek_xpt2046": "input", "zephyr_input_sdl_touch": "input", "zephyr_native_linux_evdev": "input", @@ -853,12 +1121,14 @@ "atmel_sam0_eic": "interrupt_controller", "gaisler_irqmp": "interrupt_controller", "gd_gd32_exti": "interrupt_controller", + "hazard3_hazard3_intc": "interrupt_controller", "infineon_xmc4xxx_intc": "interrupt_controller", "intel_ace_intc": "interrupt_controller", "intel_cavs_intc": "interrupt_controller", "intel_ioapic": "interrupt_controller", "intel_loapic": "interrupt_controller", "intel_vt_d": "interrupt_controller", + "ite_it51xxx_wuc": "interrupt_controller", "ite_it8xxx2_wuc": "interrupt_controller", "litex_vexriscv_intc0": "interrupt_controller", "mediatek_adsp_intc": "interrupt_controller", @@ -867,10 +1137,12 @@ "nuvoton_npcx_miwu": "interrupt_controller", "nxp_irqsteer_intc": "interrupt_controller", "nxp_pint": "interrupt_controller", - "nxp_s32_siul2_eirq": "interrupt_controller", "nxp_s32_wkpu": "interrupt_controller", + "nxp_siul2_eirq": "interrupt_controller", "openisa_rv32m1_intmux": "interrupt_controller", - "renesas_ra_interrupt_controller_unit": "interrupt_controller", + "renesas_rx_icu": "interrupt_controller", + "renesas_rz_ext_irq": "interrupt_controller", + "riscv_clic": "interrupt_controller", "shared_irq": "interrupt_controller", "sifive_plic_1_0_0": "interrupt_controller", "snps_arcv2_intc": "interrupt_controller", @@ -878,6 +1150,7 @@ "st_stm32wb0_gpio_intc": "interrupt_controller", "swerv_pic": "interrupt_controller", "ti_vim": "interrupt_controller", + "wch_exti": "interrupt_controller", "wch_pfic": "interrupt_controller", # # ipm @@ -893,17 +1166,19 @@ "xlnx_zynqmp_ipi_mailbox": "ipm", "zephyr_mbox_ipm": "ipm", # - # kscan - "zephyr_kscan_input": "kscan", - # # led + "arduino_modulino_buttons_leds": "led", + "dac_leds": "led", "gpio_leds": "led", "holtek_ht16k33": "led", "issi_is31fl3194": "led", "issi_is31fl3216a": "led", "issi_is31fl3733": "led", + "leds_group_multicolor": "led", "microchip_xec_bbled": "led", "nordic_npm1300_led": "led", + "nordic_npm1304_led": "led", + "nxp_pca9533": "led", "nxp_pca9633": "led", "onnn_ncp5623": "led", "pwm_leds": "led", @@ -920,6 +1195,7 @@ # # led_strip "apa_apa102": "led_strip", + "arduino_modulino_pixels": "led_strip", "greeled_lpd8803": "led_strip", "greeled_lpd8806": "led_strip", "ti_tlc5971": "led_strip", @@ -928,14 +1204,18 @@ "worldsemi_ws2812_i2s": "led_strip", "worldsemi_ws2812_rpi_pico_pio": "led_strip", "worldsemi_ws2812_spi": "led_strip", + "worldsemi_ws2812_uart": "led_strip", # # lora "reyax_rylrxxx": "lora", - "semtech_sx1272": "lora", - "semtech_sx1276": "lora", + # + # lora/loramac_node + "semtech_sx1272": "lora/loramac_node", + "semtech_sx1276": "lora/loramac_node", # # mbox "andestech_mbox_plic_sw": "mbox", + "arm_mhuv3": "mbox", "espressif_mbox_esp32": "mbox", "linaro_ivshmem_mbox": "mbox", "nordic_mbox_nrf_ipc": "mbox", @@ -948,13 +1228,18 @@ "nxp_mbox_imx_mu": "mbox", "nxp_mbox_mailbox": "mbox", "nxp_s32_mru": "mbox", + "renesas_ra_ipc_mbox": "mbox", + "renesas_rz_mhu_mbox": "mbox", "st_mbox_stm32_hsem": "mbox", + "ti_omap_mailbox": "mbox", + "ti_secure_proxy": "mbox", # # mdio "adi_adin2111_mdio": "mdio", "atmel_sam_mdio": "mdio", "espressif_esp32_mdio": "mdio", "infineon_xmc4xxx_mdio": "mdio", + "intel_igc_mdio": "mdio", "litex_liteeth_mdio": "mdio", "microchip_lan865x_mdio": "mdio", "nxp_enet_mdio": "mdio", @@ -963,30 +1248,40 @@ "nxp_s32_gmac_mdio": "mdio", "nxp_s32_netc_emdio": "mdio", "renesas_ra_mdio": "mdio", - "smsc_lan91c111_mdio": "mdio", + "sensry_sy1xx_mdio": "mdio", "snps_dwcxgmac_mdio": "mdio", "st_stm32_mdio": "mdio", + "xlnx_axi_ethernet_1_00_a_mdio": "mdio", "zephyr_mdio_gpio": "mdio", # # memc + "adi_max32_hpb": "memc", "atmel_sam_smc": "memc", + "bflb_bl61x_psram": "memc", "mspi_aps6404l": "memc", + "mspi_aps_z8": "memc", "nxp_imx_flexspi": "memc", "nxp_imx_flexspi_s27ks0641": "memc", "nxp_imx_flexspi_w956a8mbya": "memc", "nxp_s32_qspi": "memc", + "nxp_xspi": "memc", + "nxp_xspi_psram": "memc", "renesas_ra_sdram": "memc", "renesas_smartbond_nor_psram": "memc", "sifive_fu740_c000_ddr": "memc", + "silabs_siwx91x_qspi_memory": "memc", "st_stm32_fmc": "memc", "st_stm32_fmc_nor_psram": "memc", "st_stm32_fmc_sdram": "memc", + "st_stm32_ospi_psram": "memc", + "st_stm32_xspi_psram": "memc", "st_stm32h7_fmc": "memc", # # mfd "adi_ad559x": "mfd", "adi_adp5585": "mfd", "adi_max22017": "mfd", + "adi_maxq10xx": "mfd", "awinic_aw9523b": "mfd", "infineon_tle9104": "mfd", "ite_it8801_altctrl": "mfd", @@ -994,16 +1289,21 @@ "maxim_ds3231_mfd": "mfd", "maxim_max20335": "mfd", "maxim_max31790": "mfd", + "microchip_sam_flexcom": "mfd", + "motorola_mc146818_mfd": "mfd", "nordic_npm1300": "mfd", + "nordic_npm1304": "mfd", "nordic_npm2100": "mfd", "nordic_npm6001": "mfd", "nuvoton_nct38xx": "mfd", "nxp_lp_flexcomm": "mfd", + "nxp_pca9422": "mfd", + "nxp_pf1550": "mfd", "rohm_bd8lb600fs": "mfd", - "x_powers_axp192": "mfd", # # mipi_dbi "nxp_lcdic": "mipi_dbi", + "nxp_mipi_dbi_dcnano_lcdif": "mipi_dbi", "nxp_mipi_dbi_flexio_lcdif": "mipi_dbi", "renesas_smartbond_mipi_dbi": "mipi_dbi", "st_stm32_fmc_mipi_dbi": "mipi_dbi", @@ -1013,6 +1313,7 @@ # mipi_dsi "nxp_imx_mipi_dsi": "mipi_dsi", "nxp_mipi_dsi_2l": "mipi_dsi", + "nxp_mipi_dsi_dwc": "mipi_dsi", "renesas_ra_mipi_dsi": "mipi_dsi", "st_stm32_mipi_dsi": "mipi_dsi", "vnd_mipi_dsi": "mipi_dsi", @@ -1022,6 +1323,8 @@ # # misc/ethos_u "arm_ethos_u": "misc/ethos_u", + "nuvoton_numaker_npu": "misc/ethos_u", + "renesas_ra_npu": "misc/ethos_u", # # misc/ft8xx "ftdi_ft800": "misc/ft8xx", @@ -1029,12 +1332,22 @@ # misc/grove_lcd_rgb "seeed_grove_lcd_rgb": "misc/grove_lcd_rgb", # + # misc/interconn/renesas_elc + "renesas_ra_elc": "misc/interconn/renesas_elc", + # # misc/mcux_flexio "nxp_flexio": "misc/mcux_flexio", # # misc/nordic_vpr_launcher "nordic_nrf_vpr_coprocessor": "misc/nordic_vpr_launcher", # + # misc/nxp_imx93_mediamix + "nxp_imx93_mediamix": "misc/nxp_imx93_mediamix", + # + # misc/nxp_rtxxx_dsp_ctrl + "nxp_rt600_dsp_ctrl": "misc/nxp_rtxxx_dsp_ctrl", + "nxp_rt700_dsp_ctrl_hifi4": "misc/nxp_rtxxx_dsp_ctrl", + # # misc/nxp_s32_emios "nxp_s32_emios": "misc/nxp_s32_emios", # @@ -1044,6 +1357,15 @@ # misc/renesas_ra_external_interrupt "renesas_ra_external_interrupt": "misc/renesas_ra_external_interrupt", # + # misc/renesas_rx_dtc + "renesas_rx_dtc": "misc/renesas_rx_dtc", + # + # misc/renesas_rx_external_interrupt + "renesas_rx_external_interrupt": "misc/renesas_rx_external_interrupt", + # + # misc/stm32n6_axisram + "st_stm32n6_ramcfg": "misc/stm32n6_axisram", + # # misc/timeaware_gpio "intel_timeaware_gpio": "misc/timeaware_gpio", # @@ -1053,11 +1375,12 @@ # modem "nordic_nrf91_slm": "modem", "quectel_bg95": "modem", + "quectel_bg96": "modem", "quectel_bg9x": "modem", "quectel_eg25_g": "modem", - "simcom_sim7080": "modem", + "quectel_eg800q": "modem", + "simcom_a76xx": "modem", "sqn_gm02s": "modem", - "swir_hl7800": "modem", "telit_me310g1": "modem", "telit_me910g1": "modem", "u_blox_lara_r6": "modem", @@ -1065,10 +1388,24 @@ "u_blox_sara_r5": "modem", "wnc_m14a2a": "modem", # + # modem/hl78xx + "swir_hl7800": "modem/hl78xx", + "swir_hl7800_offload": "modem/hl78xx", + "swir_hl7812": "modem/hl78xx", + "swir_hl7812_offload": "modem/hl78xx", + # + # modem/simcom/sim7080 + "simcom_sim7080": "modem/simcom/sim7080", + # # mspi "ambiq_mspi_controller": "mspi", + "snps_designware_ssi": "mspi", "zephyr_mspi_emul_controller": "mspi", # + # opamp + "nxp_opamp": "opamp", + "nxp_opamp_fast": "opamp", + # # pcie/controller "brcm_brcmstb_pcie": "pcie/controller", # @@ -1086,6 +1423,7 @@ "nuvoton_npcx_peci": "peci", # # pinctrl + "ene_kb106x_pinctrl": "pinctrl", "ene_kb1200_pinctrl": "pinctrl", "infineon_xmc4xxx_pinctrl": "pinctrl", "ite_it8xxx2_pinctrl_func": "pinctrl", @@ -1099,12 +1437,19 @@ "realtek_rts5912_pinctrl": "pinctrl", "sensry_sy1xx_pinctrl": "pinctrl", "sifive_pinctrl": "pinctrl", + "sifli_sf32lb52x_pinmux": "pinctrl", "silabs_dbus_pinctrl": "pinctrl", + "silabs_siwx91x_pinctrl": "pinctrl", "snps_emsdp_pinctrl": "pinctrl", "telink_b91_pinctrl": "pinctrl", "ti_cc13xx_cc26xx_pinctrl": "pinctrl", + "ti_cc23x0_pinctrl": "pinctrl", "ti_cc32xx_pinctrl": "pinctrl", "ti_k3_pinctrl": "pinctrl", + "ti_mspm0_pinctrl": "pinctrl", + "wch_00x_afio": "pinctrl", + "wch_20x_30x_afio": "pinctrl", + "wch_afio": "pinctrl", "xlnx_pinctrl_zynq": "pinctrl", "xlnx_pinctrl_zynqmp": "pinctrl", # @@ -1112,6 +1457,7 @@ "renesas_rcar_pfc": "pinctrl/renesas/rcar", # # pinctrl/renesas/rz + "renesas_rza2m_pinctrl": "pinctrl/renesas/rz", "renesas_rzt2m_pinctrl": "pinctrl/renesas/rz", # # pm_cpu_ops @@ -1120,38 +1466,53 @@ # # power_domain "intel_adsp_power_domain": "power_domain", + "nordic_nrfs_gdpwr": "power_domain", + "nordic_nrfs_swext": "power_domain", "nxp_scu_pd": "power_domain", "power_domain_gpio": "power_domain", "power_domain_gpio_monitor": "power_domain", + "power_domain_soc_state_change": "power_domain", + "silabs_siwx91x_power_domain": "power_domain", + "ti_sci_pm_domain": "power_domain", # # ps2 "microchip_xec_ps2": "ps2", "nuvoton_npcx_ps2_channel": "ps2", "nuvoton_npcx_ps2_ctrl": "ps2", # + # psi5 + "nxp_s32_psi5": "psi5", + # # ptp_clock "nxp_enet_ptp_clock": "ptp_clock", + "nxp_netc_ptp_clock": "ptp_clock", # # pwm "adi_max32_pwm": "pwm", + "ambiq_ctimer_pwm": "pwm", + "ambiq_timer_pwm": "pwm", "atmel_sam0_tc_pwm": "pwm", "atmel_sam0_tcc_pwm": "pwm", "atmel_sam_pwm": "pwm", + "ene_kb106x_pwm": "pwm", "ene_kb1200_pwm": "pwm", "espressif_esp32_ledc": "pwm", "espressif_esp32_mcpwm": "pwm", "fsl_imx27_pwm": "pwm", "gd_gd32_pwm": "pwm", - "infineon_cat1_pwm": "pwm", + "infineon_tcpwm_pwm": "pwm", "infineon_xmc4xxx_ccu4_pwm": "pwm", "infineon_xmc4xxx_ccu8_pwm": "pwm", "intel_blinky_pwm": "pwm", + "ite_it51xxx_pwm": "pwm", "ite_it8801_pwm": "pwm", "ite_it8xxx2_pwm": "pwm", "litex_pwm": "pwm", "maxim_max31790_pwm": "pwm", + "microchip_tcc_g1_pwm": "pwm", "microchip_xec_pwm": "pwm", "microchip_xec_pwmbbled": "pwm", + "neorv32_pwm": "pwm", "nordic_nrf_sw_pwm": "pwm", "nuvoton_npcx_pwm": "pwm", "nuvoton_numaker_pwm": "pwm", @@ -1167,14 +1528,24 @@ "nxp_sctimer_pwm": "pwm", "openisa_rv32m1_tpm": "pwm", "raspberrypi_pico_pwm": "pwm", + "realtek_rts5912_pwm": "pwm", "renesas_pwm_rcar": "pwm", "renesas_ra_pwm": "pwm", + "renesas_rx_mtu_pwm": "pwm", + "renesas_rz_gpt_pwm": "pwm", + "renesas_rz_mtu_pwm": "pwm", "sifive_pwm0": "pwm", "silabs_gecko_pwm": "pwm", + "silabs_letimer_pwm": "pwm", + "silabs_siwx91x_pwm": "pwm", + "silabs_timer_pwm": "pwm", "st_stm32_pwm": "pwm", "telink_b91_pwm": "pwm", "ti_cc13xx_cc26xx_timer_pwm": "pwm", + "ti_cc23x0_lgpt_pwm": "pwm", + "ti_mspm0_timer_pwm": "pwm", "vnd_pwm": "pwm", + "wch_gptm_pwm": "pwm", "xlnx_xps_timer_1_00_a_pwm": "pwm", "zephyr_fake_pwm": "pwm", # @@ -1185,30 +1556,39 @@ "mps_mpm54304": "regulator", "nordic_npm1100": "regulator", "nordic_npm1300_regulator": "regulator", + "nordic_npm1304_regulator": "regulator", "nordic_npm2100_regulator": "regulator", "nordic_npm6001_regulator": "regulator", "nxp_pca9420": "regulator", + "nxp_pca9422_regulator": "regulator", + "nxp_pf1550_regulator": "regulator", "nxp_vref": "regulator", + "nxp_vrefv1": "regulator", "raspberrypi_core_supply_regulator": "regulator", "regulator_fixed": "regulator", "regulator_gpio": "regulator", "renesas_smartbond_regulator": "regulator", - "x_powers_axp192_regulator": "regulator", "zephyr_fake_regulator": "regulator", # # reset "aspeed_ast10x0_reset": "reset", "gd_gd32_rctl": "reset", "intel_socfpga_reset": "reset", + "microchip_mpfs_reset": "reset", + "microchip_rstc_g1_reset": "reset", "nuvoton_npcx_rst": "reset", "nuvoton_numaker_rst": "reset", "nxp_lpc_syscon_reset": "reset", + "nxp_mrcc_reset": "reset", "nxp_rstctl": "reset", "raspberrypi_pico_reset": "reset", + "reset_mmio": "reset", + "sifli_sf32lb_rcc_rctl": "reset", "st_stm32_rcc_rctl": "reset", # # retained_mem "nordic_nrf_gpregret": "retained_mem", + "silabs_buram": "retained_mem", "zephyr_retained_ram": "retained_mem", "zephyr_retained_reg": "retained_mem", # @@ -1216,39 +1596,58 @@ "ambiq_am1805": "rtc", "ambiq_rtc": "rtc", "atmel_sam_rtc": "rtc", + "epson_rx8130ce_rtc": "rtc", "infineon_cat1_rtc": "rtc", "infineon_xmc4xxx_rtc": "rtc", "maxim_ds1307": "rtc", + "maxim_ds1337": "rtc", "maxim_ds3231_rtc": "rtc", "microcrystal_rv3028": "rtc", + "microcrystal_rv3032": "rtc", "microcrystal_rv8803": "rtc", "microcrystal_rv_8263_c8": "rtc", "motorola_mc146818": "rtc", "nuvoton_numaker_rtc": "rtc", "nxp_irtc": "rtc", + "nxp_pcf2123": "rtc", + "nxp_pcf85063a": "rtc", "nxp_pcf8523": "rtc", "nxp_pcf8563": "rtc", "raspberrypi_pico_rtc": "rtc", + "realtek_rts5912_rtc": "rtc", + "renesas_ra_rtc": "rtc", "renesas_smartbond_rtc": "rtc", + "silabs_siwx91x_rtc": "rtc", "st_stm32_rtc": "rtc", + "ti_bq32002": "rtc", + "ti_mspm0_rtc": "rtc", "zephyr_fake_rtc": "rtc", + "zephyr_rtc_counter": "rtc", "zephyr_rtc_emul": "rtc", # # sdhc + "adi_max32_sdhc": "sdhc", + "ambiq_sdio": "sdhc", "atmel_sam_hsmci": "sdhc", "cdns_sdhc": "sdhc", "espressif_esp32_sdhc_slot": "sdhc", "infineon_cat1_sdhc_sdio": "sdhc", "intel_emmc_host": "sdhc", + "microchip_sama7g5_sdmmc": "sdhc", "nxp_imx_usdhc": "sdhc", "nxp_lpc_sdif": "sdhc", "renesas_ra_sdhc": "sdhc", "renesas_rcar_mmc": "sdhc", + "st_stm32_sdio": "sdhc", + "xlnx_versal_8_9a": "sdhc", "zephyr_sdhc_spi_slot": "sdhc", # # sensor/a01nyub "dfrobot_a01nyub": "sensor/a01nyub", # + # sensor/adi/ad2s1210 + "adi_ad2s1210": "sensor/adi/ad2s1210", + # # sensor/adi/adltc2990 "adi_adltc2990": "sensor/adi/adltc2990", # @@ -1271,6 +1670,12 @@ # sensor/adi/adxl372 "adi_adxl372": "sensor/adi/adxl372", # + # sensor/adi/max32664c + "maxim_max32664c": "sensor/adi/max32664c", + # + # sensor/als31300 + "allegro_als31300": "sensor/als31300", + # # sensor/amd_sb_tsi "amd_sb_tsi": "sensor/amd_sb_tsi", # @@ -1362,6 +1767,9 @@ "bosch_bmp388": "sensor/bosch/bmp388", "bosch_bmp390": "sensor/bosch/bmp388", # + # sensor/broadcom/afbr_s50 + "brcm_afbr_s50": "sensor/broadcom/afbr_s50", + # # sensor/current_amp "current_sense_amplifier": "sensor/current_amp", # @@ -1377,6 +1785,9 @@ # sensor/espressif/pcnt_esp32 "espressif_esp32_pcnt": "sensor/espressif/pcnt_esp32", # + # sensor/everlight/als_pt19 + "everlight_als_pt19": "sensor/everlight/als_pt19", + # # sensor/explorir_m "gss_explorir_m": "sensor/explorir_m", # @@ -1414,6 +1825,9 @@ # sensor/infineon/xmc4xxx_temp "infineon_xmc4xxx_temp": "sensor/infineon/xmc4xxx_temp", # + # sensor/ite/ite_tach_it51xxx + "ite_it51xxx_tach": "sensor/ite/ite_tach_it51xxx", + # # sensor/ite/ite_tach_it8xxx2 "ite_it8xxx2_tach": "sensor/ite/ite_tach_it8xxx2", # @@ -1423,6 +1837,12 @@ # sensor/jedec/jc42 "jedec_jc_42_4_temp": "sensor/jedec/jc42", # + # sensor/liteon/ltr329 + "liteon_ltr329": "sensor/liteon/ltr329", + # + # sensor/liteon/ltrf216a + "liteon_ltrf216a": "sensor/liteon/ltrf216a", + # # sensor/lm35 "lm35": "sensor/lm35", # @@ -1432,9 +1852,6 @@ # sensor/lm77 "lm77": "sensor/lm77", # - # sensor/ltrf216a - "ltr_f216a": "sensor/ltrf216a", - # # sensor/maxim/ds18b20 "maxim_ds18b20": "sensor/maxim/ds18b20", "maxim_ds18s20": "sensor/maxim/ds18b20", @@ -1467,11 +1884,15 @@ # sensor/maxim/max6675 "maxim_max6675": "sensor/maxim/max6675", # + # sensor/mb7040 + "maxbotix_mb7040": "sensor/mb7040", + # # sensor/meas/ms5607 "meas_ms5607": "sensor/meas/ms5607", # # sensor/meas/ms5837 - "meas_ms5837": "sensor/meas/ms5837", + "meas_ms5837_02ba": "sensor/meas/ms5837", + "meas_ms5837_30ba": "sensor/meas/ms5837", # # sensor/melexis/mlx90394 "melexis_mlx90394": "sensor/melexis/mlx90394", @@ -1491,14 +1912,18 @@ # sensor/microchip/mcp970x "microchip_mcp970x": "sensor/microchip/mcp970x", # + # sensor/microchip/mtch9010 + "microchip_mtch9010": "sensor/microchip/mtch9010", + # # sensor/microchip/tcn75a "microchip_tcn75a": "sensor/microchip/tcn75a", # # sensor/nct75 "onnn_nct75": "sensor/nct75", # - # sensor/nordic/npm1300_charger - "nordic_npm1300_charger": "sensor/nordic/npm1300_charger", + # sensor/nordic/npm13xx_charger + "nordic_npm1300_charger": "sensor/nordic/npm13xx_charger", + "nordic_npm1304_charger": "sensor/nordic/npm13xx_charger", # # sensor/nordic/npm2100_vbat "nordic_npm2100_vbat": "sensor/nordic/npm2100_vbat", @@ -1541,9 +1966,18 @@ # sensor/nxp/nxp_kinetis_temp "nxp_kinetis_temperature": "sensor/nxp/nxp_kinetis_temp", # + # sensor/nxp/nxp_lpadc_temp40 + "nxp_lpadc_temp40": "sensor/nxp/nxp_lpadc_temp40", + # + # sensor/nxp/nxp_pmc_tmpsns + "nxp_pmc_tmpsns": "sensor/nxp/nxp_pmc_tmpsns", + # # sensor/nxp/nxp_tempmon "nxp_tempmon": "sensor/nxp/nxp_tempmon", # + # sensor/nxp/nxp_tmpsns + "nxp_tmpsns": "sensor/nxp/nxp_tmpsns", + # # sensor/nxp/p3t1755 "nxp_p3t1755": "sensor/nxp/p3t1755", # @@ -1553,12 +1987,40 @@ # sensor/nxp/qdec_nxp_s32 "nxp_qdec_s32": "sensor/nxp/qdec_nxp_s32", # + # sensor/nxp/qdec_tpm + "nxp_tpm_qdec": "sensor/nxp/qdec_tpm", + # + # sensor/omron/2smpb_02e + "omron_2smpb_02e": "sensor/omron/2smpb_02e", + # + # sensor/omron/d6f + "omron_d6f_p0001": "sensor/omron/d6f", + "omron_d6f_p0010": "sensor/omron/d6f", + # + # sensor/pixart/paa3905 + "pixart_paa3905": "sensor/pixart/paa3905", + # + # sensor/pixart/paj7620 + "pixart_paj7620": "sensor/pixart/paj7620", + # + # sensor/pixart/pat9136 + "pixart_pat9136": "sensor/pixart/pat9136", + # # sensor/pms7003 "plantower_pms7003": "sensor/pms7003", # + # sensor/pni/rm3100 + "pni_rm3100": "sensor/pni/rm3100", + # + # sensor/pzem004t + "peacefair_pzem004t": "sensor/pzem004t", + # # sensor/qdec_sam "atmel_sam_tc_qdec": "sensor/qdec_sam", # + # sensor/realtek/rts5912 + "realtek_rts5912_tach": "sensor/realtek/rts5912", + # # sensor/renesas/hs300x "renesas_hs300x": "sensor/renesas/hs300x", # @@ -1571,9 +2033,15 @@ # sensor/rohm/bd8lb600fs "rohm_bd8lb600fs_diagnostics": "sensor/rohm/bd8lb600fs", # + # sensor/rohm/bh1730 + "rohm_bh1730": "sensor/rohm/bh1730", + # # sensor/rohm/bh1750 "rohm_bh1750": "sensor/rohm/bh1750", # + # sensor/rohm/bh1790 + "rohm_bh1790": "sensor/rohm/bh1790", + # # sensor/rpi_pico_temp "raspberrypi_pico_temp": "sensor/rpi_pico_temp", # @@ -1643,6 +2111,9 @@ # sensor/st/iis3dhhc "st_iis3dhhc": "sensor/st/iis3dhhc", # + # sensor/st/iis3dwb + "st_iis3dwb": "sensor/st/iis3dwb", + # # sensor/st/ism330dhcx "st_ism330dhcx": "sensor/st/ism330dhcx", # @@ -1702,7 +2173,8 @@ "st_lsm6dso16is": "sensor/st/lsm6dso16is", # # sensor/st/lsm6dsv16x - "st_lsm6dsv16x": "sensor/st/lsm6dsv16x", + "DT_DRV_COMPAT_LSM6DSV16X": "sensor/st/lsm6dsv16x", + "DT_DRV_COMPAT_LSM6DSV32X": "sensor/st/lsm6dsv16x", # # sensor/st/lsm9ds0_gyro "st_lsm9ds0_gyro": "sensor/st/lsm9ds0_gyro", @@ -1713,6 +2185,9 @@ # sensor/st/lsm9ds1 "st_lsm9ds1": "sensor/st/lsm9ds1", # + # sensor/st/lsm9ds1_mag + "st_lsm9ds1_mag": "sensor/st/lsm9ds1_mag", + # # sensor/st/qdec_stm32 "st_stm32_qdec": "sensor/st/qdec_stm32", # @@ -1745,18 +2220,32 @@ # sensor/sx9500 "semtech_sx9500": "sensor/sx9500", # + # sensor/tach_gpio + "zephyr_tach_gpio": "sensor/tach_gpio", + # + # sensor/tdk/icm40627 + "invensense_icm40627": "sensor/tdk/icm40627", + # # sensor/tdk/icm42605 "invensense_icm42605": "sensor/tdk/icm42605", # - # sensor/tdk/icm42670 - "invensense_icm42670p": "sensor/tdk/icm42670", - "invensense_icm42670s": "sensor/tdk/icm42670", + # sensor/tdk/icm4268x + "invensense_icm42688": "sensor/tdk/icm4268x", + "invensense_icm4268x": "sensor/tdk/icm4268x", # - # sensor/tdk/icm42688 - "invensense_icm42688": "sensor/tdk/icm42688", + # sensor/tdk/icm42x70 + "invensense_icm42370p": "sensor/tdk/icm42x70", + "invensense_icm42670p": "sensor/tdk/icm42x70", + "invensense_icm42670s": "sensor/tdk/icm42x70", # - # sensor/tdk/icp10125 - "invensense_icp10125": "sensor/tdk/icp10125", + # sensor/tdk/icm45686 + "invensense_icm45686": "sensor/tdk/icm45686", + # + # sensor/tdk/icp101xx + "invensense_icp101xx": "sensor/tdk/icp101xx", + # + # sensor/tdk/icp201xx + "invensense_icp201xx": "sensor/tdk/icp201xx", # # sensor/tdk/mpu6050 "invensense_mpu6050": "sensor/tdk/mpu6050", @@ -1776,17 +2265,19 @@ # sensor/ti/ina219 "ti_ina219": "sensor/ti/ina219", # - # sensor/ti/ina226 - "ti_ina226": "sensor/ti/ina226", - # - # sensor/ti/ina23x - "ti_ina230": "sensor/ti/ina23x", - "ti_ina236": "sensor/ti/ina23x", - "ti_ina237": "sensor/ti/ina23x", + # sensor/ti/ina2xx + "ti_ina226": "sensor/ti/ina2xx", + "ti_ina228": "sensor/ti/ina2xx", + "ti_ina230": "sensor/ti/ina2xx", + "ti_ina236": "sensor/ti/ina2xx", + "ti_ina237": "sensor/ti/ina2xx", # # sensor/ti/ina3221 "ti_ina3221": "sensor/ti/ina3221", # + # sensor/ti/ina7xx + "ti_ina7xx": "sensor/ti/ina7xx", + # # sensor/ti/lm95234 "national_lm95234": "sensor/ti/lm95234", # @@ -1796,6 +2287,9 @@ # sensor/ti/ti_hdc "ti_hdc": "sensor/ti/ti_hdc", # + # sensor/ti/ti_hdc302x + "ti_hdc302x": "sensor/ti/ti_hdc302x", + # # sensor/ti/tmag5170 "ti_tmag5170": "sensor/ti/tmag5170", # @@ -1815,8 +2309,11 @@ # sensor/ti/tmp114 "ti_tmp114": "sensor/ti/tmp114", # - # sensor/ti/tmp116 - "ti_tmp116": "sensor/ti/tmp116", + # sensor/ti/tmp11x + "ti_tmp11x": "sensor/ti/tmp11x", + # + # sensor/ti/tmp435 + "ti_tmp435": "sensor/ti/tmp435", # # sensor/tsic_xx6 "ist_tsic_xx6": "sensor/tsic_xx6", @@ -1830,6 +2327,12 @@ # sensor/vishay/vcnl4040 "vishay_vcnl4040": "sensor/vishay/vcnl4040", # + # sensor/vishay/veml6031 + "vishay_veml6031": "sensor/vishay/veml6031", + # + # sensor/vishay/veml6046 + "vishay_veml6046": "sensor/vishay/veml6046", + # # sensor/vishay/veml7700 "vishay_veml7700": "sensor/vishay/veml7700", # @@ -1839,37 +2342,66 @@ # sensor/wsen/wsen_hids_2525020210002 "we_wsen_hids_2525020210002": "sensor/wsen/wsen_hids_2525020210002", # + # sensor/wsen/wsen_isds_2536030320001 + "we_wsen_isds_2536030320001": "sensor/wsen/wsen_isds_2536030320001", + # + # sensor/wsen/wsen_itds_2533020201601 + "we_wsen_itds_2533020201601": "sensor/wsen/wsen_itds_2533020201601", + # + # sensor/wsen/wsen_pads_2511020213301 + "we_wsen_pads_2511020213301": "sensor/wsen/wsen_pads_2511020213301", + # + # sensor/wsen/wsen_pdms_25131308XXX05 + "we_wsen_pdms_25131308xxx05": "sensor/wsen/wsen_pdms_25131308XXX05", + # # sensor/wsen/wsen_pdus_25131308XXXXX "we_wsen_pdus_25131308xxxxx": "sensor/wsen/wsen_pdus_25131308XXXXX", # + # sensor/wsen/wsen_tids_2521020222501 + "we_wsen_tids_2521020222501": "sensor/wsen/wsen_tids_2521020222501", + # + # sensor/xbr818 + "phosense_xbr818": "sensor/xbr818", + # + # sent + "nxp_s32_sent": "sent", + # # serial "SBSA_COMPAT": "serial", "adi_max32_uart": "serial", + "aesc_uart": "serial", "altr_jtag_uart": "serial", "altr_uart": "serial", + "ambiq_uart": "serial", "arm_cmsdk_uart": "serial", "arm_pl011": "serial", "atmel_sam0_uart": "serial", "atmel_sam_uart": "serial", "atmel_sam_usart": "serial", + "bflb_uart": "serial", "brcm_bcm2711_aux_uart": "serial", "cdns_uart": "serial", "cypress_psoc6_uart": "serial", "efinix_sapphire_uart0": "serial", + "ene_kb106x_uart": "serial", "ene_kb1200_uart": "serial", + "espressif_esp32_lpuart": "serial", "espressif_esp32_uart": "serial", "espressif_esp32_usb_serial": "serial", "gaisler_apbuart": "serial", "gd_gd32_usart": "serial", "infineon_cat1_uart": "serial", + "infineon_cat1_uart_pdl": "serial", "infineon_xmc4xxx_uart": "serial", "intel_lw_uart": "serial", "intel_sedi_uart": "serial", + "ite_it51xxx_uart": "serial", "ite_it8xxx2_uart": "serial", "litex_uart": "serial", "lowrisc_opentitan_uart": "serial", "microchip_coreuart": "serial", "microchip_mec5_uart": "serial", + "microchip_sercom_g1_uart": "serial", "microchip_xec_uart": "serial", "neorv32_uart": "serial", "nordic_nrf_uart": "serial", @@ -1891,35 +2423,47 @@ "realtek_rts5912_uart": "serial", "renesas_ra8_uart_sci_b": "serial", "renesas_ra_sci_uart": "serial", - "renesas_ra_uart_sci": "serial", "renesas_rcar_hscif": "serial", "renesas_rcar_scif": "serial", + "renesas_rx_uart_sci": "serial", + "renesas_rx_uart_sci_qemu": "serial", + "renesas_rz_sci_b_uart": "serial", + "renesas_rz_sci_uart": "serial", "renesas_rz_scif_uart": "serial", + "renesas_rza2m_scif_uart": "serial", "renesas_rzt2m_uart": "serial", "renesas_smartbond_uart": "serial", "segger_rtt_uart": "serial", "sensry_sy1xx_uart": "serial", "sifive_uart0": "serial", + "sifli_sf32lb_usart": "serial", "silabs_eusart_uart": "serial", "silabs_gecko_leuart": "serial", "silabs_gecko_uart": "serial", "silabs_gecko_usart": "serial", "silabs_si32_usart": "serial", + "silabs_usart_uart": "serial", "snps_hostlink_uart": "serial", "st_stm32_uart": "serial", "telink_b91_uart": "serial", "ti_cc13xx_cc26xx_uart": "serial", + "ti_cc23x0_uart": "serial", "ti_cc32xx_uart": "serial", "ti_msp432p4xx_uart": "serial", + "ti_mspm0_uart": "serial", "ti_stellaris_uart": "serial", + "virtio_console": "serial", "vnd_serial": "serial", "wch_usart": "serial", "xen_hvc_consoleio": "serial", "xlnx_xps_uartlite_1_00_a": "serial", "xlnx_xuartps": "serial", "zephyr_native_posix_uart": "serial", + "zephyr_native_pty_uart": "serial", "zephyr_native_tty_uart": "serial", "zephyr_nus_uart": "serial", + "zephyr_uart_bitbang": "serial", + "zephyr_uart_bridge": "serial", "zephyr_uart_emul": "serial", # # sip_svc @@ -1938,18 +2482,23 @@ "arm_pl022": "spi", "atmel_sam0_spi": "spi", "atmel_sam_spi": "spi", + "cdns_spi": "spi", "cypress_psoc6_spi": "spi", + "egis_et171_spi": "spi", "espressif_esp32_spi": "spi", "gaisler_spimctrl": "spi", "gd_gd32_spi": "spi", "infineon_cat1_spi": "spi", + "infineon_cat1_spi_pdl": "spi", "infineon_xmc4xxx_spi": "spi", "intel_penwell_spi": "spi", "intel_sedi_spi": "spi", + "ite_it51xxx_spi": "spi", "ite_it8xxx2_spi": "spi", "litex_spi": "spi", "litex_spi_litespi": "spi", "lowrisc_opentitan_spi": "spi", + "microchip_mec5_qspi": "spi", "microchip_mpfs_qspi": "spi", "microchip_mpfs_spi": "spi", "microchip_xec_qmspi": "spi", @@ -1961,20 +2510,28 @@ "nxp_imx_ecspi": "spi", "nxp_lpc_spi": "spi", "nxp_s32_spi": "spi", + "nxp_sc18is606_spi": "spi", "opencores_spi_simple": "spi", "openisa_rv32m1_lpspi": "spi", "raspberrypi_pico_spi_pio": "spi", "renesas_ra8_spi_b": "spi", "renesas_ra_spi": "spi", + "renesas_rx_rspi": "spi", + "renesas_rz_rspi": "spi", + "renesas_rz_spi": "spi", "renesas_smartbond_spi": "spi", "sifive_spi0": "spi", "silabs_eusart_spi": "spi", + "silabs_gspi": "spi", "silabs_usart_spi": "spi", "snps_designware_spi": "spi", "st_stm32_spi": "spi", "telink_b91_spi": "spi", "ti_cc13xx_cc26xx_spi": "spi", + "ti_cc23x0_spi": "spi", + "ti_omap_mcspi": "spi", "vnd_spi": "spi", + "wch_spi": "spi", "xlnx_xps_spi_2_00_a": "spi", "zephyr_spi_bitbang": "spi", "zephyr_spi_emul_controller": "spi", @@ -1984,16 +2541,20 @@ # # stepper "zephyr_fake_stepper": "stepper", - "zephyr_gpio_stepper": "stepper", + "zephyr_h_bridge_stepper": "stepper", # # stepper/adi_tmc "adi_tmc2209": "stepper/adi_tmc", - "adi_tmc5041": "stepper/adi_tmc", + "adi_tmc50xx": "stepper/adi_tmc", + # + # stepper/allegro + "allegro_a4979": "stepper/allegro", # # stepper/ti - "ti_drv8424": "stepper/ti", + "ti_drv84xx": "stepper/ti", # # syscon + "bflb_efuse": "syscon", "syscon": "syscon", # # tee/optee @@ -2001,35 +2562,35 @@ # # timer "ambiq_stimer": "timer", - "andestech_machine_timer": "timer", "atmel_sam0_rtc": "timer", "gaisler_gptimer": "timer", + "infineon_cat1_lp_timer": "timer", "intel_adsp_timer": "timer", "intel_hpet": "timer", + "ite_it51xxx_timer": "timer", "ite_it8xxx2_timer": "timer", "litex_timer0": "timer", - "lowrisc_machine_timer": "timer", - "microchip_mec5_ktimer": "timer", + "microchip_sam_pit64b": "timer", "microchip_xec_rtos_timer": "timer", - "neorv32_machine_timer": "timer", - "niosv_machine_timer": "timer", - "nuclei_systimer": "timer", "nuvoton_npcx_itim_timer": "timer", "nxp_gpt_hw_timer": "timer", - "nxp_kinetis_lptmr": "timer", "nxp_lptmr": "timer", "nxp_os_timer": "timer", "openisa_rv32m1_lptmr": "timer", "realtek_rts5912_rtmr": "timer", + "renesas_ra_ulpt_timer": "timer", "renesas_rcar_cmt": "timer", - "scr_machine_timer": "timer", - "sifive_clint0": "timer", + "renesas_rx_timer_cmt": "timer", + "renesas_rz_gtm_os_timer": "timer", + "renesas_rza2m_ostm": "timer", + "riscv_machine_timer": "timer", "silabs_gecko_burtc": "timer", "st_stm32_lptim": "timer", "sy1xx_sys_timer": "timer", - "telink_machine_timer": "timer", "ti_am654_timer": "timer", "ti_cc13xx_cc26xx_rtc_timer": "timer", + "ti_cc23x0_rtc_timer": "timer", + "ti_cc23x0_systim_timer": "timer", "wch_systick": "timer", "xlnx_ttcps": "timer", # @@ -2037,12 +2598,13 @@ "diodes_pi3usb9201": "usb/bc12", # # usb/device - "atmel_sam0_usb": "usb/device", "atmel_sam_usbc": "usb/device", "atmel_sam_usbhs": "usb/device", # # usb/udc + "adi_max32_usbhs": "usb/udc", "ambiq_usb": "usb/udc", + "atmel_sam0_usb": "usb/udc", "ite_it82xx2_usb": "usb/udc", "nordic_nrf_usbd": "usb/udc", "nuvoton_numaker_usbd": "usb/udc", @@ -2061,6 +2623,10 @@ # # usb/uhc "maxim_max3421e_spi": "usb/uhc", + "nxp_uhc_ehci": "usb/uhc", + "nxp_uhc_ip3516hs": "usb/uhc", + "nxp_uhc_khci": "usb/uhc", + "nxp_uhc_ohci": "usb/uhc", "zephyr_uhc_virtual": "usb/uhc", # # usb_c/ppc @@ -2069,6 +2635,7 @@ # # usb_c/tcpc "nuvoton_numaker_tcpc": "usb_c/tcpc", + "onnn_fusb307_tcpc": "usb_c/tcpc", "parade_ps8xxx": "usb_c/tcpc", "richtek_rt1715": "usb_c/tcpc", "st_stm32_ucpd": "usb_c/tcpc", @@ -2082,6 +2649,7 @@ "aptina_mt9m114": "video", "espressif_esp32_lcd_cam": "video", "galaxycore_gc2145": "video", + "himax_hm01b0": "video", "nxp_imx_csi": "video", "nxp_mipi_csi2rx": "video", "nxp_video_smartdma": "video", @@ -2089,10 +2657,21 @@ "ovti_ov5640": "video", "ovti_ov7670": "video", "ovti_ov7725": "video", + "ovti_ov9655": "video", + "renesas_ra_ceu": "video", + "sony_imx335": "video", + "st_mipid02": "video", "st_stm32_dcmi": "video", - "zephyr_sw_generator": "video", + "st_stm32_dcmipp": "video", + "st_stm32_jpeg": "video", + "st_stm32_venc": "video", "zephyr_video_emul_imager": "video", "zephyr_video_emul_rx": "video", + "zephyr_video_sw_generator": "video", + # + # virtio + "virtio_mmio": "virtio", + "virtio_pci": "virtio", # # virtualization "qemu_ivshmem": "virtualization", @@ -2113,7 +2692,9 @@ "andestech_atcwdt200": "watchdog", "arm_cmsdk_watchdog": "watchdog", "atmel_sam0_watchdog": "watchdog", + "atmel_sam4l_watchdog": "watchdog", "atmel_sam_watchdog": "watchdog", + "ene_kb106x_watchdog": "watchdog", "ene_kb1200_watchdog": "watchdog", "espressif_esp32_watchdog": "watchdog", "espressif_esp32_xt_wdt": "watchdog", @@ -2123,31 +2704,45 @@ "infineon_xmc4xxx_watchdog": "watchdog", "intel_adsp_watchdog": "watchdog", "intel_tco_wdt": "watchdog", + "ite_it51xxx_watchdog": "watchdog", "ite_it8xxx2_watchdog": "watchdog", "litex_watchdog": "watchdog", "lowrisc_opentitan_aontimer": "watchdog", "microchip_xec_watchdog": "watchdog", "nordic_npm1300_wdt": "watchdog", + "nordic_npm1304_wdt": "watchdog", "nordic_npm2100_wdt": "watchdog", "nordic_npm6001_wdt": "watchdog", "nuvoton_npcx_watchdog": "watchdog", "nuvoton_numaker_wwdt": "watchdog", + "nxp_cop": "watchdog", + "nxp_ewm": "watchdog", "nxp_fs26_wdog": "watchdog", "nxp_imx_wdog": "watchdog", "nxp_kinetis_wdog": "watchdog", "nxp_lpc_wwdt": "watchdog", + "nxp_rtwdog": "watchdog", "nxp_s32_swt": "watchdog", "nxp_wdog32": "watchdog", "raspberrypi_pico_watchdog": "watchdog", + "realtek_rts5912_watchdog": "watchdog", + "renesas_rx_iwdt": "watchdog", + "renesas_rz_wdt": "watchdog", "renesas_smartbond_watchdog": "watchdog", "sifive_wdt": "watchdog", + "sifli_sf32lb_wdt": "watchdog", "silabs_gecko_wdog": "watchdog", + "silabs_siwx91x_wdt": "watchdog", "snps_designware_watchdog": "watchdog", "st_stm32_watchdog": "watchdog", "st_stm32_window_watchdog": "watchdog", "ti_cc13xx_cc26xx_watchdog": "watchdog", + "ti_cc23x0_wdt": "watchdog", "ti_cc32xx_watchdog": "watchdog", + "ti_j7_rti_wdt": "watchdog", "ti_tps382x": "watchdog", + "wch_iwdg": "watchdog", + "xlnx_versal_wwdt": "watchdog", "xlnx_xps_timebase_wdt_1_00_a": "watchdog", # # wifi/esp32/src @@ -2166,6 +2761,9 @@ # wifi/nxp "nxp_wifi": "wifi/nxp", # + # wifi/siwx91x + "silabs_siwx91x_wifi": "wifi/siwx91x", + # # wifi/winc1500 "atmel_winc1500": "wifi/winc1500", } diff --git a/ports/zephyr-cp/cptools/cpbuild.py b/ports/zephyr-cp/cptools/cpbuild.py index ef836b3df8af3..edaf020c264a0 100644 --- a/ports/zephyr-cp/cptools/cpbuild.py +++ b/ports/zephyr-cp/cptools/cpbuild.py @@ -1,15 +1,14 @@ import asyncio -import inspect +import atexit +import hashlib +import json import logging import os import pathlib -import shlex -import time -import hashlib -import atexit -import json import re -import sys +import tempfile +import time +from typing import Optional logger = logging.getLogger(__name__) @@ -111,7 +110,14 @@ def _create_semaphore(): max_track = 0 -async def run_command(command, working_directory, description=None, check_hash=[], extradeps=[]): +async def run_command( + command, + working_directory, + description=None, + check_hash=[], + extradeps=[], + responsefile: Optional[pathlib.Path] = None, +): """ Runs a command asynchronously. The command should ideally be a list of strings and pathlib.Path objects. If all of the paths haven't been modified since the last @@ -124,26 +130,39 @@ async def run_command(command, working_directory, description=None, check_hash=[ Paths in check_hash are hashed before and after the command. If the hash is the same, then the old mtimes are reset. This is helpful if a command may produce - the same result and you don't want the rest of the build impacted. + the same result and you don't want the rest of the build impacted + + responsefile is used to store the command line arguments if they are too long for the OS. + The arguments will be replaced with @ and tried again. + If None, commands that are too long will fail. """ paths = [] + responsefile_contents = None if isinstance(command, list): for i, part in enumerate(command): if isinstance(part, pathlib.Path): paths.append(part) part = part.relative_to(working_directory, walk_up=True) - # if isinstance(part, list): command[i] = str(part) - command = " ".join(command) + command_string = " ".join(command) + + # When on windows, use a responsefile if the command string is >= 8192 + if responsefile is not None and os.name == "nt" and len(command_string) >= 8192: + # Escape backslashes + responsefile_contents = "\n".join(part.replace("\\", "\\\\") for part in command[1:]) + responsefile.write_text(responsefile_contents) + command_string = f"{command[0]} -v @{responsefile}" + else: + command_string = command - command_hash = hashlib.sha3_256(command.encode("utf-8")) + command_hash = hashlib.sha3_256(command_string.encode("utf-8")) command_hash.update(str(working_directory).encode("utf-8")) command_hash = command_hash.hexdigest() # If a command is run multiple times, then wait for the first one to continue. Don't run it again. if command_hash in ALREADY_RUN: - logger.debug(f"Already running {command_hash} {command}") + logger.debug(f"Already running {command_hash} {command_string}") await ALREADY_RUN[command_hash].wait() return ALREADY_RUN[command_hash] = asyncio.Event() @@ -169,7 +188,7 @@ async def run_command(command, working_directory, description=None, check_hash=[ run_reason = f"{p.relative_to(working_directory, walk_up=True)} is newer" break if nothing_newer: - logger.debug(f"Nothing newer {command[-32:]}") + logger.debug(f"Nothing newer {command_string[-32:]}") ALREADY_RUN[command_hash].set() return else: @@ -196,7 +215,7 @@ async def run_command(command, working_directory, description=None, check_hash=[ track = tracks.pop() start_time = time.perf_counter_ns() // 1000 process = await asyncio.create_subprocess_shell( - command, + command_string, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=working_directory, @@ -242,22 +261,25 @@ async def run_command(command, working_directory, description=None, check_hash=[ raise cancellation if description: logger.info(f"{description} ({run_reason})") - logger.debug(command) + logger.debug(command_string) else: - logger.info(f"{command} ({run_reason})") + logger.info(f"{command_string} ({run_reason})") if old_newest_file == newest_file: logger.error("No files were modified by the command.") raise RuntimeError() else: if command_hash in LAST_BUILD_TIMES: del LAST_BUILD_TIMES[command_hash] + logger.error(command_string) + if responsefile_contents: + logger.error(f"Response file contents:\n{responsefile_contents}") + logger.error(f"Return code: {process.returncode}") if stdout: logger.info(stdout.decode("utf-8").strip()) if stderr: logger.warning(stderr.decode("utf-8").strip()) if not stdout and not stderr: logger.warning("No output") - logger.error(command) if cancellation: raise cancellation raise RuntimeError() @@ -335,8 +357,8 @@ async def preprocess( ): output_file.parent.mkdir(parents=True, exist_ok=True) depfile = output_file.parent / (output_file.name + ".d") - if depfile.exists(): - pass + responsefile = output_file.parent / (output_file.name + ".rsp") + await run_command( [ self.c_compiler, @@ -354,6 +376,7 @@ async def preprocess( description=f"Preprocess {source_file.relative_to(self.srcdir)} -> {output_file.relative_to(self.builddir)}", working_directory=self.srcdir, check_hash=[output_file], + responsefile=responsefile, ) async def compile( @@ -365,6 +388,7 @@ async def compile( output_file = self.builddir / output_file output_file.parent.mkdir(parents=True, exist_ok=True) depfile = output_file.with_suffix(".d") + responsefile = output_file.with_suffix(".rsp") extradeps = [] if depfile.exists(): depfile_contents = depfile.read_text().split() @@ -375,25 +399,30 @@ async def compile( extradeps.append(pathlib.Path(dep)) else: extradeps.append(self.srcdir / dep) + await run_command( - [self.c_compiler, self.cflags, "-MMD", "-c", source_file, *flags, "-o", output_file], + [ + self.c_compiler, + self.cflags, + "-MMD", + "-c", + source_file, + *flags, + "-o", + output_file, + ], description=f"Compile {source_file.relative_to(self.srcdir)} -> {output_file.relative_to(self.builddir)}", working_directory=self.srcdir, extradeps=extradeps, + responsefile=responsefile, ) async def archive(self, objects: list[pathlib.Path], output_file: pathlib.Path): output_file.parent.mkdir(parents=True, exist_ok=True) - # Do one file at a time so that we don't have a long command line. run_command - # should skip unchanged files ok. - input_files = output_file.with_suffix(output_file.suffix + ".input_files") - input_file_content = "\n".join(str(p) for p in objects) - # Windows paths have \ as separator but ar wants them as / (like UNIX) - input_file_content = input_file_content.replace("\\", "/") - input_files.write_text(input_file_content) + responsefile = output_file.with_suffix(".rsp") await run_command( - [self.ar, "rvs", output_file, f"@{input_files}"], + [self.ar, "rvs", output_file, *objects], description=f"Create archive {output_file.relative_to(self.srcdir)}", working_directory=self.srcdir, - extradeps=objects, + responsefile=responsefile, ) diff --git a/ports/zephyr-cp/cptools/gen_compat2driver.py b/ports/zephyr-cp/cptools/gen_compat2driver.py index 0cb6a16f9da54..1e529072dab4a 100644 --- a/ports/zephyr-cp/cptools/gen_compat2driver.py +++ b/ports/zephyr-cp/cptools/gen_compat2driver.py @@ -2,7 +2,7 @@ mapping = {} -drivers = pathlib.Path("lib/zephyr/drivers") +drivers = pathlib.Path("zephyr/drivers") for p in drivers.glob("**/*.c"): for line in p.open(): if line.startswith("#define DT_DRV_COMPAT"): diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index cd0e3a6f2aade..071334161b94c 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -1,14 +1,16 @@ import logging import pathlib -import cpbuild -from devicetree import dtlib +import cpbuild import yaml - from compat2driver import COMPAT_TO_DRIVER +from devicetree import dtlib logger = logging.getLogger(__name__) +# GPIO flags defined here: include/zephyr/dt-bindings/gpio/gpio.h +GPIO_ACTIVE_LOW = 1 << 0 + MANUAL_COMPAT_TO_DRIVER = { "renesas_ra_nv_flash": "flash", } @@ -82,6 +84,26 @@ "D12", "D13", ], + "arducam,dvp-20pin-connector": [ + "SCL", + "SDA", + "VS", + "HS", + "PCLK", + "XCLK", + "D7", + "D6", + "D5", + "D4", + "D3", + "D2", + "D1", + "D0", + "PEN", + "PDN", + "GPIO0", + "GPIO1", + ], "renesas,ra-gpio-mipi-header": [ "IIC_SDA", "DISP_BLEN", @@ -89,6 +111,60 @@ "DISP_INT", "DISP_RST", ], + "renesas,ra-parallel-graphics-header": [ + "DISP_BLEN", + "IIC_SDA", + "DISP_INT", + "IIC_SCL", + "DISP_RST", + "LCDC_TCON0", + "LCDC_CLK", + "LCDC_TCON2", + "LCDC_TCON1", + "LCDC_EXTCLK", + "LCDC_TCON3", + "LCDC_DATA01", + "LCDC_DATA00", + "LCDC_DATA03", + "LCDC_DATA02", + "LCDC_DATA05", + "LCDC_DATA04", + "LCDC_DATA07", + "LCDC_DATA16", + "LCDC_DATA09", + "LCDC_DATA08", + "LCDC_DATA11", + "LCDC_DATA10", + "LCDC_DATA13", + "LCDC_DATA12", + "LCDC_DATA15", + "LCDC_DATA14", + "LCDC_DATA17", + "LCDC_DATA16", + "LCDC_DATA19", + "LCDC_DATA18", + "LCDC_DATA21", + "LCDC_DATA20", + "LCDC_DATA23", + "LCDC_DATA22", + ], + "st,stm32-dcmi-camera-fpu-330zh": [ + "SCL", + "SDA", + "RESET", + "PEN", + "VS", + "HS", + "PCLK", + "D7", + "D6", + "D5", + "D4", + "D3", + "D2", + "D1", + "D0", + ], } @@ -139,6 +215,7 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 flashes = [] rams = [] status_led = None + status_led_inverted = False path2chosen = {} chosen2path = {} usb_num_endpoint_pairs = 0 @@ -247,6 +324,7 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 props = led.props ioport = props["gpios"]._markers[1][2] num = int.from_bytes(props["gpios"].value[4:8], "big") + flags = int.from_bytes(props["gpios"].value[8:12], "big") if "label" in props: if (ioport, num) not in board_names: board_names[(ioport, num)] = [] @@ -257,6 +335,7 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 if "led0" in node2alias[led]: board_names[(ioport, num)].append("LED") status_led = (ioport, num) + status_led_inverted = flags & GPIO_ACTIVE_LOW board_names[(ioport, num)].extend(node2alias[led]) if "gpio-keys" in compatible: @@ -316,8 +395,12 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 header = board_dir / "mpconfigboard.h" if status_led: status_led = f"#define MICROPY_HW_LED_STATUS (&pin_{status_led})\n" + status_led_inverted = ( + f"#define MICROPY_HW_LED_STATUS_INVERTED ({'1' if status_led_inverted else '0'})\n" + ) else: status_led = "" + status_led_inverted = "" ram_list = [] ram_externs = [] max_size = 0 @@ -339,6 +422,7 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 #define MICROPY_HW_MCU_NAME "{soc_name}" #define CIRCUITPY_RAM_DEVICE_COUNT {len(rams)} {status_led} +{status_led_inverted} """ if not header.exists() or header.read_text() != new_header_content: header.write_text(new_header_content) @@ -384,8 +468,7 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); """ - if not board_c.exists() or new_board_c_content != board_c.read_text(): - board_c.write_text(new_board_c_content) + board_c.write_text(new_board_c_content) board_info["source_files"] = [board_c] board_info["cflags"] = ("-I", board_dir) board_info["flash_count"] = len(flashes) diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index f769d7dc6b819..bd693bb897ea8 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -14,7 +14,7 @@ CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_THREAD_STACK_INFO=y CONFIG_STACK_SENTINEL=y CONFIG_DEBUG_THREAD_INFO=y -CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_INFO=y CONFIG_USB_DEVICE_STACK=n diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index 01712e864e0db..3eb38f7bea63b 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -2,6 +2,6 @@ manifest: projects: - name: zephyr url: https://github.com/adafruit/zephyr - revision: circuitpython + revision: circuitpython-v4.3.0 clone-depth: 100 import: true From c5a28aa41e637c6bb32eed99693e5d9a1606e809 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 24 Nov 2025 23:29:18 -0500 Subject: [PATCH 217/517] hack_club_sprig: add board.SPI() for display and SD card use --- .../boards/hack_club_sprig/board.c | 19 ++++++++++--------- .../boards/hack_club_sprig/mpconfigboard.h | 8 ++++++++ .../raspberrypi/boards/hack_club_sprig/pins.c | 16 ++++++++++------ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/ports/raspberrypi/boards/hack_club_sprig/board.c b/ports/raspberrypi/boards/hack_club_sprig/board.c index 89b1b3b75b7bf..40711c554452e 100644 --- a/ports/raspberrypi/boards/hack_club_sprig/board.c +++ b/ports/raspberrypi/boards/hack_club_sprig/board.c @@ -10,7 +10,7 @@ #include "shared-bindings/fourwire/FourWire.h" #include "shared-module/displayio/__init__.h" #include "shared-module/displayio/mipi_constants.h" -#include "supervisor/shared/board.h" +#include "shared-bindings/board/__init__.h" // display init sequence from CircuitPython library https://github.com/adafruit/Adafruit_CircuitPython_ST7735R/blob/dfae353330cf051d1f31db9e4b681c8d70900cc5/adafruit_st7735r.py @@ -57,17 +57,14 @@ uint8_t display_init_sequence[] = { void board_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; - busio_spi_obj_t *spi = &bus->inline_bus; - common_hal_busio_spi_construct(spi, &pin_GPIO18, &pin_GPIO19, &pin_GPIO16, false); - common_hal_busio_spi_never_reset(spi); - bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO22, // DC - &pin_GPIO20, // CS - &pin_GPIO26, // RST + CIRCUITPY_BOARD_TFT_DC, + CIRCUITPY_BOARD_TFT_CS, + CIRCUITPY_BOARD_TFT_RESET, 30000000, 0, 0); @@ -92,7 +89,7 @@ void board_init(void) { MIPI_COMMAND_WRITE_MEMORY_START, // Write memory command display_init_sequence, sizeof(display_init_sequence), - &pin_GPIO17, // backlight pin + CIRCUITPY_BOARD_TFT_BACKLIGHT, NO_BRIGHTNESS_COMMAND, 1.0f, // brightness false, // single_byte_bounds @@ -104,4 +101,8 @@ void board_init(void) { 50000); // backlight pwm frequency } +void board_deinit(void) { + common_hal_displayio_release_displays(); +} + // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/hack_club_sprig/mpconfigboard.h b/ports/raspberrypi/boards/hack_club_sprig/mpconfigboard.h index 041aca6d45eb7..bfc0469a71178 100644 --- a/ports/raspberrypi/boards/hack_club_sprig/mpconfigboard.h +++ b/ports/raspberrypi/boards/hack_club_sprig/mpconfigboard.h @@ -10,3 +10,11 @@ #define MICROPY_HW_MCU_NAME "rp2040" #define MICROPY_HW_LED_STATUS (&pin_GPIO4) + +#define CIRCUITPY_BOARD_TFT_DC (&pin_GPIO22) +#define CIRCUITPY_BOARD_TFT_CS (&pin_GPIO20) +#define CIRCUITPY_BOARD_TFT_RESET (&pin_GPIO26) +#define CIRCUITPY_BOARD_TFT_BACKLIGHT (&pin_GPIO17) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO18, .mosi = &pin_GPIO19, .miso = &pin_GPIO16 }} diff --git a/ports/raspberrypi/boards/hack_club_sprig/pins.c b/ports/raspberrypi/boards/hack_club_sprig/pins.c index e5ce147839890..7802ef5466d1d 100644 --- a/ports/raspberrypi/boards/hack_club_sprig/pins.c +++ b/ports/raspberrypi/boards/hack_club_sprig/pins.c @@ -60,7 +60,6 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, - // Start Sprig-specific definitions { MP_ROM_QSTR(MP_QSTR_BLUE_LED), MP_ROM_PTR(&pin_GPIO4) }, @@ -79,18 +78,23 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_BUTTON_K), MP_ROM_PTR(&pin_GPIO14) }, { MP_ROM_QSTR(MP_QSTR_BUTTON_L), MP_ROM_PTR(&pin_GPIO15) }, - { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO16) }, - { MP_ROM_QSTR(MP_QSTR_TFT_LITE), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO19) }, - { MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_CARD_CS), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO22) }, - { MP_ROM_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(&pin_GPIO23) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_DC) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_CS) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_RESET) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_BACKLIGHT), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_BACKLIGHT) }, + { MP_ROM_QSTR(MP_QSTR_TFT_LITE), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_BACKLIGHT) }, + { MP_ROM_QSTR(MP_QSTR_WHITE_LED), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 26bd5af3dd1fda13507750a104a3ae3e17972548 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 2 Dec 2025 09:43:08 -0600 Subject: [PATCH 218/517] bitmaptools.replace_color() function --- shared-bindings/bitmaptools/__init__.c | 41 ++++++++++++++++++++++++++ shared-bindings/bitmaptools/__init__.h | 3 ++ shared-module/bitmaptools/__init__.c | 15 ++++++++++ 3 files changed, 59 insertions(+) diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 709e458211341..ba7bf7e3fa1e9 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -418,6 +418,46 @@ static mp_obj_t bitmaptools_alphablend(size_t n_args, const mp_obj_t *pos_args, } MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_alphablend_obj, 0, bitmaptools_alphablend); +//| def replace_color( +//| dest_bitmap: displayio.Bitmap, old_color: int, new_color: int +//| ) -> None: +//| """Replace any pixels of old_color with new_color in the dest_bitmap +//| +//| :param bitmap dest_bitmap: Destination bitmap that will be written into +//| :param int old_color: Bitmap palette index that will overwritten +//| :param int new_color: Bitmap palette index that will get put in the bitmap""" +//| ... +//| +//| +static mp_obj_t bitmaptools_obj_replace_color(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_dest_bitmap, ARG_old_color, ARG_new_color}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_old_color, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_new_color, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + + uint32_t old_color, new_color, color_depth; + old_color = args[ARG_old_color].u_int; + new_color = args[ARG_new_color].u_int; + + color_depth = (1 << destination->bits_per_value); + if (color_depth <= old_color || color_depth <= new_color) { + mp_raise_ValueError(MP_ERROR_TEXT("out of range of target")); + } + + common_hal_bitmaptools_replace_color(destination, old_color, new_color); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_replace_color_obj, 0, bitmaptools_obj_replace_color); + //| def fill_region( //| dest_bitmap: displayio.Bitmap, x1: int, y1: int, x2: int, y2: int, value: int //| ) -> None: @@ -1103,6 +1143,7 @@ static const mp_rom_map_elem_t bitmaptools_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_rotozoom), MP_ROM_PTR(&bitmaptools_rotozoom_obj) }, { MP_ROM_QSTR(MP_QSTR_arrayblit), MP_ROM_PTR(&bitmaptools_arrayblit_obj) }, { MP_ROM_QSTR(MP_QSTR_alphablend), MP_ROM_PTR(&bitmaptools_alphablend_obj) }, + { MP_ROM_QSTR(MP_QSTR_replace_color), MP_ROM_PTR(&bitmaptools_replace_color_obj) }, { MP_ROM_QSTR(MP_QSTR_fill_region), MP_ROM_PTR(&bitmaptools_fill_region_obj) }, { MP_ROM_QSTR(MP_QSTR_boundary_fill), MP_ROM_PTR(&bitmaptools_boundary_fill_obj) }, { MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) }, diff --git a/shared-bindings/bitmaptools/__init__.h b/shared-bindings/bitmaptools/__init__.h index 21116b8bd4276..f193ef6d95282 100644 --- a/shared-bindings/bitmaptools/__init__.h +++ b/shared-bindings/bitmaptools/__init__.h @@ -42,6 +42,9 @@ void common_hal_bitmaptools_fill_region(displayio_bitmap_t *destination, int16_t x2, int16_t y2, uint32_t value); +void common_hal_bitmaptools_replace_color(displayio_bitmap_t *destination, + uint32_t old_color, uint32_t new_color); + void common_hal_bitmaptools_boundary_fill(displayio_bitmap_t *destination, int16_t x, int16_t y, uint32_t fill_color_value, uint32_t replaced_color_value); diff --git a/shared-module/bitmaptools/__init__.c b/shared-module/bitmaptools/__init__.c index 8a41e387e513a..a4cc18529ccec 100644 --- a/shared-module/bitmaptools/__init__.c +++ b/shared-module/bitmaptools/__init__.c @@ -204,6 +204,21 @@ void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16 } } +void common_hal_bitmaptools_replace_color(displayio_bitmap_t *destination, + uint32_t old_color, + uint32_t new_color){ + + int16_t x, y; + for (x = 0; x < destination->width; x++) { + for (y = 0; y < destination->height; y++) { + uint32_t pixel_val = common_hal_displayio_bitmap_get_pixel(destination, x, y); + if (pixel_val == old_color) { + displayio_bitmap_write_pixel(destination, x, y, new_color); + } + } + } +} + void common_hal_bitmaptools_fill_region(displayio_bitmap_t *destination, int16_t x1, int16_t y1, int16_t x2, int16_t y2, From 579afb27da70ed7b74d0ef5ee01d46c6ec7b0e4b Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 2 Dec 2025 09:50:50 -0600 Subject: [PATCH 219/517] fix code format --- shared-module/bitmaptools/__init__.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shared-module/bitmaptools/__init__.c b/shared-module/bitmaptools/__init__.c index a4cc18529ccec..2f2b7aaf47583 100644 --- a/shared-module/bitmaptools/__init__.c +++ b/shared-module/bitmaptools/__init__.c @@ -206,16 +206,16 @@ void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16 void common_hal_bitmaptools_replace_color(displayio_bitmap_t *destination, uint32_t old_color, - uint32_t new_color){ + uint32_t new_color) { int16_t x, y; for (x = 0; x < destination->width; x++) { - for (y = 0; y < destination->height; y++) { - uint32_t pixel_val = common_hal_displayio_bitmap_get_pixel(destination, x, y); - if (pixel_val == old_color) { - displayio_bitmap_write_pixel(destination, x, y, new_color); + for (y = 0; y < destination->height; y++) { + uint32_t pixel_val = common_hal_displayio_bitmap_get_pixel(destination, x, y); + if (pixel_val == old_color) { + displayio_bitmap_write_pixel(destination, x, y, new_color); + } } - } } } From f6b91b72814a3c00bd599baaf5de50c2d72925ea Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 2 Dec 2025 12:07:01 -0600 Subject: [PATCH 220/517] simpler arg name, validate bitmap type, reuse error message, remove unneeded required arg default values. --- shared-bindings/bitmaptools/__init__.c | 76 +++++++++++++------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index ba7bf7e3fa1e9..f3aad1b67b046 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -185,8 +185,8 @@ static mp_obj_t bitmaptools_obj_rotozoom(size_t n_args, const mp_obj_t *pos_args ARG_angle, ARG_scale, ARG_skip_index}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, {MP_QSTR_ox, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->width / 2 {MP_QSTR_oy, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->height / 2 @@ -421,26 +421,26 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_alphablend_obj, 0, bitmaptools_alphablend //| def replace_color( //| dest_bitmap: displayio.Bitmap, old_color: int, new_color: int //| ) -> None: -//| """Replace any pixels of old_color with new_color in the dest_bitmap +//| """Replace any pixels of ``old_color`` with ``new_color`` in the ``bitmap`` //| -//| :param bitmap dest_bitmap: Destination bitmap that will be written into +//| :param displayio.Bitmap bitmap: Bitmap that will be changed //| :param int old_color: Bitmap palette index that will overwritten //| :param int new_color: Bitmap palette index that will get put in the bitmap""" //| ... //| //| static mp_obj_t bitmaptools_obj_replace_color(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum {ARG_dest_bitmap, ARG_old_color, ARG_new_color}; + enum {ARG_bitmap, ARG_old_color, ARG_new_color}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_old_color, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_new_color, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_old_color, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_new_color, MP_ARG_REQUIRED | MP_ARG_INT}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_bitmap)); uint32_t old_color, new_color, color_depth; old_color = args[ARG_old_color].u_int; @@ -448,7 +448,7 @@ static mp_obj_t bitmaptools_obj_replace_color(size_t n_args, const mp_obj_t *pos color_depth = (1 << destination->bits_per_value); if (color_depth <= old_color || color_depth <= new_color) { - mp_raise_ValueError(MP_ERROR_TEXT("out of range of target")); + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q out of range"), MP_QSTR_color); } common_hal_bitmaptools_replace_color(destination, old_color, new_color); @@ -478,9 +478,9 @@ static mp_obj_t bitmaptools_obj_fill_region(size_t n_args, const mp_obj_t *pos_a enum {ARG_dest_bitmap, ARGS_X1_Y1_X2_Y2, ARG_value}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, ALLOWED_ARGS_X1_Y1_X2_Y2(MP_ARG_REQUIRED, MP_ARG_REQUIRED), - {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -527,10 +527,10 @@ static mp_obj_t bitmaptools_obj_boundary_fill(size_t n_args, const mp_obj_t *pos enum {ARG_dest_bitmap, ARG_x, ARG_y, ARG_fill_color_value, ARG_replaced_color_value}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_fill_color_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_fill_color_value, MP_ARG_REQUIRED | MP_ARG_INT}, {MP_QSTR_replaced_color_value, MP_ARG_INT, {.u_int = INT_MAX} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -588,12 +588,12 @@ static mp_obj_t bitmaptools_obj_draw_line(size_t n_args, const mp_obj_t *pos_arg enum {ARG_dest_bitmap, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_value}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -675,10 +675,10 @@ static mp_obj_t bitmaptools_obj_draw_polygon(size_t n_args, const mp_obj_t *pos_ enum {ARG_dest_bitmap, ARG_xs, ARG_ys, ARG_value, ARG_close}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_xs, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_ys, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_xs, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_ys, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT}, {MP_QSTR_close, MP_ARG_BOOL, {.u_bool = true}}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -757,8 +757,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_polygon_obj, 0, bitmaptools_obj_draw static mp_obj_t bitmaptools_arrayblit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_bitmap, ARG_data, ARGS_X1_Y1_X2_Y2, ARG_skip_index }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, ALLOWED_ARGS_X1_Y1_X2_Y2(0, 0), { MP_QSTR_skip_index, MP_ARG_OBJ, {.u_obj = mp_const_none } }, }; @@ -822,9 +822,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_arrayblit_obj, 0, bitmaptools_arrayblit); static mp_obj_t bitmaptools_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_bitmap, ARG_file, ARG_bits_per_pixel, ARG_element_size, ARG_reverse_pixels_in_element, ARG_swap_bytes_in_element, ARG_reverse_rows }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_file, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_bits_per_pixel, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_file, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_bits_per_pixel, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_element_size, MP_ARG_INT, { .u_int = 1 } }, { MP_QSTR_reverse_pixels_in_element, MP_ARG_BOOL, { .u_bool = false } }, { MP_QSTR_swap_bytes_in_element, MP_ARG_BOOL, { .u_bool = false } }, @@ -913,9 +913,9 @@ MAKE_ENUM_TYPE(bitmaptools, DitherAlgorithm, bitmaptools_dither_algorithm); static mp_obj_t bitmaptools_dither(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_dest_bitmap, ARG_source_bitmap, ARG_source_colorspace, ARG_algorithm }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_source_colorspace, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_source_colorspace, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_algorithm, MP_ARG_OBJ, { .u_obj = MP_ROM_PTR((void *)&dither_algorithm_Atkinson_obj) } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -1078,10 +1078,10 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_circle_obj, 0, bitmaptools_obj_draw_ static mp_obj_t bitmaptools_obj_blit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum {ARG_destination, ARG_source, ARG_x, ARG_y, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_skip_source_index, ARG_skip_dest_index}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, - {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, ALLOWED_ARGS_X1_Y1_X2_Y2(0, 0), {MP_QSTR_skip_source_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, {MP_QSTR_skip_dest_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, From 7b429baa0dd36e3e552e805481820f42b6219f6c Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 2 Dec 2025 14:53:44 -0600 Subject: [PATCH 221/517] -Wno-missing-field-initializers on unix port make --- ports/unix/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index bb1f6a4740a35..6aee1a939b306 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -46,7 +46,7 @@ INC += -I$(BUILD) # compiler settings CWARN = -Wall -Werror -CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion +CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Wno-missing-field-initializers CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) # Force the use of 64-bits for file sizes in C library functions on 32-bit platforms. From 88619f47353ce3b7c2618ddb209d81a7bad9a3a7 Mon Sep 17 00:00:00 2001 From: noqman Date: Wed, 3 Dec 2025 16:59:42 +0800 Subject: [PATCH 222/517] Initial commit --- .../boards/cytron_edu_pico_v2_2w/board.c | 9 ++ .../cytron_edu_pico_v2_2w/mpconfigboard.h | 21 +++++ .../cytron_edu_pico_v2_2w/mpconfigboard.mk | 53 ++++++++++++ .../pico-sdk-configboard.h | 9 ++ .../boards/cytron_edu_pico_v2_2w/pins.c | 86 +++++++++++++++++++ 5 files changed, 178 insertions(+) create mode 100644 ports/raspberrypi/boards/cytron_edu_pico_v2_2w/board.c create mode 100644 ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.h create mode 100644 ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk create mode 100644 ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pico-sdk-configboard.h create mode 100644 ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pins.c diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/board.c b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/board.c new file mode 100644 index 0000000000000..e6a868ab21226 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.h b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.h new file mode 100644 index 0000000000000..8eab7e5a52557 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.h @@ -0,0 +1,21 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Cytron EDU PICO 2" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1) + +#define MICROPY_HW_LED_STATUS (&pin_CYW0) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}} + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO18, .mosi = &pin_GPIO19, .miso = &pin_GPIO16}} diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk new file mode 100644 index 0000000000000..72d20ea7e055e --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk @@ -0,0 +1,53 @@ +USB_VID = 0x2E8A +USB_PID = 0x107E +USB_PRODUCT = "Cytron EDU PICO 2 for Pico 2W" +USB_MANUFACTURER = "Cytron" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" + +CIRCUITPY__EVE = 1 + +CIRCUITPY_CYW43 = 1 +CIRCUITPY_SSL = 1 +CIRCUITPY_HASHLIB = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_MDNS = 1 +CIRCUITPY_SOCKETPOOL = 1 +CIRCUITPY_WIFI = 1 + +CFLAGS += \ + -DCYW43_PIN_WL_DYNAMIC=0 \ + -DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \ + -DCYW43_DEFAULT_PIN_WL_REG_ON=23 \ + -DCYW43_DEFAULT_PIN_WL_CLOCK=29 \ + -DCYW43_DEFAULT_PIN_WL_DATA_IN=24 \ + -DCYW43_DEFAULT_PIN_WL_DATA_OUT=24 \ + -DCYW43_DEFAULT_PIN_WL_CS=25 \ + -DCYW43_WL_GPIO_COUNT=3 \ + -DCYW43_WL_GPIO_LED_PIN=0 + -DCYW43_PIO_CLOCK_DIV_INT=3 + +# Must be accompanied by a linker script change +CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' + +# The default is -O3. Change to -O2 because the build was overflowing. +OPTIMIZATION_FLAGS = -O2 + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Motor +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SimpleIO +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_framebuf +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ImageLoad +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_AHTx0 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pico-sdk-configboard.h b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pico-sdk-configboard.h new file mode 100644 index 0000000000000..110195b779498 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pins.c b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pins.c new file mode 100644 index 0000000000000..83b93e7268596 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pins.c @@ -0,0 +1,86 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + + // Motor Controls + { MP_ROM_QSTR(MP_QSTR_M1A), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_M1B), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_M2A), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_M2B), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_LOG_SWITCH), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + + // SPI Pins for SD Card + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_USB_RELAY), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_SMPS_MODE), MP_ROM_PTR(&pin_CYW1) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_CYW0) }, + + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_CYW2) }, + + { MP_ROM_QSTR(MP_QSTR_GP26_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_GP27_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + + { MP_ROM_QSTR(MP_QSTR_USB_HOST_DATA_PLUS), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_USB_HOST_DATA_MINUS), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_USB_HOST_POWER), MP_ROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_GP28_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SD_SPI), MP_ROM_PTR(&board_spi_obj) }, + + + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From da50dfa686401f3b0c13bb879844ca3d36c21705 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 2 Dec 2025 09:52:57 -0800 Subject: [PATCH 223/517] Add I2C support to the Zephyr builds You cannot instantiate `zephyr_i2c.I2C()` because they are fixed by the board's device tree. All busses with status = "okay" are added to board with all of their labels as names. So, I2C() may be the same as I2C1() and ARDUINO_I2C(). Part of #9903. --- locale/circuitpython.pot | 14 +- ports/zephyr-cp/bindings/zephyr_i2c/I2C.c | 378 ++++++++++++++++++ ports/zephyr-cp/bindings/zephyr_i2c/I2C.h | 32 ++ .../zephyr-cp/bindings/zephyr_i2c/__init__.c | 27 ++ ports/zephyr-cp/boards/board_aliases.cmake | 2 + .../nordic/nrf5340dk/autogen_board_info.toml | 3 +- .../nordic/nrf54h20dk/autogen_board_info.toml | 116 ++++++ .../nordic/nrf54h20dk/circuitpython.toml | 1 + .../nordic/nrf54l15dk/autogen_board_info.toml | 3 +- .../nordic/nrf7002dk/autogen_board_info.toml | 3 +- .../boards/nrf54h20dk_nrf54h20_cpuapp.conf | 1 + .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 45 +++ .../zephyr-cp/boards/nucleo_n657x0_q.overlay | 0 .../renesas/ek_ra6m5/autogen_board_info.toml | 3 +- .../renesas/ek_ra8d1/autogen_board_info.toml | 3 +- .../nucleo_n657x0_q/autogen_board_info.toml | 116 ++++++ .../st/nucleo_n657x0_q/circuitpython.toml | 1 + .../nucleo_u575zi_q/autogen_board_info.toml | 3 +- .../st/stm32h7b3i_dk/autogen_board_info.toml | 3 +- ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c | 142 ++++--- ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h | 12 +- .../zephyr-cp/common-hal/zephyr_serial/UART.c | 6 + .../zephyr-cp/common-hal/zephyr_serial/UART.h | 2 + .../zephyr-cp/cptools/build_circuitpython.py | 12 +- ports/zephyr-cp/cptools/zephyr2cp.py | 123 +++++- ports/zephyr-cp/prj.conf | 2 + ports/zephyr-cp/supervisor/flash.c | 4 + 27 files changed, 975 insertions(+), 82 deletions(-) create mode 100644 ports/zephyr-cp/bindings/zephyr_i2c/I2C.c create mode 100644 ports/zephyr-cp/bindings/zephyr_i2c/I2C.h create mode 100644 ports/zephyr-cp/bindings/zephyr_i2c/__init__.c create mode 100644 ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf create mode 100644 ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 ports/zephyr-cp/boards/nucleo_n657x0_q.overlay create mode 100644 ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 35fc94513a547..81074f3f80464 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -111,6 +111,7 @@ msgid "%q in %q must be of type %q, not %q" msgstr "" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -244,7 +245,8 @@ msgstr "" #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "" @@ -834,10 +836,6 @@ msgstr "" msgid "Clock unit in use" msgstr "" -#: ports/espressif/common-hal/mipidsi/Display.c -msgid "Color depth must be 16 or 24" -msgstr "" - #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -1119,8 +1117,9 @@ msgid "" "Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" msgstr "" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "" @@ -1271,6 +1270,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c new file mode 100644 index 0000000000000..08ce9e966a669 --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c @@ -0,0 +1,378 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/zephyr_i2c/I2C.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared/runtime/buffer_helper.h" +#include "py/binary.h" +#include "py/mperrno.h" +#include "py/runtime.h" + +#include +#include + +//| class I2C: +//| """Two wire serial protocol +//| +//| I2C is a two-wire protocol for communicating between devices. At the +//| physical level it consists of 2 wires: SCL and SDA, the clock and data +//| lines respectively. +//| +//| .. class:: I2C() +//| +//| Cannot be instantiated directly. Instead singletons are created using the +//| `board` aliases that match the device tree labels. `board` may list multiple +//| aliases for a single device. For example, `board.I2C1` and `board.ARDUINO_I2C` +//| may both refer to the same device. +//| """ +//| + +static zephyr_i2c_i2c_obj_t *native_i2c(mp_obj_t self_in) { + mp_check_self(mp_obj_is_type(self_in, &zephyr_i2c_i2c_type)); + return MP_OBJ_TO_PTR(self_in); +} + +static void check_for_deinit(zephyr_i2c_i2c_obj_t *self) { + if (common_hal_zephyr_i2c_i2c_deinited(self)) { + raise_deinited_error(); + } +} + +static void check_lock(zephyr_i2c_i2c_obj_t *self) { + if (!common_hal_zephyr_i2c_i2c_has_lock(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Function requires lock")); + } +} + +//| def deinit(self) -> None: +//| """Releases control of the underlying hardware so other classes can use it.""" +//| ... +static mp_obj_t zephyr_i2c_i2c_obj_deinit(mp_obj_t self_in) { + zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); + common_hal_zephyr_i2c_i2c_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c_deinit_obj, zephyr_i2c_i2c_obj_deinit); + +//| def __enter__(self) -> I2C: +//| """No-op used in Context Managers.""" +//| ... +static mp_obj_t zephyr_i2c_i2c_obj___enter__(mp_obj_t self_in) { + zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); + check_for_deinit(self); + return self_in; +} +static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c___enter___obj, zephyr_i2c_i2c_obj___enter__); + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware on context exit.""" +//| ... +static mp_obj_t zephyr_i2c_i2c_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_zephyr_i2c_i2c_deinit(args[0]); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_i2c_i2c___exit___obj, 4, 4, zephyr_i2c_i2c_obj___exit__); + +//| def probe(self, address: int) -> bool: +//| """Check if a device responds at the address. +//| +//| :param int address: The 7-bit device address +//| :return: True if the device responds, False otherwise +//| :rtype: bool +//| """ +//| ... +static mp_obj_t zephyr_i2c_i2c_probe(mp_obj_t self_in, mp_obj_t addr_obj) { + zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); + check_for_deinit(self); + check_lock(self); + + mp_int_t addr = mp_obj_get_int(addr_obj); + return mp_obj_new_bool(common_hal_zephyr_i2c_i2c_probe(self, addr)); +} +static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_i2c_i2c_probe_obj, zephyr_i2c_i2c_probe); + +//| def scan(self) -> List[int]: +//| """Scan all I2C addresses between 0x08 and 0x77 inclusive and return a +//| list of those that respond. +//| +//| :return: List of device addresses +//| :rtype: list +//| """ +//| ... +static mp_obj_t zephyr_i2c_i2c_scan(mp_obj_t self_in) { + zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); + check_for_deinit(self); + check_lock(self); + + mp_obj_t list = mp_obj_new_list(0, NULL); + // 7-bit addresses 0b0000xxx and 0b1111xxx are reserved + for (uint8_t addr = 0x08; addr <= 0x77; addr++) { + if (common_hal_zephyr_i2c_i2c_probe(self, addr)) { + mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr)); + } + } + return list; +} +static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c_scan_obj, zephyr_i2c_i2c_scan); + +//| def try_lock(self) -> bool: +//| """Attempts to grab the I2C lock. Returns True on success. +//| +//| :return: True when lock has been grabbed +//| :rtype: bool +//| """ +//| ... +static mp_obj_t zephyr_i2c_i2c_obj_try_lock(mp_obj_t self_in) { + zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_zephyr_i2c_i2c_try_lock(self)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c_try_lock_obj, zephyr_i2c_i2c_obj_try_lock); + +//| def unlock(self) -> None: +//| """Releases the I2C lock.""" +//| ... +static mp_obj_t zephyr_i2c_i2c_obj_unlock(mp_obj_t self_in) { + zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); + check_for_deinit(self); + common_hal_zephyr_i2c_i2c_unlock(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c_unlock_obj, zephyr_i2c_i2c_obj_unlock); + +//| import sys +//| +//| def readfrom_into( +//| self, address: int, buffer: WriteableBuffer, *, start: int = 0, end: int = sys.maxsize +//| ) -> None: +//| """Read into ``buffer`` from the device selected by ``address``. +//| At least one byte must be read. +//| +//| If ``start`` or ``end`` is provided, then the buffer will be sliced +//| as if ``buffer[start:end]`` were passed, but without copying the data. +//| The number of bytes read will be the length of ``buffer[start:end]``. +//| +//| :param int address: 7-bit device address +//| :param ~circuitpython_typing.WriteableBuffer buffer: buffer to write into +//| :param int start: beginning of buffer slice +//| :param int end: end of buffer slice; if not specified, use ``len(buffer)`` +//| """ +//| ... +static mp_obj_t zephyr_i2c_i2c_readfrom_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_address, ARG_buffer, ARG_start, ARG_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + }; + zephyr_i2c_i2c_obj_t *self = native_i2c(pos_args[0]); + check_for_deinit(self); + check_lock(self); + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); + + // Compute bounds in terms of elements, not bytes. + int stride_in_bytes = mp_binary_get_size('@', bufinfo.typecode, NULL); + size_t length = bufinfo.len / stride_in_bytes; + int32_t start = args[ARG_start].u_int; + const int32_t end = args[ARG_end].u_int; + normalize_buffer_bounds(&start, end, &length); + mp_arg_validate_length_min(length, 1, MP_QSTR_buffer); + + // Treat start and length in terms of bytes from now on. + start *= stride_in_bytes; + length *= stride_in_bytes; + + uint8_t status = common_hal_zephyr_i2c_i2c_read(self, args[ARG_address].u_int, + ((uint8_t *)bufinfo.buf) + start, length); + + if (status != 0) { + mp_raise_OSError(status); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_i2c_i2c_readfrom_into_obj, 3, zephyr_i2c_i2c_readfrom_into); + +//| import sys +//| +//| def writeto( +//| self, address: int, buffer: ReadableBuffer, *, start: int = 0, end: int = sys.maxsize +//| ) -> None: +//| """Write the bytes from ``buffer`` to the device selected by ``address`` and +//| then transmit a stop bit. +//| +//| If ``start`` or ``end`` is provided, then the buffer will be sliced +//| as if ``buffer[start:end]`` were passed, but without copying the data. +//| The number of bytes written will be the length of ``buffer[start:end]``. +//| +//| Writing a buffer or slice of length zero is permitted, as it can be used +//| to poll for the existence of a device. +//| +//| :param int address: 7-bit device address +//| :param ~circuitpython_typing.ReadableBuffer buffer: buffer containing the bytes to write +//| :param int start: beginning of buffer slice +//| :param int end: end of buffer slice; if not specified, use ``len(buffer)`` +//| """ +//| ... +static mp_obj_t zephyr_i2c_i2c_writeto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_address, ARG_buffer, ARG_start, ARG_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + }; + zephyr_i2c_i2c_obj_t *self = native_i2c(pos_args[0]); + check_for_deinit(self); + check_lock(self); + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // get the buffer to write the data from + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); + int stride_in_bytes = mp_binary_get_size('@', bufinfo.typecode, NULL); + + // Compute bounds in terms of elements, not bytes. + size_t length = bufinfo.len / stride_in_bytes; + int32_t start = args[ARG_start].u_int; + const int32_t end = args[ARG_end].u_int; + normalize_buffer_bounds(&start, end, &length); + + // Treat start and length in terms of bytes from now on. + start *= stride_in_bytes; + length *= stride_in_bytes; + + uint8_t status = common_hal_zephyr_i2c_i2c_write(self, args[ARG_address].u_int, + ((uint8_t *)bufinfo.buf) + start, length); + + if (status != 0) { + mp_raise_OSError(status); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_i2c_i2c_writeto_obj, 3, zephyr_i2c_i2c_writeto); + +//| import sys +//| +//| def writeto_then_readfrom( +//| self, +//| address: int, +//| out_buffer: ReadableBuffer, +//| in_buffer: WriteableBuffer, +//| *, +//| out_start: int = 0, +//| out_end: int = None, +//| in_start: int = 0, +//| in_end: int = None +//| ) -> None: +//| """Write the bytes from ``out_buffer`` to the device selected by ``address``, generate +//| no stop bit, generate a repeated start and read into ``in_buffer``. ``out_buffer`` and +//| ``in_buffer`` can be the same buffer because they are used sequentially. +//| +//| If ``out_start`` or ``out_end`` is provided, then the buffer will be sliced +//| as if ``out_buffer[out_start:out_end]`` were passed, but without copying the data. +//| The number of bytes written will be the length of ``out_buffer[start:end]``. +//| +//| If ``in_start`` or ``in_end`` is provided, then the input buffer will be sliced +//| as if ``in_buffer[in_start:in_end]`` were passed, +//| The number of bytes read will be the length of ``out_buffer[in_start:in_end]``. +//| +//| :param int address: 7-bit device address +//| :param ~circuitpython_typing.ReadableBuffer out_buffer: buffer containing the bytes to write +//| :param ~circuitpython_typing.WriteableBuffer in_buffer: buffer to write into +//| :param int out_start: beginning of ``out_buffer`` slice +//| :param int out_end: end of ``out_buffer`` slice; if not specified, use ``len(out_buffer)`` +//| :param int in_start: beginning of ``in_buffer`` slice +//| :param int in_end: end of ``in_buffer`` slice; if not specified, use ``len(in_buffer)`` +//| """ +//| ... +static mp_obj_t zephyr_i2c_i2c_writeto_then_readfrom(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_address, ARG_out_buffer, ARG_in_buffer, ARG_out_start, ARG_out_end, ARG_in_start, ARG_in_end }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_out_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_in_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_out_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_out_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + { MP_QSTR_in_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_in_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, + }; + zephyr_i2c_i2c_obj_t *self = native_i2c(pos_args[0]); + check_for_deinit(self); + check_lock(self); + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_buffer_info_t out_bufinfo; + mp_get_buffer_raise(args[ARG_out_buffer].u_obj, &out_bufinfo, MP_BUFFER_READ); + int out_stride_in_bytes = mp_binary_get_size('@', out_bufinfo.typecode, NULL); + size_t out_length = out_bufinfo.len / out_stride_in_bytes; + int32_t out_start = args[ARG_out_start].u_int; + const int32_t out_end = args[ARG_out_end].u_int; + normalize_buffer_bounds(&out_start, out_end, &out_length); + + mp_buffer_info_t in_bufinfo; + mp_get_buffer_raise(args[ARG_in_buffer].u_obj, &in_bufinfo, MP_BUFFER_WRITE); + int in_stride_in_bytes = mp_binary_get_size('@', in_bufinfo.typecode, NULL); + size_t in_length = in_bufinfo.len / in_stride_in_bytes; + int32_t in_start = args[ARG_in_start].u_int; + const int32_t in_end = args[ARG_in_end].u_int; + normalize_buffer_bounds(&in_start, in_end, &in_length); + mp_arg_validate_length_min(in_length, 1, MP_QSTR_out_buffer); + + // Treat start and length in terms of bytes from now on. + out_start *= out_stride_in_bytes; + out_length *= out_stride_in_bytes; + in_start *= in_stride_in_bytes; + in_length *= in_stride_in_bytes; + + uint8_t status = common_hal_zephyr_i2c_i2c_write_read(self, args[ARG_address].u_int, + ((uint8_t *)out_bufinfo.buf) + out_start, out_length, + ((uint8_t *)in_bufinfo.buf) + in_start, in_length); + + if (status != 0) { + mp_raise_OSError(status); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_i2c_i2c_writeto_then_readfrom_obj, 4, zephyr_i2c_i2c_writeto_then_readfrom); + +static const mp_rom_map_elem_t zephyr_i2c_i2c_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&zephyr_i2c_i2c_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&zephyr_i2c_i2c_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&zephyr_i2c_i2c___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&zephyr_i2c_i2c___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_probe), MP_ROM_PTR(&zephyr_i2c_i2c_probe_obj) }, + { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&zephyr_i2c_i2c_scan_obj) }, + + { MP_ROM_QSTR(MP_QSTR_try_lock), MP_ROM_PTR(&zephyr_i2c_i2c_try_lock_obj) }, + { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&zephyr_i2c_i2c_unlock_obj) }, + + { MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&zephyr_i2c_i2c_readfrom_into_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&zephyr_i2c_i2c_writeto_obj) }, + { MP_ROM_QSTR(MP_QSTR_writeto_then_readfrom), MP_ROM_PTR(&zephyr_i2c_i2c_writeto_then_readfrom_obj) }, +}; +static MP_DEFINE_CONST_DICT(zephyr_i2c_i2c_locals_dict, zephyr_i2c_i2c_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + zephyr_i2c_i2c_type, + MP_QSTR_I2C, + MP_TYPE_FLAG_NONE, + locals_dict, &zephyr_i2c_i2c_locals_dict + ); diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.h b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.h new file mode 100644 index 0000000000000..7f7eecbea6c62 --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.h @@ -0,0 +1,32 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "common-hal/zephyr_i2c/I2C.h" + +extern const mp_obj_type_t zephyr_i2c_i2c_type; + +// Check if the I2C object has been deinitialized +bool common_hal_zephyr_i2c_i2c_deinited(zephyr_i2c_i2c_obj_t *self); + +// Deinitialize the I2C bus +void common_hal_zephyr_i2c_i2c_deinit(zephyr_i2c_i2c_obj_t *self); + +// Locking functions +bool common_hal_zephyr_i2c_i2c_try_lock(zephyr_i2c_i2c_obj_t *self); +bool common_hal_zephyr_i2c_i2c_has_lock(zephyr_i2c_i2c_obj_t *self); +void common_hal_zephyr_i2c_i2c_unlock(zephyr_i2c_i2c_obj_t *self); + +// Device communication functions +bool common_hal_zephyr_i2c_i2c_probe(zephyr_i2c_i2c_obj_t *self, uint8_t addr); +uint8_t common_hal_zephyr_i2c_i2c_write(zephyr_i2c_i2c_obj_t *self, uint16_t address, + const uint8_t *data, size_t len); +uint8_t common_hal_zephyr_i2c_i2c_read(zephyr_i2c_i2c_obj_t *self, uint16_t address, + uint8_t *data, size_t len); +uint8_t common_hal_zephyr_i2c_i2c_write_read(zephyr_i2c_i2c_obj_t *self, uint16_t address, + uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len); diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/__init__.c b/ports/zephyr-cp/bindings/zephyr_i2c/__init__.c new file mode 100644 index 0000000000000..3274dfdb74099 --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_i2c/__init__.c @@ -0,0 +1,27 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/runtime.h" +#include "bindings/zephyr_i2c/I2C.h" + +//| """Zephyr I2C driver for fixed I2C busses. +//| +//| This module provides access to I2C busses using Zephyr device labels.""" + +static const mp_rom_map_elem_t zephyr_i2c_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr_i2c) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&zephyr_i2c_i2c_type) }, +}; + +static MP_DEFINE_CONST_DICT(zephyr_i2c_module_globals, zephyr_i2c_module_globals_table); + +const mp_obj_module_t zephyr_i2c_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&zephyr_i2c_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_zephyr_i2c, zephyr_i2c_module); diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index b8df9fb87f5b8..bb400f188dbe9 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -2,7 +2,9 @@ set(pca10056_BOARD_ALIAS nrf52840dk/nrf52840) set(renesas_ek_ra6m5_BOARD_ALIAS ek_ra6m5) set(renesas_ek_ra8d1_BOARD_ALIAS ek_ra8d1) set(nordic_nrf54l15dk_BOARD_ALIAS nrf54l15dk/nrf54l15/cpuapp) +set(nordic_nrf54h20dk_BOARD_ALIAS nrf54h20dk/nrf54h20/cpuapp) set(nordic_nrf5340dk_BOARD_ALIAS nrf5340dk/nrf5340/cpuapp) set(nordic_nrf7002dk_BOARD_ALIAS nrf7002dk/nrf5340/cpuapp) set(st_stm32h7b3i_dk_BOARD_ALIAS stm32h7b3i_dk) set(st_nucleo_u575zi_q_BOARD_ALIAS nucleo_u575zi_q/stm32u575xx) +set(st_nucleo_n657x0_q_BOARD_ALIAS nucleo_n657x0_q/stm32n657xx) diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 35966d6f9e2c6..c59e03c323eb1 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -110,6 +110,7 @@ vectorio = false warnings = false watchdog = false wifi = false +zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true +zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml new file mode 100644 index 0000000000000..d4017cae4dad0 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -0,0 +1,116 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Nordic Semiconductor nRF54H20 DK" + +[modules] +__future__ = false +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = false +bitmaptools = false +bitops = false +board = false +busdisplay = false +busio = false +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = false +dotclockframebuffer = false +dualbank = false +epaperdisplay = false +floppyio = false +fontio = false +fourwire = false +framebufferio = false +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = false +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = false +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = false +sdioio = false +sharpdisplay = false +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = false +synthio = false +terminalio = false +tilepalettemapper = false +time = true +touchio = false +traceback = false +uheap = false +usb = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = false +warnings = false +watchdog = false +wifi = false +zephyr_i2c = true # Zephyr board has zephyr_i2c +zephyr_kernel = false +zephyr_serial = true # Zephyr board has zephyr_serial +zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index e3b6cc2248f2e..dbffa5944d96c 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -110,6 +110,7 @@ vectorio = false warnings = false watchdog = false wifi = false +zephyr_i2c = false zephyr_kernel = false -zephyr_serial = true +zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 328105d715059..7a3ea9267c9e9 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -110,6 +110,7 @@ vectorio = false warnings = false watchdog = false wifi = true # Zephyr board has wifi +zephyr_i2c = false zephyr_kernel = false -zephyr_serial = true +zephyr_serial = false zlib = false diff --git a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf new file mode 100644 index 0000000000000..f7443ecfa33d4 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -0,0 +1 @@ +CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE=4096 diff --git a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 0000000000000..0d90c87931b61 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,45 @@ +&gpio6 { + status = "okay"; + zephyr,pm-device-runtime-auto; +}; + +&exmif { + status = "okay"; + zephyr,pm-device-runtime-auto; +}; + +&mx25uw63 { + status = "okay"; +}; + +/* + * SDA = P2.11 + * SCL = P2.10 + */ + +&pinctrl { + i2c130_default: i2c130_default { + group1 { + psels = , + ; + }; + }; + + i2c130_sleep: i2c130_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +&i2c130 { + clock-frequency = ; + pinctrl-0 = <&i2c130_default>; + pinctrl-1 = <&i2c130_sleep>; + pinctrl-names = "default", "sleep"; + zephyr,concat-buf-size = <256>; + memory-regions = <&cpuapp_dma_region>; + status = "okay"; +}; diff --git a/ports/zephyr-cp/boards/nucleo_n657x0_q.overlay b/ports/zephyr-cp/boards/nucleo_n657x0_q.overlay new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 2bc6e3e03a249..8c522296da6e3 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -110,6 +110,7 @@ vectorio = false warnings = false watchdog = false wifi = false +zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true +zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 2a2e16fd16afe..5c79b0e962efd 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -110,6 +110,7 @@ vectorio = false warnings = false watchdog = false wifi = false +zephyr_i2c = false zephyr_kernel = false -zephyr_serial = true +zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml new file mode 100644 index 0000000000000..7a5beb79e442c --- /dev/null +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -0,0 +1,116 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "STMicroelectronics Nucleo N657X0-Q" + +[modules] +__future__ = false +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = false +bitmaptools = false +bitops = false +board = false +busdisplay = false +busio = false +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = false +dotclockframebuffer = false +dualbank = false +epaperdisplay = false +floppyio = false +fontio = false +fourwire = false +framebufferio = false +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = false +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = false +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = false +sdioio = false +sharpdisplay = false +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = false +synthio = false +terminalio = false +tilepalettemapper = false +time = true +touchio = false +traceback = false +uheap = false +usb = false +usb_cdc = false # No TinyUSB settings for stm32n657xx +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = false +warnings = false +watchdog = false +wifi = false +zephyr_i2c = true # Zephyr board has zephyr_i2c +zephyr_kernel = false +zephyr_serial = true # Zephyr board has zephyr_serial +zlib = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml new file mode 100644 index 0000000000000..83e6bcd39c4f9 --- /dev/null +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 5042eb972b143..a3183f180aed6 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -110,6 +110,7 @@ vectorio = false warnings = false watchdog = false wifi = false +zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true +zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index cfe48a90ceea4..bf6a282874349 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -110,6 +110,7 @@ vectorio = false warnings = false watchdog = false wifi = false +zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true +zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c b/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c index d5bb0d4466912..12ce95c282d1c 100644 --- a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c +++ b/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c @@ -1,92 +1,134 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries -// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec -// SPDX-FileCopyrightText: Copyright (c) 2017 hathach -// SPDX-FileCopyrightText: Copyright (c) 2016 Sandeep Mistry All right reserved. +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT -#include "shared-bindings/busio/I2C.h" -#include "shared-bindings/microcontroller/__init__.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/tick.h" +#include "bindings/zephyr_i2c/I2C.h" #include "py/mperrno.h" #include "py/runtime.h" +#include +#include +#include -void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { - // never_reset_pin_number(self->scl_pin_number); - // never_reset_pin_number(self->sda_pin_number); +mp_obj_t zephyr_i2c_i2c_zephyr_init(zephyr_i2c_i2c_obj_t *self, const struct device *i2c_device) { + self->base.type = &zephyr_i2c_i2c_type; + self->i2c_device = i2c_device; + k_mutex_init(&self->mutex); + return MP_OBJ_FROM_PTR(self); } -void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) { - -} - -bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { - // return self->sda_pin_number == NO_PIN; - return true; +bool common_hal_zephyr_i2c_i2c_deinited(zephyr_i2c_i2c_obj_t *self) { + // Always leave it active + return false; } -void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { - if (common_hal_busio_i2c_deinited(self)) { +void common_hal_zephyr_i2c_i2c_deinit(zephyr_i2c_i2c_obj_t *self) { + if (common_hal_zephyr_i2c_i2c_deinited(self)) { return; } - - // nrfx_twim_uninit(&self->twim_peripheral->twim); - - // reset_pin_number(self->sda_pin_number); - // reset_pin_number(self->scl_pin_number); - - // self->twim_peripheral->in_use = false; - // common_hal_busio_i2c_mark_deinit(self); -} - -void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { - // self->sda_pin_number = NO_PIN; + // Always leave it active } -// nrfx_twim_tx doesn't support 0-length data so we fall back to the hal API -bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { - bool found = true; +bool common_hal_zephyr_i2c_i2c_probe(zephyr_i2c_i2c_obj_t *self, uint8_t addr) { + if (common_hal_zephyr_i2c_i2c_deinited(self)) { + return false; + } - return found; + // Try a zero-length write to probe for device + uint8_t dummy; + int ret = i2c_write(self->i2c_device, &dummy, 0, addr); + return ret == 0; } -bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { - if (common_hal_busio_i2c_deinited(self)) { +bool common_hal_zephyr_i2c_i2c_try_lock(zephyr_i2c_i2c_obj_t *self) { + if (common_hal_zephyr_i2c_i2c_deinited(self)) { return false; } - bool grabbed_lock = false; - return grabbed_lock; + + self->has_lock = k_mutex_lock(&self->mutex, K_NO_WAIT) == 0; + return self->has_lock; } -bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { +bool common_hal_zephyr_i2c_i2c_has_lock(zephyr_i2c_i2c_obj_t *self) { return self->has_lock; } -void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { - self->has_lock = false; +void common_hal_zephyr_i2c_i2c_unlock(zephyr_i2c_i2c_obj_t *self) { + k_mutex_unlock(&self->mutex); } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { +uint8_t common_hal_zephyr_i2c_i2c_write(zephyr_i2c_i2c_obj_t *self, uint16_t addr, + const uint8_t *data, size_t len) { + + if (common_hal_zephyr_i2c_i2c_deinited(self)) { + return MP_EIO; + } + + int ret = i2c_write(self->i2c_device, data, len, addr); + if (ret != 0) { + // Map Zephyr error codes to errno + if (ret == -ENOTSUP) { + return MP_EOPNOTSUPP; + } else if (ret == -EIO || ret == -ENXIO) { + return MP_EIO; + } else if (ret == -EBUSY) { + return MP_EBUSY; + } + return MP_EIO; + } + return 0; } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { +uint8_t common_hal_zephyr_i2c_i2c_read(zephyr_i2c_i2c_obj_t *self, uint16_t addr, + uint8_t *data, size_t len) { + + if (common_hal_zephyr_i2c_i2c_deinited(self)) { + return MP_EIO; + } + if (len == 0) { return 0; } + int ret = i2c_read(self->i2c_device, data, len, addr); + if (ret != 0) { + // Map Zephyr error codes to errno + if (ret == -ENOTSUP) { + return MP_EOPNOTSUPP; + } else if (ret == -EIO || ret == -ENXIO) { + return MP_EIO; + } else if (ret == -EBUSY) { + return MP_EBUSY; + } + return MP_EIO; + } + + return 0; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +uint8_t common_hal_zephyr_i2c_i2c_write_read(zephyr_i2c_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); - if (result != 0) { - return result; + + if (common_hal_zephyr_i2c_i2c_deinited(self)) { + return MP_EIO; } - return common_hal_busio_i2c_read(self, addr, in_data, in_len); + // Use i2c_write_read for combined transaction with repeated start + int ret = i2c_write_read(self->i2c_device, addr, out_data, out_len, in_data, in_len); + if (ret != 0) { + // Map Zephyr error codes to errno + if (ret == -ENOTSUP) { + return MP_EOPNOTSUPP; + } else if (ret == -EIO || ret == -ENXIO) { + return MP_EIO; + } else if (ret == -EBUSY) { + return MP_EBUSY; + } + return MP_EIO; + } + + return 0; } diff --git a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h b/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h index 22e9251b3f11a..0265511e20894 100644 --- a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h +++ b/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h @@ -1,17 +1,19 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT #pragma once #include "py/obj.h" +#include typedef struct { mp_obj_base_t base; - // twim_peripheral_t *twim_peripheral; + const struct device *i2c_device; + struct k_mutex mutex; bool has_lock; - uint8_t scl_pin_number; - uint8_t sda_pin_number; -} busio_i2c_obj_t; +} zephyr_i2c_i2c_obj_t; + +mp_obj_t zephyr_i2c_i2c_zephyr_init(zephyr_i2c_i2c_obj_t *self, const struct device *i2c_device); diff --git a/ports/zephyr-cp/common-hal/zephyr_serial/UART.c b/ports/zephyr-cp/common-hal/zephyr_serial/UART.c index fcc05c22f1aad..55e9c424ffed5 100644 --- a/ports/zephyr-cp/common-hal/zephyr_serial/UART.c +++ b/ports/zephyr-cp/common-hal/zephyr_serial/UART.c @@ -74,6 +74,12 @@ void zephyr_serial_uart_construct(zephyr_serial_uart_obj_t *self, const struct d uart_irq_rx_enable(uart_device); } +mp_obj_t zephyr_serial_uart_zephyr_init(zephyr_serial_uart_obj_t *self, const struct device *uart_device) { + self->base.type = &zephyr_serial_uart_type; + zephyr_serial_uart_construct(self, uart_device, 128, NULL); + return MP_OBJ_FROM_PTR(self); +} + bool zephyr_serial_uart_deinited(zephyr_serial_uart_obj_t *self) { return !device_is_ready(self->uart_device); } diff --git a/ports/zephyr-cp/common-hal/zephyr_serial/UART.h b/ports/zephyr-cp/common-hal/zephyr_serial/UART.h index 4e220ee630755..eb3cdd746c7d0 100644 --- a/ports/zephyr-cp/common-hal/zephyr_serial/UART.h +++ b/ports/zephyr-cp/common-hal/zephyr_serial/UART.h @@ -20,3 +20,5 @@ typedef struct { bool rx_paused; // set by irq if no space in rbuf } zephyr_serial_uart_obj_t; + +mp_obj_t zephyr_serial_uart_zephyr_init(zephyr_serial_uart_obj_t *self, const struct device *uart_device); diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index fd1ca7dc43ffc..abb1969470a70 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -51,7 +51,6 @@ "json", "random", "digitalio", - "zephyr_serial", ] MPCONFIG_FLAGS = ["ulab", "nvm", "displayio", "warnings", "alarm", "array", "json"] @@ -254,7 +253,9 @@ async def build_circuitpython(): ) ) - board_autogen_task = tg.create_task(zephyr_dts_to_cp_board(builddir, zephyrbuilddir)) + board_autogen_task = tg.create_task( + zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir) + ) board_info = board_autogen_task.result() mpconfigboard_fn = board_tools.find_mpconfigboard(portdir, board) mpconfigboard = { @@ -290,6 +291,12 @@ async def build_circuitpython(): module_reasons["socketpool"] = "Zephyr networking enabled" module_reasons["ssl"] = "Zephyr networking enabled" + for port_module in (portdir / "bindings").iterdir(): + if not board_info.get(port_module.name, False): + continue + enabled_modules.add(port_module.name) + module_reasons[port_module.name] = f"Zephyr board has {port_module.name}" + circuitpython_flags.extend(board_info["cflags"]) supervisor_source = [ "main.c", @@ -301,6 +308,7 @@ async def build_circuitpython(): portdir / "common-hal/microcontroller/Processor.c", portdir / "common-hal/os/__init__.c", "shared/readline/readline.c", + "shared/runtime/buffer_helper.c", "shared/runtime/context_manager_helpers.c", "shared/runtime/pyexec.c", "shared/runtime/interrupt_char.c", diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 071334161b94c..9d238a03bd810 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -7,12 +7,18 @@ from devicetree import dtlib logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) # GPIO flags defined here: include/zephyr/dt-bindings/gpio/gpio.h GPIO_ACTIVE_LOW = 1 << 0 +MINIMUM_RAM_SIZE = 1024 + MANUAL_COMPAT_TO_DRIVER = { "renesas_ra_nv_flash": "flash", + "nordic_nrf_uarte": "serial", + "nordic_nrf_uart": "serial", + "nordic_nrf_twim": "i2c", } # These are controllers, not the flash devices themselves. @@ -22,6 +28,8 @@ "nordic,nrf-spim", ) +DRIVER_CLASSES = {"serial": "UART", "i2c": "I2C", "spi": "SPI"} + CONNECTORS = { "mikro-bus": [ "AN", @@ -104,6 +112,22 @@ "GPIO0", "GPIO1", ], + "raspberrypi,csi-connector": [ + "CSI_D0_N", + "CSI_D0_P", + "CSI_D1_N", + "CSI_D1_P", + "CSI_CK_N", + "CSI_CK_P", + "CSI_D2_N", + "CSI_D2_P", + "CSI_D3_N", + "CSI_D3_P", + "IO0", + "IO1", + "I2C_SCL", + "I2C_SDA", + ], "renesas,ra-gpio-mipi-header": [ "IIC_SDA", "DISP_BLEN", @@ -167,9 +191,11 @@ ], } +EXCEPTIONAL_DRIVERS = ["entropy", "gpio", "led"] + @cpbuild.run_in_thread -def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 +def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 board_dir = builddir / "board" # Auto generate board files from device tree. @@ -218,6 +244,9 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 status_led_inverted = False path2chosen = {} chosen2path = {} + + # Store active Zephyr device labels per-driver so that we can make them available via board. + active_zephyr_devices = {} usb_num_endpoint_pairs = 0 for k in edt_pickle.root.nodes["chosen"].props: value = edt_pickle.root.nodes["chosen"].props[k] @@ -238,16 +267,20 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 compatible = [] if "compatible" in node.props: compatible = node.props["compatible"].to_strings() - logger.debug(node.name, status) + logger.debug(f"{node.name}: {status}") + logger.debug(f"compatible: {compatible}") chosen = None if node in path2chosen: chosen = path2chosen[node] - logger.debug(" chosen:", chosen) + logger.debug(f" chosen: {chosen}") + if not compatible and chosen == "zephyr,sram": + # The designated sram region may not have any compatible properties, + # so we assume it is compatible with mmio + compatible.append("mmio") for c in compatible: underscored = c.replace(",", "_").replace("-", "_") driver = COMPAT_TO_DRIVER.get(underscored, None) if "mmio" in c: - logger.debug(" ", c, node.labels, node.props) address, size = node.props["reg"].to_nums() end = address + size if chosen == "zephyr,sram": @@ -270,20 +303,21 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 info = (node.labels[0], start, end, size, node.path) if chosen == "zephyr,sram": rams.insert(0, info) - else: + elif status == "okay" and size > MINIMUM_RAM_SIZE: + logger.debug(f"Adding RAM info: {info}") rams.append(info) if not driver: driver = MANUAL_COMPAT_TO_DRIVER.get(underscored, None) - logger.debug(" ", underscored, driver) - if not driver: + logger.debug(f" {c} -> {underscored} -> {driver}") + if not driver or status != "okay": continue - if driver == "flash" and status == "okay": + if driver == "flash": if not chosen and compatible[0] not in BLOCKED_FLASH_COMPAT: # Skip chosen nodes because they are used by Zephyr. flashes.append(f"DEVICE_DT_GET(DT_NODELABEL({node.labels[0]}))") else: logger.debug(" skipping due to blocked compat") - if driver == "usb/udc" and status == "okay": + elif driver == "usb/udc": board_info["usb_device"] = True props = node.props if "num-bidir-endpoints" not in props: @@ -297,8 +331,18 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 single_direction_endpoints.append(props[eps].to_num() if eps in props else 0) # Count separate in/out pairs as bidirectional. usb_num_endpoint_pairs += min(single_direction_endpoints) - if driver.startswith("wifi") and status == "okay": + elif driver.startswith("wifi"): board_info["wifi"] = True + elif driver in EXCEPTIONAL_DRIVERS: + pass + elif (portdir / f"bindings/zephyr_{driver}").exists(): + board_info[f"zephyr_{driver}"] = True + logger.info(f"Supported driver: {driver}") + if driver not in active_zephyr_devices: + active_zephyr_devices[driver] = [] + active_zephyr_devices[driver].append(node.labels) + else: + logger.warning(f"Unsupported driver: {driver}") if gpio: if "ngpios" in node.props: @@ -391,6 +435,58 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 board_pin_mapping = "\n ".join(board_pin_mapping) mcu_pin_mapping = "\n ".join(mcu_pin_mapping) + zephyr_binding_headers = [] + zephyr_binding_objects = [] + zephyr_binding_labels = [] + for driver, instances in active_zephyr_devices.items(): + driverclass = DRIVER_CLASSES[driver] + zephyr_binding_headers.append(f'#include "bindings/zephyr_{driver}/{driverclass}.h"') + + # Designate a main bus such as board.I2C. + if len(instances) == 1: + instances[0].append(driverclass) + else: + # Check to see if a main bus has already been designated + found_main = False + for labels in instances: + for label in labels: + if label == driverclass: + found_main = True + if not found_main: + for priority_label in ("zephyr_i2c", "arduino_i2c"): + for labels in instances: + if priority_label in labels: + labels.append(driverclass) + found_main = True + break + if found_main: + break + for labels in instances: + instance_name = f"{driver}_{labels[0]}" + c_function_name = f"_{instance_name}" + singleton_ptr = f"{c_function_name}_singleton" + function_object = f"{c_function_name}_obj" + binding_prefix = f"zephyr_{driver}_{driverclass.lower()}" + zephyr_binding_objects.append( + f"""static {binding_prefix}_obj_t {instance_name}_obj; +static mp_obj_t {singleton_ptr} = mp_const_none; +static mp_obj_t {c_function_name}(void) {{ + if ({singleton_ptr} != mp_const_none) {{ + return {singleton_ptr}; + }} + {singleton_ptr} = {binding_prefix}_zephyr_init(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]}))); + return {singleton_ptr}; +}} +static MP_DEFINE_CONST_FUN_OBJ_0({function_object}, {c_function_name});""" + ) + for label in labels: + zephyr_binding_labels.append( + f"{{ MP_ROM_QSTR(MP_QSTR_{label.upper()}), MP_ROM_PTR(&{function_object}) }}," + ) + zephyr_binding_headers = "\n".join(zephyr_binding_headers) + zephyr_binding_objects = "\n".join(zephyr_binding_objects) + zephyr_binding_labels = "\n".join(zephyr_binding_labels) + board_dir.mkdir(exist_ok=True, parents=True) header = board_dir / "mpconfigboard.h" if status_led: @@ -442,6 +538,8 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 #include "py/obj.h" #include "py/mphal.h" +{zephyr_binding_headers} + const struct device* const flashes[] = {{ {", ".join(flashes)} }}; const int circuitpy_flash_device_count = {len(flashes)}; @@ -453,6 +551,8 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 {pin_defs} +{zephyr_binding_objects} + static const mp_rom_map_elem_t mcu_pin_globals_table[] = {{ {mcu_pin_mapping} }}; @@ -463,7 +563,8 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 {board_pin_mapping} -// {{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }}, +{zephyr_binding_labels} + }}; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index bd693bb897ea8..8651b2715cc1a 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -26,3 +26,5 @@ CONFIG_ASSERT=y CONFIG_LOG_BLOCK_IN_THREAD=y CONFIG_EVENTS=y + +CONFIG_I2C=y diff --git a/ports/zephyr-cp/supervisor/flash.c b/ports/zephyr-cp/supervisor/flash.c index 6b893f89abe55..8daef5fcdaa0d 100644 --- a/ports/zephyr-cp/supervisor/flash.c +++ b/ports/zephyr-cp/supervisor/flash.c @@ -111,6 +111,10 @@ void supervisor_flash_init(void) { const struct device *d = flashes[i]; printk("flash %p %s\n", d, d->name); + if (!device_is_ready(d)) { + printk(" not ready\n"); + continue; + } if (covered_by_areas[i]) { printk(" covered by flash area\n"); continue; From 46408592cd6432b122bf66cfd099201b0fe4ca34 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 3 Dec 2025 16:07:38 -0800 Subject: [PATCH 224/517] Allow swapping LSFS USB on ESP32-P4 to USB OTG This allows full USB device on the M5Stack Tab5. Improves #10666 --- lib/tinyusb | 2 +- .../analog/boards/apard32690/mpconfigboard.mk | 1 - .../boards/max32690evkit/mpconfigboard.mk | 1 - ports/analog/mpconfigport.h | 2 + ports/broadcom/mpconfigport.h | 2 + ports/broadcom/mpconfigport.mk | 1 - ports/cxd56/mpconfigport.h | 2 + ports/cxd56/mpconfigport.mk | 2 - .../boards/m5stack_tab5/mpconfigboard.h | 3 +- .../common-hal/microcontroller/Pin.c | 15 +++++ ports/espressif/mpconfigport.h | 12 ++++ ports/espressif/mpconfigport.mk | 1 - ports/espressif/supervisor/usb.c | 59 ++++++++++++++++++- ports/mimxrt10xx/mpconfigport.h | 2 + ports/mimxrt10xx/mpconfigport.mk | 2 - py/circuitpy_mpconfig.h | 8 +++ shared-module/usb_cdc/__init__.c | 6 +- shared-module/usb_midi/__init__.c | 4 +- supervisor/shared/usb/tusb_config.h | 32 +++++----- supervisor/shared/usb/usb_msc_flash.c | 4 +- supervisor/supervisor.mk | 3 - 21 files changed, 123 insertions(+), 41 deletions(-) diff --git a/lib/tinyusb b/lib/tinyusb index c1bf19ed6cf1e..3af1bec1a9161 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit c1bf19ed6cf1eaa791f221c1bc5ce4b3d069f76d +Subproject commit 3af1bec1a9161ee8dec29487831f7ac7ade9e189 diff --git a/ports/analog/boards/apard32690/mpconfigboard.mk b/ports/analog/boards/apard32690/mpconfigboard.mk index 7cc54ccfc6dd0..0e62f25f3f859 100644 --- a/ports/analog/boards/apard32690/mpconfigboard.mk +++ b/ports/analog/boards/apard32690/mpconfigboard.mk @@ -19,7 +19,6 @@ USB_VID=0x0456 USB_PID=0x003C USB_MANUFACTURER="Analog Devices, Inc." USB_PRODUCT="MAX32690 APARD" -USB_HIGHSPEED=1 # NOTE: MAX32 devices do not support IN/OUT pairs on the same EP USB_NUM_ENDPOINT_PAIRS=12 diff --git a/ports/analog/boards/max32690evkit/mpconfigboard.mk b/ports/analog/boards/max32690evkit/mpconfigboard.mk index 61413216d8175..a97bc9ddacd0b 100644 --- a/ports/analog/boards/max32690evkit/mpconfigboard.mk +++ b/ports/analog/boards/max32690evkit/mpconfigboard.mk @@ -19,7 +19,6 @@ USB_VID=0x0456 USB_PID=0x003D USB_MANUFACTURER="Analog Devices, Inc." USB_PRODUCT="MAX32690 EvKit" -USB_HIGHSPEED=1 # NOTE: MAX32 devices do not support IN/OUT pairs on the same EP USB_NUM_ENDPOINT_PAIRS=12 diff --git a/ports/analog/mpconfigport.h b/ports/analog/mpconfigport.h index c4b3ee031cacf..3296661fca787 100644 --- a/ports/analog/mpconfigport.h +++ b/ports/analog/mpconfigport.h @@ -12,6 +12,8 @@ // 24KiB stack #define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000 +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + // Also includes mpconfigboard.h #include "py/circuitpy_mpconfig.h" diff --git a/ports/broadcom/mpconfigport.h b/ports/broadcom/mpconfigport.h index 8b749ca03100a..73fae8295ded2 100644 --- a/ports/broadcom/mpconfigport.h +++ b/ports/broadcom/mpconfigport.h @@ -26,6 +26,8 @@ #define MICROPY_FATFS_EXFAT (1) #define MICROPY_FATFS_MKFS_FAT32 (1) +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + //////////////////////////////////////////////////////////////////////////////////////////////////// // This also includes mpconfigboard.h. diff --git a/ports/broadcom/mpconfigport.mk b/ports/broadcom/mpconfigport.mk index d906b5d2aa467..844794b107a21 100644 --- a/ports/broadcom/mpconfigport.mk +++ b/ports/broadcom/mpconfigport.mk @@ -22,7 +22,6 @@ CIRCUITPY_VIDEOCORE = 1 INTERNAL_FLASH_FILESYSTEM = 1 USB_NUM_ENDPOINT_PAIRS = 8 -USB_HIGHSPEED = 1 CIRCUITPY_BUILD_EXTENSIONS ?= disk.img.zip,kernel8.img diff --git a/ports/cxd56/mpconfigport.h b/ports/cxd56/mpconfigport.h index f0a57248bebef..ec606a76547bc 100644 --- a/ports/cxd56/mpconfigport.h +++ b/ports/cxd56/mpconfigport.h @@ -23,6 +23,8 @@ #define USB_MSC_EP_NUM_OUT (5) #define USB_MSC_EP_NUM_IN (4) +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + #include "py/circuitpy_mpconfig.h" #define MICROPY_BYTES_PER_GC_BLOCK (32) diff --git a/ports/cxd56/mpconfigport.mk b/ports/cxd56/mpconfigport.mk index cb54233a07e51..ebfaa91f251ae 100644 --- a/ports/cxd56/mpconfigport.mk +++ b/ports/cxd56/mpconfigport.mk @@ -1,5 +1,3 @@ -USB_HIGHSPEED = 1 - # Number of USB endpoint pairs. USB_NUM_ENDPOINT_PAIRS = 6 diff --git a/ports/espressif/boards/m5stack_tab5/mpconfigboard.h b/ports/espressif/boards/m5stack_tab5/mpconfigboard.h index f85f1fe24b8ab..6c748e93ecdbe 100644 --- a/ports/espressif/boards/m5stack_tab5/mpconfigboard.h +++ b/ports/espressif/boards/m5stack_tab5/mpconfigboard.h @@ -25,4 +25,5 @@ #define DEFAULT_SPI_BUS_MISO (&pin_GPIO19) // Use the second USB device (numbered 0 and 1) -#define CIRCUITPY_USB_DEVICE_INSTANCE 1 +#define CIRCUITPY_USB_DEVICE_INSTANCE 0 +#define CIRCUITPY_ESP32P4_SWAP_LSFS (1) diff --git a/ports/espressif/common-hal/microcontroller/Pin.c b/ports/espressif/common-hal/microcontroller/Pin.c index 3ccd8dcbb693b..dc4e4bb032e74 100644 --- a/ports/espressif/common-hal/microcontroller/Pin.c +++ b/ports/espressif/common-hal/microcontroller/Pin.c @@ -211,11 +211,26 @@ static const uint64_t pin_mask_reset_forbidden = GPIO_SEL_32 | GPIO_SEL_33 | GPIO_SEL_34 | + // USB is on the FS OTG + #if CIRCUITPY_USB_DEVICE_INSTANCE == 0 + #if CIRCUITPY_ESP32P4_SWAP_LSFS == 1 + // We leave 24 and 25 alone in addition to 26 and 27 when swapped. It doesn't work otherwise. (Not sure why.) + GPIO_SEL_24 | // USB D- + GPIO_SEL_25 | // USB D+ + #endif + GPIO_SEL_26 | // USB D- + GPIO_SEL_27 | // USB D+ + #endif #if CIRCUITPY_ESP_USB_SERIAL_JTAG || (defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) && CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) // Never ever reset serial/JTAG communication pins. + #if CIRCUITPY_ESP32P4_SWAP_LSFS == 1 + GPIO_SEL_26 | // USB D- + GPIO_SEL_27 | // USB D+ + #else GPIO_SEL_24 | // USB D- GPIO_SEL_25 | // USB D+ #endif + #endif #if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) && CONFIG_ESP_CONSOLE_UART_DEFAULT && CONFIG_ESP_CONSOLE_UART_NUM == 0 // Never reset debug UART/console pins. GPIO_SEL_37 | diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 031676cf4d8ae..6b85100608c65 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -68,3 +68,15 @@ extern portMUX_TYPE background_task_mutex; #ifndef CIRCUITPY_WIFI_DEFAULT_TX_POWER #define CIRCUITPY_WIFI_DEFAULT_TX_POWER (20) #endif + +#ifndef CIRCUITPY_USB_DEVICE_HIGH_SPEED +#if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 1 +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) +#else +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (0) +#endif +#endif + +#ifndef CIRCUITPY_ESP32P4_SWAP_LSFS +#define CIRCUITPY_ESP32P4_SWAP_LSFS (0) +#endif diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index c3585c7e39984..a967b8a4f15f5 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -290,7 +290,6 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 # Second stage bootloader doesn't work when the factory partition is empty due to # UF2 missing. UF2_BOOTLOADER = 0 -USB_HIGHSPEED = 1 CIRCUITPY_USB_HID = 0 CIRCUITPY_USB_MIDI = 0 CIRCUITPY_TUSB_MEM_ALIGN = 64 diff --git a/ports/espressif/supervisor/usb.c b/ports/espressif/supervisor/usb.c index 274542ef22a7d..327fada778f13 100644 --- a/ports/espressif/supervisor/usb.c +++ b/ports/espressif/supervisor/usb.c @@ -2,6 +2,7 @@ // // SPDX-FileCopyrightText: Copyright (c) 2018 hathach for Adafruit Industries // SPDX-FileCopyrightText: Copyright (c) 2019 Lucian Copeland for Adafruit Industries +// SPDX-FileContributor: 2025 Nicolai Electronics // // SPDX-License-Identifier: MIT @@ -12,6 +13,7 @@ #include "shared/readline/readline.h" #include "hal/gpio_ll.h" +#include "hal/usb_serial_jtag_ll.h" #include "esp_err.h" #include "esp_private/usb_phy.h" @@ -37,7 +39,7 @@ StackType_t usb_device_stack[USBD_STACK_SIZE]; StaticTask_t usb_device_taskdef; -static usb_phy_handle_t phy_hdl; +static usb_phy_handle_t device_phy_hdl; // USB Device Driver task // This top level thread process all usb events and invoke callbacks @@ -62,13 +64,64 @@ void init_usb_hardware(void) { usb_phy_config_t phy_conf = { .controller = USB_PHY_CTRL_OTG, .target = USB_PHY_TARGET_INT, + .otg_mode = USB_OTG_MODE_DEVICE, + #if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 0 + .otg_speed = USB_PHY_SPEED_FULL, + #else + // https://github.com/hathach/tinyusb/issues/2943#issuecomment-2601888322 + // Set speed to undefined (auto-detect) to avoid timing/race issue with S3 with host such as macOS + .otg_speed = USB_PHY_SPEED_UNDEFINED, + #endif + }; + usb_new_phy(&phy_conf, &device_phy_hdl); + + #if CIRCUITPY_ESP32P4_SWAP_LSFS == 1 + #ifndef CONFIG_IDF_TARGET_ESP32P4 + #error "LSFS swap is only supported on ESP32P4" + #endif + // Switch the USB PHY + const usb_serial_jtag_pull_override_vals_t override_disable_usb = { + .dm_pd = true, .dm_pu = false, .dp_pd = true, .dp_pu = false}; + const usb_serial_jtag_pull_override_vals_t override_enable_usb = { + .dm_pd = false, .dm_pu = false, .dp_pd = false, .dp_pu = true}; + + // Drop off the bus by removing the pull-up on USB DP + usb_serial_jtag_ll_phy_enable_pull_override(&override_disable_usb); + // Select USB mode by swapping and un-swapping the two PHYs + vTaskDelay(pdMS_TO_TICKS(500)); // Wait for disconnect before switching to device + usb_serial_jtag_ll_phy_select(1); + + // Put the device back onto the bus by re-enabling the pull-up on USB DP + usb_serial_jtag_ll_phy_enable_pull_override(&override_enable_usb); + usb_serial_jtag_ll_phy_disable_pull_override(); + #endif + + // Pin the USB task to the same core as CircuitPython. This way we leave + // the other core for networking. + (void)xTaskCreateStaticPinnedToCore(usb_device_task, + "usbd", + USBD_STACK_SIZE, + NULL, + 5, + usb_device_stack, + &usb_device_taskdef, + xPortGetCoreID()); + #endif + // Configure USB PHY + usb_phy_config_t phy_conf = { + .controller = USB_PHY_CTRL_OTG, + .target = USB_PHY_TARGET_INT, .otg_mode = USB_OTG_MODE_DEVICE, + #if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 0 + .otg_speed = USB_PHY_SPEED_FULL, + #else // https://github.com/hathach/tinyusb/issues/2943#issuecomment-2601888322 // Set speed to undefined (auto-detect) to avoid timing/race issue with S3 with host such as macOS - .otg_speed = USB_PHY_SPEED_UNDEFINED, + .otg_speed = USB_PHY_SPEED_UNDEFINED, + #endif }; - usb_new_phy(&phy_conf, &phy_hdl); + usb_new_phy(&phy_conf, &device_phy_hdl); // Pin the USB task to the same core as CircuitPython. This way we leave // the other core for networking. diff --git a/ports/mimxrt10xx/mpconfigport.h b/ports/mimxrt10xx/mpconfigport.h index 4d5e4e59700bd..df634dc73282e 100644 --- a/ports/mimxrt10xx/mpconfigport.h +++ b/ports/mimxrt10xx/mpconfigport.h @@ -25,6 +25,8 @@ extern uint8_t _ld_default_stack_size; // are aligned to cache lines. #define MICROPY_BYTES_PER_GC_BLOCK (32) +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + #include "py/circuitpy_mpconfig.h" // TODO: diff --git a/ports/mimxrt10xx/mpconfigport.mk b/ports/mimxrt10xx/mpconfigport.mk index 20d802eb3548e..b3f078c7ecf98 100644 --- a/ports/mimxrt10xx/mpconfigport.mk +++ b/ports/mimxrt10xx/mpconfigport.mk @@ -2,8 +2,6 @@ LD_FILE = $(FLASH).ld $(CHIP_FAMILY).ld imxrt10xx.ld INTERNAL_LIBM = 1 -USB_HIGHSPEED = 1 - # Number of USB endpoint pairs. USB_NUM_ENDPOINT_PAIRS = 8 # Align buffers on the cache boundary so we don't inadvertently load them early. diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 2754d57b01c9b..4acbf96db038e 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -539,10 +539,18 @@ void background_callback_run_all(void); #define CIRCUITPY_USB_DEVICE_INSTANCE 0 #endif +#ifndef CIRCUITPY_USB_DEVICE_HIGH_SPEED +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED 0 +#endif + #ifndef CIRCUITPY_USB_HOST_INSTANCE #define CIRCUITPY_USB_HOST_INSTANCE -1 #endif +#ifndef CIRCUITPY_USB_HOST_HIGH_SPEED +#define CIRCUITPY_USB_HOST_HIGH_SPEED 0 +#endif + // If the port requires certain USB endpoint numbers, define these in mpconfigport.h. #ifndef USB_CDC_EP_NUM_NOTIFICATION diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index 9a9f158f28cfb..644c4ccae792b 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -105,7 +105,7 @@ static const uint8_t usb_cdc_descriptor_template[] = { 0xFF, // 54 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] #define CDC_DATA_OUT_ENDPOINT_INDEX 54 0x02, // 55 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 56,57 wMaxPacketSize 512 #else 0x40, 0x00, // 56,57 wMaxPacketSize 64 @@ -118,7 +118,7 @@ static const uint8_t usb_cdc_descriptor_template[] = { 0xFF, // 61 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number] #define CDC_DATA_IN_ENDPOINT_INDEX 61 0x02, // 62 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 63,64 wMaxPacketSize 512 #else 0x40, 0x00, // 63,64 wMaxPacketSize 64 @@ -326,7 +326,7 @@ static const uint8_t usb_vendor_descriptor_template[] = { 0xFF, // 11 bEndpointAddress (IN/D2H) [SET AT RUNTIME: number] #define VENDOR_OUT_ENDPOINT_INDEX 11 0x02, // 12 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 13,14 wMaxPacketSize 512 #else 0x40, 0x00, // 13,14 wMaxPacketSize 64 diff --git a/shared-module/usb_midi/__init__.c b/shared-module/usb_midi/__init__.c index 3808801ff7e1f..1722911536fc2 100644 --- a/shared-module/usb_midi/__init__.c +++ b/shared-module/usb_midi/__init__.c @@ -106,7 +106,7 @@ static const uint8_t usb_midi_descriptor_template[] = { 0xFF, // 66 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] #define MIDI_STREAMING_OUT_ENDPOINT_INDEX (66) 0x02, // 67 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 68,69 wMaxPacketSize (512) #else 0x40, 0x00, // 68,69 wMaxPacketSize (64) @@ -126,7 +126,7 @@ static const uint8_t usb_midi_descriptor_template[] = { 0xFF, // 78 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number] #define MIDI_STREAMING_IN_ENDPOINT_INDEX (78) 0x02, // 79 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 80, 81 wMaxPacketSize (512) #else 0x40, 0x00, // 80, 81 wMaxPacketSize (64) diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index ac2fd9f4f258c..f2d800b99127e 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -79,18 +79,16 @@ extern "C" { #if CIRCUITPY_USB_DEVICE -#if CIRCUITPY_USB_DEVICE_INSTANCE == 0 -#if USB_HIGHSPEED -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 +#define _DEVICE_SPEED OPT_MODE_HIGH_SPEED #else -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) +#define _DEVICE_SPEED 0 #endif + +#if CIRCUITPY_USB_DEVICE_INSTANCE == 0 +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | _DEVICE_SPEED) #elif CIRCUITPY_USB_DEVICE_INSTANCE == 1 -#if USB_HIGHSPEED -#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) -#else -#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE) -#endif +#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | _DEVICE_SPEED) #endif // Use DMA with the USB peripheral. @@ -157,18 +155,16 @@ extern "C" { #define CFG_TUH_RPI_PIO_USB 1 #endif -#if CIRCUITPY_USB_HOST_INSTANCE == 0 -#if USB_HIGHSPEED -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) +#if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 +#define _HOST_SPEED OPT_MODE_HIGH_SPEED #else -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST) +#define _HOST_SPEED 0 #endif + +#if CIRCUITPY_USB_HOST_INSTANCE == 0 +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | _HOST_SPEED) #elif CIRCUITPY_USB_HOST_INSTANCE == 1 -#if USB_HIGHSPEED -#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) -#else -#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST) -#endif +#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | _HOST_SPEED) #endif // Size of buffer to hold descriptors and other data used for enumeration diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index adc182497617c..5ea457ef328cc 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -72,7 +72,7 @@ static const uint8_t usb_msc_descriptor_template[] = { 0xFF, // 11 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number] #define MSC_IN_ENDPOINT_INDEX (11) 0x02, // 12 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 13,14 wMaxPacketSize 512 #else 0x40, 0x00, // 13,14 wMaxPacketSize 64 @@ -85,7 +85,7 @@ static const uint8_t usb_msc_descriptor_template[] = { 0xFF, // 18 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] #define MSC_OUT_ENDPOINT_INDEX (18) 0x02, // 19 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 20,21 wMaxPacketSize 512 #else 0x40, 0x00, // 20,21 wMaxPacketSize 64 diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 21bda334131c7..4848915630cff 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -259,9 +259,6 @@ ifeq ($(CIRCUITPY_USB_CDC),1) CFLAGS += -DCFG_TUD_CDC=2 endif -USB_HIGHSPEED ?= 0 -CFLAGS += -DUSB_HIGHSPEED=$(USB_HIGHSPEED) - $(BUILD)/supervisor/shared/translate/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/compressed_translations.generated.h CIRCUITPY_DISPLAY_FONT ?= "../../tools/fonts/ter-u12n.bdf" From 1ddc9d011b98256fa20c3a7a2c758d78a105c214 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 3 Dec 2025 16:21:56 -0800 Subject: [PATCH 225/517] Fix board autogen --- .../zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 7a3ea9267c9e9..2d723cbced681 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -110,7 +110,7 @@ vectorio = false warnings = false watchdog = false wifi = true # Zephyr board has wifi -zephyr_i2c = false +zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = false +zephyr_serial = true # Zephyr board has zephyr_serial zlib = false From 145d06f9aa951cad09e46e63dc76ff03701c5da9 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 3 Dec 2025 16:22:06 -0800 Subject: [PATCH 226/517] Fix doc build --- ports/zephyr-cp/bindings/zephyr_i2c/I2C.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c index 08ce9e966a669..f2fe029ee0148 100644 --- a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c +++ b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c @@ -25,8 +25,8 @@ //| .. class:: I2C() //| //| Cannot be instantiated directly. Instead singletons are created using the -//| `board` aliases that match the device tree labels. `board` may list multiple -//| aliases for a single device. For example, `board.I2C1` and `board.ARDUINO_I2C` +//| ``board`` aliases that match the device tree labels. ``board`` may list multiple +//| aliases for a single device. For example, ``board.I2C1`` and ``board.ARDUINO_I2C`` //| may both refer to the same device. //| """ //| From 7e7abc0f1649b98ddb6a3ccabdf2ddb3c285b081 Mon Sep 17 00:00:00 2001 From: Jerzeek Date: Thu, 4 Dec 2025 11:01:39 +0100 Subject: [PATCH 227/517] add studiolab_picoexpander# Please enter the commit message for your changes. Lines starting --- .../boards/studiolab_picoexpander/board.c | 9 ++ .../boards/studiolab_picoexpander/link.ld | 1 + .../studiolab_picoexpander/mpconfigboard.h | 20 ++++ .../studiolab_picoexpander/mpconfigboard.mk | 35 +++++++ .../pico-sdk-configboard.h | 9 ++ .../boards/studiolab_picoexpander/pins.c | 94 +++++++++++++++++++ 6 files changed, 168 insertions(+) create mode 100644 ports/raspberrypi/boards/studiolab_picoexpander/board.c create mode 100644 ports/raspberrypi/boards/studiolab_picoexpander/link.ld create mode 100644 ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h create mode 100644 ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk create mode 100644 ports/raspberrypi/boards/studiolab_picoexpander/pico-sdk-configboard.h create mode 100644 ports/raspberrypi/boards/studiolab_picoexpander/pins.c diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/board.c b/ports/raspberrypi/boards/studiolab_picoexpander/board.c new file mode 100644 index 0000000000000..299f32da04f8c --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/link.ld b/ports/raspberrypi/boards/studiolab_picoexpander/link.ld new file mode 100644 index 0000000000000..e814bead4c51e --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/link.ld @@ -0,0 +1 @@ +firmware_size = 1532k; diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h new file mode 100644 index 0000000000000..5e2dd69f290d0 --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h @@ -0,0 +1,20 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "Pico Expander" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1) + +#define MICROPY_HW_LED_STATUS (&pin_CYW0) +#define MICROPY_HW_NEOPIXEL (&pin_GPIO2) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}} + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO18, .mosi = &pin_GPIO19, .miso = &pin_GPIO16}} diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk new file mode 100644 index 0000000000000..e1407c16854a4 --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk @@ -0,0 +1,35 @@ +USB_VID = 0x239A +USB_PID = 0x8162 +USB_PRODUCT = "Pico 2 W" +USB_MANUFACTURER = "Raspberry Pi" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" + +CIRCUITPY__EVE = 1 + +CIRCUITPY_CYW43 = 1 +CIRCUITPY_SSL = 1 +CIRCUITPY_HASHLIB = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_MDNS = 1 +CIRCUITPY_SOCKETPOOL = 1 +CIRCUITPY_WIFI = 1 + +CFLAGS += \ + -DCYW43_PIN_WL_DYNAMIC=0 \ + -DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \ + -DCYW43_DEFAULT_PIN_WL_REG_ON=23 \ + -DCYW43_DEFAULT_PIN_WL_CLOCK=29 \ + -DCYW43_DEFAULT_PIN_WL_DATA_IN=24 \ + -DCYW43_DEFAULT_PIN_WL_DATA_OUT=24 \ + -DCYW43_DEFAULT_PIN_WL_CS=25 \ + -DCYW43_WL_GPIO_COUNT=3 \ + -DCYW43_WL_GPIO_LED_PIN=0 \ + -DCYW43_PIO_CLOCK_DIV_INT=3 + +# Must be accompanied by a linker script change +CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/pico-sdk-configboard.h b/ports/raspberrypi/boards/studiolab_picoexpander/pico-sdk-configboard.h new file mode 100644 index 0000000000000..945802d1ab116 --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/pins.c b/ports/raspberrypi/boards/studiolab_picoexpander/pins.c new file mode 100644 index 0000000000000..f2c89251149e3 --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/pins.c @@ -0,0 +1,94 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_D22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_BTN), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO15) }, + + + { MP_ROM_QSTR(MP_QSTR_SMPS_MODE), MP_ROM_PTR(&pin_CYW1) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_CYW0) }, + + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_CYW2) }, + + { MP_ROM_QSTR(MP_QSTR_GP26_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + + + { MP_ROM_QSTR(MP_QSTR_GP27_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + + { MP_ROM_QSTR(MP_QSTR_GP28_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 84373ebf8f801ea1a8b5c9c0a8ca253bb0dcb768 Mon Sep 17 00:00:00 2001 From: Jerzeek Date: Thu, 4 Dec 2025 12:06:42 +0100 Subject: [PATCH 228/517] Correct PID and VID --- .../boards/studiolab_picoexpander/mpconfigboard.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk index e1407c16854a4..9db1547907663 100644 --- a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk +++ b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk @@ -1,5 +1,5 @@ -USB_VID = 0x239A -USB_PID = 0x8162 +USB_VID = 0x1209 +USB_PID = 0xC1C1 USB_PRODUCT = "Pico 2 W" USB_MANUFACTURER = "Raspberry Pi" From 0348f977db195cbaee49d3acd7e54c9d7e6ed7db Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 4 Dec 2025 10:09:26 -0800 Subject: [PATCH 229/517] Fix ESP builds --- ports/espressif/supervisor/usb.c | 41 ++++++++------------------------ 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/ports/espressif/supervisor/usb.c b/ports/espressif/supervisor/usb.c index 327fada778f13..56042fce1b7f5 100644 --- a/ports/espressif/supervisor/usb.c +++ b/ports/espressif/supervisor/usb.c @@ -13,7 +13,6 @@ #include "shared/readline/readline.h" #include "hal/gpio_ll.h" -#include "hal/usb_serial_jtag_ll.h" #include "esp_err.h" #include "esp_private/usb_phy.h" @@ -29,6 +28,10 @@ #include "tusb.h" +#ifdef CONFIG_IDF_TARGET_ESP32P4 +#include "hal/usb_serial_jtag_ll.h" +#endif + #if CIRCUITPY_USB_DEVICE #ifdef CFG_TUSB_DEBUG #define USBD_STACK_SIZE (3 * configMINIMAL_STACK_SIZE) @@ -66,11 +69,11 @@ void init_usb_hardware(void) { .target = USB_PHY_TARGET_INT, .otg_mode = USB_OTG_MODE_DEVICE, #if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 0 - .otg_speed = USB_PHY_SPEED_FULL, + .otg_speed = USB_PHY_SPEED_FULL, #else // https://github.com/hathach/tinyusb/issues/2943#issuecomment-2601888322 // Set speed to undefined (auto-detect) to avoid timing/race issue with S3 with host such as macOS - .otg_speed = USB_PHY_SPEED_UNDEFINED, + .otg_speed = USB_PHY_SPEED_UNDEFINED, #endif }; usb_new_phy(&phy_conf, &device_phy_hdl); @@ -81,9 +84,11 @@ void init_usb_hardware(void) { #endif // Switch the USB PHY const usb_serial_jtag_pull_override_vals_t override_disable_usb = { - .dm_pd = true, .dm_pu = false, .dp_pd = true, .dp_pu = false}; + .dm_pd = true, .dm_pu = false, .dp_pd = true, .dp_pu = false + }; const usb_serial_jtag_pull_override_vals_t override_enable_usb = { - .dm_pd = false, .dm_pu = false, .dp_pd = false, .dp_pu = true}; + .dm_pd = false, .dm_pu = false, .dp_pd = false, .dp_pu = true + }; // Drop off the bus by removing the pull-up on USB DP usb_serial_jtag_ll_phy_enable_pull_override(&override_disable_usb); @@ -97,32 +102,6 @@ void init_usb_hardware(void) { usb_serial_jtag_ll_phy_disable_pull_override(); #endif - // Pin the USB task to the same core as CircuitPython. This way we leave - // the other core for networking. - (void)xTaskCreateStaticPinnedToCore(usb_device_task, - "usbd", - USBD_STACK_SIZE, - NULL, - 5, - usb_device_stack, - &usb_device_taskdef, - xPortGetCoreID()); - #endif - // Configure USB PHY - usb_phy_config_t phy_conf = { - .controller = USB_PHY_CTRL_OTG, - .target = USB_PHY_TARGET_INT, - .otg_mode = USB_OTG_MODE_DEVICE, - #if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 0 - .otg_speed = USB_PHY_SPEED_FULL, - #else - // https://github.com/hathach/tinyusb/issues/2943#issuecomment-2601888322 - // Set speed to undefined (auto-detect) to avoid timing/race issue with S3 with host such as macOS - .otg_speed = USB_PHY_SPEED_UNDEFINED, - #endif - }; - usb_new_phy(&phy_conf, &device_phy_hdl); - // Pin the USB task to the same core as CircuitPython. This way we leave // the other core for networking. (void)xTaskCreateStaticPinnedToCore(usb_device_task, From e5fb28d53ebb49f83459cfb616b8ac31bab430af Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 4 Dec 2025 11:16:38 -0800 Subject: [PATCH 230/517] Revert TinyUSB update. It is too big now --- lib/tinyusb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tinyusb b/lib/tinyusb index 3af1bec1a9161..c1bf19ed6cf1e 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 3af1bec1a9161ee8dec29487831f7ac7ade9e189 +Subproject commit c1bf19ed6cf1eaa791f221c1bc5ce4b3d069f76d From 3453683042d9ac4f0f4f476534a5f4f14ba5bd63 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Fri, 5 Dec 2025 19:39:52 +0000 Subject: [PATCH 231/517] implement hashlib.new('sha256') support --- py/circuitpy_mpconfig.mk | 3 +++ shared-module/hashlib/Hash.c | 20 ++++++++++++++++++++ shared-module/hashlib/Hash.h | 6 ++++++ shared-module/hashlib/__init__.c | 8 ++++++++ shared-module/hashlib/__init__.h | 7 +++++++ 5 files changed, 44 insertions(+) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 9bbe5691dfb3b..dbd9de2b3f8ac 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -343,6 +343,9 @@ CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS=$(CIRCUITPY_HASHLIB_MBEDTLS) CIRCUITPY_HASHLIB_MBEDTLS_ONLY ?= $(call enable-if-all,$(CIRCUITPY_HASHLIB_MBEDTLS) $(call enable-if-not,$(CIRCUITPY_SSL))) CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS_ONLY=$(CIRCUITPY_HASHLIB_MBEDTLS_ONLY) +CIRCUITPY_HASHLIB_SHA256 ?= 0 +CFLAGS += -DCIRCUITPY_HASHLIB_SHA256=$(CIRCUITPY_HASHLIB_SHA256) + CIRCUITPY_I2CTARGET ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_I2CTARGET=$(CIRCUITPY_I2CTARGET) diff --git a/shared-module/hashlib/Hash.c b/shared-module/hashlib/Hash.c index a454966d99361..a4e21c7a06f0d 100644 --- a/shared-module/hashlib/Hash.c +++ b/shared-module/hashlib/Hash.c @@ -14,6 +14,12 @@ void common_hal_hashlib_hash_update(hashlib_hash_obj_t *self, const uint8_t *dat mbedtls_sha1_update_ret(&self->sha1, data, datalen); return; } + #if CIRCUITPY_HASHLIB_SHA256 + else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + mbedtls_sha256_update_ret(&self->sha256, data, datalen); + return; + } + #endif } void common_hal_hashlib_hash_digest(hashlib_hash_obj_t *self, uint8_t *data, size_t datalen) { @@ -28,11 +34,25 @@ void common_hal_hashlib_hash_digest(hashlib_hash_obj_t *self, uint8_t *data, siz mbedtls_sha1_finish_ret(&self->sha1, data); mbedtls_sha1_clone(&self->sha1, ©); } + #if CIRCUITPY_HASHLIB_SHA256 + else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + mbedtls_sha256_context copy; + mbedtls_sha256_clone(©, &self->sha256); + mbedtls_sha256_finish_ret(&self->sha256, data); + mbedtls_sha256_clone(&self->sha256, ©); + } + #endif } size_t common_hal_hashlib_hash_get_digest_size(hashlib_hash_obj_t *self) { if (self->hash_type == MBEDTLS_SSL_HASH_SHA1) { return 20; } + #if CIRCUITPY_HASHLIB_SHA256 + else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + return 32; + } + #endif + return 0; } diff --git a/shared-module/hashlib/Hash.h b/shared-module/hashlib/Hash.h index ccc82037cb7aa..0c33b4876c19c 100644 --- a/shared-module/hashlib/Hash.h +++ b/shared-module/hashlib/Hash.h @@ -7,11 +7,17 @@ #pragma once #include "mbedtls/sha1.h" +#if CIRCUITPY_HASHLIB_SHA256 +#include "mbedtls/sha256.h" +#endif typedef struct { mp_obj_base_t base; union { mbedtls_sha1_context sha1; + #if CIRCUITPY_HASHLIB_SHA256 + mbedtls_sha256_context sha256; + #endif }; // Of MBEDTLS_SSL_HASH_* uint8_t hash_type; diff --git a/shared-module/hashlib/__init__.c b/shared-module/hashlib/__init__.c index be3a9f1895964..ebf9d1700eab6 100644 --- a/shared-module/hashlib/__init__.c +++ b/shared-module/hashlib/__init__.c @@ -17,5 +17,13 @@ bool common_hal_hashlib_new(hashlib_hash_obj_t *self, const char *algorithm) { mbedtls_sha1_starts_ret(&self->sha1); return true; } + #if CIRCUITPY_HASHLIB_SHA256 + else if (strcmp(algorithm, "sha256") == 0) { + self->hash_type = MBEDTLS_SSL_HASH_SHA256; + mbedtls_sha256_init(&self->sha256); + mbedtls_sha256_starts_ret(&self->sha256, 0); + return true; + } + #endif return false; } diff --git a/shared-module/hashlib/__init__.h b/shared-module/hashlib/__init__.h index f72882a1c03b3..50125595ab53a 100644 --- a/shared-module/hashlib/__init__.h +++ b/shared-module/hashlib/__init__.h @@ -13,4 +13,11 @@ #define mbedtls_sha1_starts_ret mbedtls_sha1_starts #define mbedtls_sha1_update_ret mbedtls_sha1_update #define mbedtls_sha1_finish_ret mbedtls_sha1_finish + +#if CIRCUITPY_HASHLIB_SHA256 +#define mbedtls_sha256_starts_ret mbedtls_sha256_starts +#define mbedtls_sha256_update_ret mbedtls_sha256_update +#define mbedtls_sha256_finish_ret mbedtls_sha256_finish +#endif + #endif From f80d99297009428ed6c65a67be6b234bfaa71f7b Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Fri, 5 Dec 2025 19:40:49 +0000 Subject: [PATCH 232/517] enable hashlib sha1 & sha256 for CLUE board --- ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk b/ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk index e1a25137a1924..c835621547a0a 100644 --- a/ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk +++ b/ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk @@ -7,3 +7,6 @@ MCU_CHIP = nrf52840 QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "GD25Q16C, W25Q16JVxQ" + +CIRCUITPY_HASHLIB = 1 +CIRCUITPY_HASHLIB_SHA256 = 1 From db86a75be37468ad962c0418ab2a5227df549a44 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 5 Dec 2025 15:05:58 -0800 Subject: [PATCH 233/517] Add mutable brightness --- .../common-hal/microcontroller/Pin.c | 9 ++--- ports/espressif/common-hal/mipidsi/Display.c | 34 +++++++++++++++++++ ports/espressif/common-hal/mipidsi/Display.h | 1 + shared-bindings/mipidsi/Display.c | 11 ++++++ shared-bindings/mipidsi/Display.h | 2 ++ 5 files changed, 50 insertions(+), 7 deletions(-) diff --git a/ports/espressif/common-hal/microcontroller/Pin.c b/ports/espressif/common-hal/microcontroller/Pin.c index dc4e4bb032e74..f995e65545d3c 100644 --- a/ports/espressif/common-hal/microcontroller/Pin.c +++ b/ports/espressif/common-hal/microcontroller/Pin.c @@ -204,13 +204,8 @@ static const uint64_t pin_mask_reset_forbidden = #endif // ESP32H2 #if defined(CONFIG_IDF_TARGET_ESP32P4) - // Never ever reset pins used to communicate with the SPI flash. - GPIO_SEL_28 | - GPIO_SEL_29 | - GPIO_SEL_30 | - GPIO_SEL_32 | - GPIO_SEL_33 | - GPIO_SEL_34 | + // SPI flash is on dedicated pins. + // USB is on the FS OTG #if CIRCUITPY_USB_DEVICE_INSTANCE == 0 #if CIRCUITPY_ESP32P4_SWAP_LSFS == 1 diff --git a/ports/espressif/common-hal/mipidsi/Display.c b/ports/espressif/common-hal/mipidsi/Display.c index cc45db72375a2..46ef46ed60185 100644 --- a/ports/espressif/common-hal/mipidsi/Display.c +++ b/ports/espressif/common-hal/mipidsi/Display.c @@ -222,6 +222,40 @@ void common_hal_mipidsi_display_refresh(mipidsi_display_obj_t *self) { // sends data from the framebuffer to the display } +mp_float_t common_hal_mipidsi_display_get_brightness(mipidsi_display_obj_t *self) { + return self->current_brightness; +} + +bool common_hal_mipidsi_display_set_brightness(mipidsi_display_obj_t *self, mp_float_t brightness) { + if (!self->backlight_on_high) { + brightness = 1.0 - brightness; + } + bool ok = false; + + // Avoid PWM types and functions when the module isn't enabled + #if (CIRCUITPY_PWMIO) + bool ispwm = (self->backlight_pwm.base.type == &pwmio_pwmout_type) ? true : false; + #else + bool ispwm = false; + #endif + + if (ispwm) { + #if (CIRCUITPY_PWMIO) + common_hal_pwmio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t)(0xffff * brightness)); + ok = true; + #else + ok = false; + #endif + } else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { + common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, brightness > 0.99); + ok = true; + } + if (ok) { + self->current_brightness = brightness; + } + return ok; +} + int common_hal_mipidsi_display_get_width(mipidsi_display_obj_t *self) { return self->width; } diff --git a/ports/espressif/common-hal/mipidsi/Display.h b/ports/espressif/common-hal/mipidsi/Display.h index 61e6cb73f6d75..cbc87e268dc80 100644 --- a/ports/espressif/common-hal/mipidsi/Display.h +++ b/ports/espressif/common-hal/mipidsi/Display.h @@ -34,4 +34,5 @@ typedef struct { #endif }; bool backlight_on_high; + mp_float_t current_brightness; } mipidsi_display_obj_t; diff --git a/shared-bindings/mipidsi/Display.c b/shared-bindings/mipidsi/Display.c index ad4522d653c7f..149d31e52a93f 100644 --- a/shared-bindings/mipidsi/Display.c +++ b/shared-bindings/mipidsi/Display.c @@ -220,6 +220,15 @@ static void mipidsi_display_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufi common_hal_mipidsi_display_get_buffer(self_in, bufinfo, 0); } +static float mipidsi_display_get_brightness_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_brightness(self_in); +} + +static bool mipidsi_display_set_brightness_proto(mp_obj_t self_in, mp_float_t value) { + common_hal_mipidsi_display_set_brightness(self_in, value); + return true; +} + // These versions exist so that the prototype matches the protocol, // avoiding a type cast that can hide errors static void mipidsi_display_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) { @@ -266,6 +275,8 @@ static int mipidsi_display_get_row_stride_proto(mp_obj_t self_in) { static const framebuffer_p_t mipidsi_display_proto = { MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer) .get_bufinfo = mipidsi_display_get_bufinfo, + .set_brightness = mipidsi_display_set_brightness_proto, + .get_brightness = mipidsi_display_get_brightness_proto, .get_width = mipidsi_display_get_width_proto, .get_height = mipidsi_display_get_height_proto, .get_color_depth = mipidsi_display_get_color_depth_proto, diff --git a/shared-bindings/mipidsi/Display.h b/shared-bindings/mipidsi/Display.h index 1cec788f16cd9..09cf130bf4a28 100644 --- a/shared-bindings/mipidsi/Display.h +++ b/shared-bindings/mipidsi/Display.h @@ -35,6 +35,8 @@ void common_hal_mipidsi_display_construct(mipidsi_display_obj_t *self, void common_hal_mipidsi_display_deinit(mipidsi_display_obj_t *self); bool common_hal_mipidsi_display_deinited(mipidsi_display_obj_t *self); void common_hal_mipidsi_display_refresh(mipidsi_display_obj_t *self); +mp_float_t common_hal_mipidsi_display_get_brightness(mipidsi_display_obj_t *self); +bool common_hal_mipidsi_display_set_brightness(mipidsi_display_obj_t *self, mp_float_t brightness); int common_hal_mipidsi_display_get_width(mipidsi_display_obj_t *self); int common_hal_mipidsi_display_get_height(mipidsi_display_obj_t *self); int common_hal_mipidsi_display_get_row_stride(mipidsi_display_obj_t *self); From 9e204f114b4d786b3eda53840f057018bff10a2d Mon Sep 17 00:00:00 2001 From: noqman Date: Mon, 8 Dec 2025 16:17:35 +0800 Subject: [PATCH 234/517] Remove adafruit_bus_device --- ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk index 72d20ea7e055e..95686552e6987 100644 --- a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk @@ -38,7 +38,6 @@ CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' OPTIMIZATION_FLAGS = -O2 # Include these Python libraries in firmware. -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel From 2d6d5f6aad79c9a5463dc45d1bd3e61ac07e2237 Mon Sep 17 00:00:00 2001 From: noqman Date: Mon, 8 Dec 2025 16:20:17 +0800 Subject: [PATCH 235/517] Remove adafruit_sdcard and enable sdcardio --- .../raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk index 95686552e6987..af59c1ab25be4 100644 --- a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk @@ -11,6 +11,8 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY__EVE = 1 +CIRCUITPY_SDCARDIO = 1 + CIRCUITPY_CYW43 = 1 CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 @@ -48,5 +50,4 @@ FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ImageLoad FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_AHTx0 -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer From c69dc8f0f34c3e2362e25358e10b65a7cf687782 Mon Sep 17 00:00:00 2001 From: noqman Date: Mon, 8 Dec 2025 16:21:51 +0800 Subject: [PATCH 236/517] Add adafruit_OPT4048 --- ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk index af59c1ab25be4..2bea1359b4a83 100644 --- a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk @@ -48,6 +48,7 @@ FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SimpleIO FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_framebuf FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_OPT4048 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ImageLoad FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_AHTx0 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer From b04f7e53da206e3b638fb17473d8545d180f5b35 Mon Sep 17 00:00:00 2001 From: noqman Date: Mon, 8 Dec 2025 16:35:22 +0800 Subject: [PATCH 237/517] Add the OPT4048 submodule --- .gitmodules | 3 +++ frozen/Adafruit_CircuitPython_OPT4048 | 1 + 2 files changed, 4 insertions(+) create mode 160000 frozen/Adafruit_CircuitPython_OPT4048 diff --git a/.gitmodules b/.gitmodules index 7eaa0b04142b4..4cef2eeb9d500 100644 --- a/.gitmodules +++ b/.gitmodules @@ -415,3 +415,6 @@ [submodule "ports/espressif/microros-lib"] path = ports/espressif/microros-lib url = https://github.com/hierophect/microros-lib.git +[submodule "frozen/Adafruit_CircuitPython_OPT4048"] + path = frozen/Adafruit_CircuitPython_OPT4048 + url = https://github.com/adafruit/Adafruit_CircuitPython_OPT4048.git diff --git a/frozen/Adafruit_CircuitPython_OPT4048 b/frozen/Adafruit_CircuitPython_OPT4048 new file mode 160000 index 0000000000000..b6a02e289e922 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_OPT4048 @@ -0,0 +1 @@ +Subproject commit b6a02e289e922b328fd395908b6b89f12ee137ea From 3630042e27350327761961d027a18f4384b88ff0 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 8 Dec 2025 10:08:26 -0800 Subject: [PATCH 238/517] Add parens Co-authored-by: Dan Halbert --- ports/zephyr-cp/bindings/zephyr_i2c/I2C.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c index f2fe029ee0148..456f3703ff553 100644 --- a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c +++ b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c @@ -26,7 +26,7 @@ //| //| Cannot be instantiated directly. Instead singletons are created using the //| ``board`` aliases that match the device tree labels. ``board`` may list multiple -//| aliases for a single device. For example, ``board.I2C1`` and ``board.ARDUINO_I2C`` +//| aliases for a single device. For example, ``board.I2C1()`` and ``board.ARDUINO_I2C()`` //| may both refer to the same device. //| """ //| From 30c8dd626f543c2927a08f8c5797aa0e0cf79ba7 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Mon, 8 Dec 2025 18:37:33 +0000 Subject: [PATCH 239/517] default to enabling SHA256 when hashlib is enabled --- py/circuitpy_mpconfig.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index dbd9de2b3f8ac..61344a86f9e75 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -343,7 +343,7 @@ CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS=$(CIRCUITPY_HASHLIB_MBEDTLS) CIRCUITPY_HASHLIB_MBEDTLS_ONLY ?= $(call enable-if-all,$(CIRCUITPY_HASHLIB_MBEDTLS) $(call enable-if-not,$(CIRCUITPY_SSL))) CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS_ONLY=$(CIRCUITPY_HASHLIB_MBEDTLS_ONLY) -CIRCUITPY_HASHLIB_SHA256 ?= 0 +CIRCUITPY_HASHLIB_SHA256 ?= $(CIRCUITPY_HASHLIB) CFLAGS += -DCIRCUITPY_HASHLIB_SHA256=$(CIRCUITPY_HASHLIB_SHA256) CIRCUITPY_I2CTARGET ?= $(CIRCUITPY_FULL_BUILD) From 9996cbf740e0a86bd14456d0baba6eb8c2f6dd40 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Mon, 8 Dec 2025 21:05:28 +0100 Subject: [PATCH 240/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 14 +++++++------- locale/el.po | 14 +++++++------- locale/hi.po | 14 +++++++------- locale/ko.po | 14 +++++++------- locale/ru.po | 14 +++++++------- locale/tr.po | 14 +++++++------- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index 44adf48833626..35196a280e3c7 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -122,6 +122,7 @@ msgid "%q in %q must be of type %q, not %q" msgstr "%q v %q musí být typu %q, ne %q" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -255,7 +256,8 @@ msgstr "%q je mimo hranice" #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "%q je mimo rozsah" @@ -850,10 +852,6 @@ msgstr "Jádro kódu CircuitPython tvrdě havarovalo. Jejda!\n" msgid "Clock unit in use" msgstr "Jednotka hodin je používána" -#: ports/espressif/common-hal/mipidsi/Display.c -msgid "Color depth must be 16 or 24" -msgstr "" - #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -1139,8 +1137,9 @@ msgstr "" "Frekvence musí být 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 nebo 1008 " "Mhz" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "Funkce vyžaduje zámek" @@ -1291,6 +1290,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c diff --git a/locale/el.po b/locale/el.po index 01f199d5c6a14..bde4522cc60f0 100644 --- a/locale/el.po +++ b/locale/el.po @@ -126,6 +126,7 @@ msgid "%q in %q must be of type %q, not %q" msgstr "%q στο %q πρέπει να είναι τύπου %q, όχι %q" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -259,7 +260,8 @@ msgstr "%q εκτός ορίων" #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "%q εκτός εμβέλειας" @@ -855,10 +857,6 @@ msgstr "Ο πυρήνας της CircuitPython κατέρευσε. Οουπς!\n msgid "Clock unit in use" msgstr "Μονάδα ρολογιού ήδη σε χρήση" -#: ports/espressif/common-hal/mipidsi/Display.c -msgid "Color depth must be 16 or 24" -msgstr "" - #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -1145,8 +1143,9 @@ msgid "" "Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" msgstr "" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "" @@ -1297,6 +1296,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c diff --git a/locale/hi.po b/locale/hi.po index ea538c2f6f3dc..43737b6cdd827 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -113,6 +113,7 @@ msgid "%q in %q must be of type %q, not %q" msgstr "" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -246,7 +247,8 @@ msgstr "" #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "" @@ -836,10 +838,6 @@ msgstr "" msgid "Clock unit in use" msgstr "" -#: ports/espressif/common-hal/mipidsi/Display.c -msgid "Color depth must be 16 or 24" -msgstr "" - #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -1121,8 +1119,9 @@ msgid "" "Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" msgstr "" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "" @@ -1273,6 +1272,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c diff --git a/locale/ko.po b/locale/ko.po index 96d8003ff9324..f77519cf701d9 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -124,6 +124,7 @@ msgid "%q in %q must be of type %q, not %q" msgstr "%q의 %q는 %q가 아니라 %q 유형이어야 합니다" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -263,7 +264,8 @@ msgstr "%q가 경계를 벗어남" #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c #, fuzzy msgid "%q out of range" msgstr "%q가 범위를 벗어남" @@ -880,10 +882,6 @@ msgstr "CircuitPython 핵심 코드가 심하게 충돌했습니다. 앗!\n" msgid "Clock unit in use" msgstr "시계 장치가 사용 중입니다" -#: ports/espressif/common-hal/mipidsi/Display.c -msgid "Color depth must be 16 or 24" -msgstr "" - #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -1172,8 +1170,9 @@ msgstr "" "주파수는 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 또는 1008 Mhz 여야 " "합니다" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "이 함수에는 잠금이 필요합니다" @@ -1324,6 +1323,7 @@ msgstr "출력 함수로 인해 종료되었다" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c diff --git a/locale/ru.po b/locale/ru.po index 3b957b347c133..bbd39319823d3 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -126,6 +126,7 @@ msgid "%q in %q must be of type %q, not %q" msgstr "%q в %q должно быть типа %q, а не %q" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -259,7 +260,8 @@ msgstr "%q за пределом" #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "%q вне диапазона" @@ -858,10 +860,6 @@ msgstr "Основной код CircuitPython сильно разбился. У msgid "Clock unit in use" msgstr "Источник тактирования уже используется" -#: ports/espressif/common-hal/mipidsi/Display.c -msgid "Color depth must be 16 or 24" -msgstr "" - #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -1156,8 +1154,9 @@ msgstr "" "Частота должна быть 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 или 1008 " "МГц" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "Функция требует блокировки" @@ -1312,6 +1311,7 @@ msgstr "Прерывается функцией выхода" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c diff --git a/locale/tr.po b/locale/tr.po index 54155a605438a..966a9123083b8 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -124,6 +124,7 @@ msgid "%q in %q must be of type %q, not %q" msgstr "" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -257,7 +258,8 @@ msgstr "%q sınırların dışında" #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "%q aralık dışında" @@ -850,10 +852,6 @@ msgstr "CircuitPython kor kodu patladı. Haydaaa!\n" msgid "Clock unit in use" msgstr "Saat ünitesi kullanımda" -#: ports/espressif/common-hal/mipidsi/Display.c -msgid "Color depth must be 16 or 24" -msgstr "" - #: shared-bindings/_bleio/Connection.c msgid "" "Connection has been disconnected and can no longer be used. Create a new " @@ -1139,8 +1137,9 @@ msgstr "" "Frekans 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 ya da 1008 Mhz " "olmalıdır" -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c +#: shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "Fonksiyon kilit gerektirir" @@ -1291,6 +1290,7 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c From f9d5778aa706e9094161d603b828443be627057c Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Mon, 8 Dec 2025 20:36:18 +0000 Subject: [PATCH 241/517] turn on hashlib for all nrf52840 boards This turns on hashlib at the port level, but only for nrf52840 boards. There's a comment in mpconfigport.mk suggesting that space is already tight on nrf52833 boards. --- ports/nordic/mpconfigport.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/nordic/mpconfigport.mk b/ports/nordic/mpconfigport.mk index 502e71ae4ae16..48536ec2acd7b 100644 --- a/ports/nordic/mpconfigport.mk +++ b/ports/nordic/mpconfigport.mk @@ -63,6 +63,8 @@ CIRCUITPY_MEMORYMAP ?= 1 CIRCUITPY_RGBMATRIX ?= 1 CIRCUITPY_FRAMEBUFFERIO ?= 1 +CIRCUITPY_HASHLIB ?= 1 + CIRCUITPY_COUNTIO ?= 1 CIRCUITPY_WATCHDOG ?= 1 From fabe72b40e623f5504342e712d16aa9b131a4937 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Mon, 8 Dec 2025 20:38:31 +0000 Subject: [PATCH 242/517] remove redundant hashlib enable for CLUE This is enabled at the port level now. --- ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk | 3 --- 1 file changed, 3 deletions(-) diff --git a/ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk b/ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk index c835621547a0a..e1a25137a1924 100644 --- a/ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk +++ b/ports/nordic/boards/clue_nrf52840_express/mpconfigboard.mk @@ -7,6 +7,3 @@ MCU_CHIP = nrf52840 QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "GD25Q16C, W25Q16JVxQ" - -CIRCUITPY_HASHLIB = 1 -CIRCUITPY_HASHLIB_SHA256 = 1 From c1bd53d40ae5aa5f655522d58fafa8c8dce45343 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Mon, 8 Dec 2025 21:54:30 +0000 Subject: [PATCH 243/517] remove CIRCUITPY_HASHLIB_SHA256 ifdef guards --- py/circuitpy_mpconfig.mk | 3 --- shared-module/hashlib/Hash.c | 16 +++------------- shared-module/hashlib/Hash.h | 4 ---- shared-module/hashlib/__init__.c | 5 +---- shared-module/hashlib/__init__.h | 2 -- 5 files changed, 4 insertions(+), 26 deletions(-) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 61344a86f9e75..9bbe5691dfb3b 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -343,9 +343,6 @@ CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS=$(CIRCUITPY_HASHLIB_MBEDTLS) CIRCUITPY_HASHLIB_MBEDTLS_ONLY ?= $(call enable-if-all,$(CIRCUITPY_HASHLIB_MBEDTLS) $(call enable-if-not,$(CIRCUITPY_SSL))) CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS_ONLY=$(CIRCUITPY_HASHLIB_MBEDTLS_ONLY) -CIRCUITPY_HASHLIB_SHA256 ?= $(CIRCUITPY_HASHLIB) -CFLAGS += -DCIRCUITPY_HASHLIB_SHA256=$(CIRCUITPY_HASHLIB_SHA256) - CIRCUITPY_I2CTARGET ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_I2CTARGET=$(CIRCUITPY_I2CTARGET) diff --git a/shared-module/hashlib/Hash.c b/shared-module/hashlib/Hash.c index a4e21c7a06f0d..b7e966e951b5c 100644 --- a/shared-module/hashlib/Hash.c +++ b/shared-module/hashlib/Hash.c @@ -13,13 +13,10 @@ void common_hal_hashlib_hash_update(hashlib_hash_obj_t *self, const uint8_t *dat if (self->hash_type == MBEDTLS_SSL_HASH_SHA1) { mbedtls_sha1_update_ret(&self->sha1, data, datalen); return; - } - #if CIRCUITPY_HASHLIB_SHA256 - else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + } else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { mbedtls_sha256_update_ret(&self->sha256, data, datalen); return; } - #endif } void common_hal_hashlib_hash_digest(hashlib_hash_obj_t *self, uint8_t *data, size_t datalen) { @@ -33,26 +30,19 @@ void common_hal_hashlib_hash_digest(hashlib_hash_obj_t *self, uint8_t *data, siz mbedtls_sha1_clone(©, &self->sha1); mbedtls_sha1_finish_ret(&self->sha1, data); mbedtls_sha1_clone(&self->sha1, ©); - } - #if CIRCUITPY_HASHLIB_SHA256 - else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + } else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { mbedtls_sha256_context copy; mbedtls_sha256_clone(©, &self->sha256); mbedtls_sha256_finish_ret(&self->sha256, data); mbedtls_sha256_clone(&self->sha256, ©); } - #endif } size_t common_hal_hashlib_hash_get_digest_size(hashlib_hash_obj_t *self) { if (self->hash_type == MBEDTLS_SSL_HASH_SHA1) { return 20; - } - #if CIRCUITPY_HASHLIB_SHA256 - else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + } else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { return 32; } - #endif - return 0; } diff --git a/shared-module/hashlib/Hash.h b/shared-module/hashlib/Hash.h index 0c33b4876c19c..f3c2979e59c85 100644 --- a/shared-module/hashlib/Hash.h +++ b/shared-module/hashlib/Hash.h @@ -7,17 +7,13 @@ #pragma once #include "mbedtls/sha1.h" -#if CIRCUITPY_HASHLIB_SHA256 #include "mbedtls/sha256.h" -#endif typedef struct { mp_obj_base_t base; union { mbedtls_sha1_context sha1; - #if CIRCUITPY_HASHLIB_SHA256 mbedtls_sha256_context sha256; - #endif }; // Of MBEDTLS_SSL_HASH_* uint8_t hash_type; diff --git a/shared-module/hashlib/__init__.c b/shared-module/hashlib/__init__.c index ebf9d1700eab6..f9bc787d49f16 100644 --- a/shared-module/hashlib/__init__.c +++ b/shared-module/hashlib/__init__.c @@ -16,14 +16,11 @@ bool common_hal_hashlib_new(hashlib_hash_obj_t *self, const char *algorithm) { mbedtls_sha1_init(&self->sha1); mbedtls_sha1_starts_ret(&self->sha1); return true; - } - #if CIRCUITPY_HASHLIB_SHA256 - else if (strcmp(algorithm, "sha256") == 0) { + } else if (strcmp(algorithm, "sha256") == 0) { self->hash_type = MBEDTLS_SSL_HASH_SHA256; mbedtls_sha256_init(&self->sha256); mbedtls_sha256_starts_ret(&self->sha256, 0); return true; } - #endif return false; } diff --git a/shared-module/hashlib/__init__.h b/shared-module/hashlib/__init__.h index 50125595ab53a..847bd8a834728 100644 --- a/shared-module/hashlib/__init__.h +++ b/shared-module/hashlib/__init__.h @@ -14,10 +14,8 @@ #define mbedtls_sha1_update_ret mbedtls_sha1_update #define mbedtls_sha1_finish_ret mbedtls_sha1_finish -#if CIRCUITPY_HASHLIB_SHA256 #define mbedtls_sha256_starts_ret mbedtls_sha256_starts #define mbedtls_sha256_update_ret mbedtls_sha256_update #define mbedtls_sha256_finish_ret mbedtls_sha256_finish -#endif #endif From c6ae68ad41891df42a4156611a59dbd33f1fa612 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 8 Dec 2025 14:34:29 -0800 Subject: [PATCH 244/517] Init older M5Stack Tab5 display Fixes #10666 --- ports/espressif/boards/m5stack_tab5/board.c | 370 ++++++++++++++++++++ 1 file changed, 370 insertions(+) diff --git a/ports/espressif/boards/m5stack_tab5/board.c b/ports/espressif/boards/m5stack_tab5/board.c index a3a9eec047145..7c7c768d5c154 100644 --- a/ports/espressif/boards/m5stack_tab5/board.c +++ b/ports/espressif/boards/m5stack_tab5/board.c @@ -5,5 +5,375 @@ // SPDX-License-Identifier: MIT #include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/mipidsi/Bus.h" +#include "shared-bindings/mipidsi/Display.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/framebufferio/__init__.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" +#include "ports/espressif/common-hal/microcontroller/Pin.h" + +// Statically allocate the MIPI DSI bus (only one DSI bus on ESP32-P4) +static mipidsi_bus_obj_t board_mipidsi_bus; + +// ILI9881C initialization sequence +static const uint8_t ili9881c_init_sequence[] = { + 0xff, 0x03, 0x98, 0x81, 0x00, + 0x01, 0x80, 0x78, // software reset + // CMD_Page 1 + 0xff, 0x03, 0x98, 0x81, 0x01, + 0xb7, 0x01, 0x03, // set 2 lane + // CMD Page 0 + 0xff, 0x03, 0x98, 0x81, 0x00, + 0x11, 0x80, 0x0a, // out of sleep + 0x36, 0x01, 0x00, // madctl + 0x3a, 0x01, 0x55, // colmod + // CMD_Page 3 + 0xff, 0x03, 0x98, 0x81, 0x03, + 0x01, 0x01, 0x00, + 0x02, 0x01, 0x00, + 0x03, 0x01, 0x73, + 0x04, 0x01, 0x00, + 0x05, 0x01, 0x00, + 0x06, 0x01, 0x08, + 0x07, 0x01, 0x00, + 0x08, 0x01, 0x00, + 0x09, 0x01, 0x1b, + 0x0a, 0x01, 0x01, + 0x0b, 0x01, 0x01, + 0x0c, 0x01, 0x0d, + 0x0d, 0x01, 0x01, + 0x0e, 0x01, 0x01, + 0x0f, 0x01, 0x26, + 0x10, 0x01, 0x26, + 0x11, 0x01, 0x00, + 0x12, 0x01, 0x00, + 0x13, 0x01, 0x02, + 0x14, 0x01, 0x00, + 0x15, 0x01, 0x00, + 0x16, 0x01, 0x00, + 0x17, 0x01, 0x00, + 0x18, 0x01, 0x00, + 0x19, 0x01, 0x00, + 0x1a, 0x01, 0x00, + 0x1b, 0x01, 0x00, + 0x1c, 0x01, 0x00, + 0x1d, 0x01, 0x00, + 0x1e, 0x01, 0x40, + 0x1f, 0x01, 0x00, + 0x20, 0x01, 0x06, + 0x21, 0x01, 0x01, + 0x22, 0x01, 0x00, + 0x23, 0x01, 0x00, + 0x24, 0x01, 0x00, + 0x25, 0x01, 0x00, + 0x26, 0x01, 0x00, + 0x27, 0x01, 0x00, + 0x28, 0x01, 0x33, + 0x29, 0x01, 0x03, + 0x2a, 0x01, 0x00, + 0x2b, 0x01, 0x00, + 0x2c, 0x01, 0x00, + 0x2d, 0x01, 0x00, + 0x2e, 0x01, 0x00, + 0x2f, 0x01, 0x00, + 0x30, 0x01, 0x00, + 0x31, 0x01, 0x00, + 0x32, 0x01, 0x00, + 0x33, 0x01, 0x00, + 0x34, 0x01, 0x00, + 0x35, 0x01, 0x00, + 0x36, 0x01, 0x00, + 0x37, 0x01, 0x00, + 0x38, 0x01, 0x00, + 0x39, 0x01, 0x00, + 0x3a, 0x01, 0x00, + 0x3b, 0x01, 0x00, + 0x3c, 0x01, 0x00, + 0x3d, 0x01, 0x00, + 0x3e, 0x01, 0x00, + 0x3f, 0x01, 0x00, + 0x40, 0x01, 0x00, + 0x41, 0x01, 0x00, + 0x42, 0x01, 0x00, + 0x43, 0x01, 0x00, + 0x44, 0x01, 0x00, + 0x50, 0x01, 0x01, + 0x51, 0x01, 0x23, + 0x52, 0x01, 0x45, + 0x53, 0x01, 0x67, + 0x54, 0x01, 0x89, + 0x55, 0x01, 0xab, + 0x56, 0x01, 0x01, + 0x57, 0x01, 0x23, + 0x58, 0x01, 0x45, + 0x59, 0x01, 0x67, + 0x5a, 0x01, 0x89, + 0x5b, 0x01, 0xab, + 0x5c, 0x01, 0xcd, + 0x5d, 0x01, 0xef, + 0x5e, 0x01, 0x11, + 0x5f, 0x01, 0x02, + 0x60, 0x01, 0x00, + 0x61, 0x01, 0x07, + 0x62, 0x01, 0x06, + 0x63, 0x01, 0x0e, + 0x64, 0x01, 0x0f, + 0x65, 0x01, 0x0c, + 0x66, 0x01, 0x0d, + 0x67, 0x01, 0x02, + 0x68, 0x01, 0x02, + 0x69, 0x01, 0x02, + 0x6a, 0x01, 0x02, + 0x6b, 0x01, 0x02, + 0x6c, 0x01, 0x02, + 0x6d, 0x01, 0x02, + 0x6e, 0x01, 0x02, + 0x6f, 0x01, 0x02, + 0x70, 0x01, 0x02, + 0x71, 0x01, 0x02, + 0x72, 0x01, 0x02, + 0x73, 0x01, 0x05, + 0x74, 0x01, 0x01, + 0x75, 0x01, 0x02, + 0x76, 0x01, 0x00, + 0x77, 0x01, 0x07, + 0x78, 0x01, 0x06, + 0x79, 0x01, 0x0e, + 0x7a, 0x01, 0x0f, + 0x7b, 0x01, 0x0c, + 0x7c, 0x01, 0x0d, + 0x7d, 0x01, 0x02, + 0x7e, 0x01, 0x02, + 0x7f, 0x01, 0x02, + 0x80, 0x01, 0x02, + 0x81, 0x01, 0x02, + 0x82, 0x01, 0x02, + 0x83, 0x01, 0x02, + 0x84, 0x01, 0x02, + 0x85, 0x01, 0x02, + 0x86, 0x01, 0x02, + 0x87, 0x01, 0x02, + 0x88, 0x01, 0x02, + 0x89, 0x01, 0x05, + 0x8a, 0x01, 0x01, + // CMD_Page 4 + 0xff, 0x03, 0x98, 0x81, 0x04, + 0x38, 0x01, 0x01, + 0x39, 0x01, 0x00, + 0x6c, 0x01, 0x15, + 0x6e, 0x01, 0x1a, + 0x6f, 0x01, 0x25, + 0x3a, 0x01, 0xa4, + 0x8d, 0x01, 0x20, + 0x87, 0x01, 0xba, + 0x3b, 0x01, 0x98, + // CMD_Page 1 + 0xff, 0x03, 0x98, 0x81, 0x01, + 0x22, 0x01, 0x0a, + 0x31, 0x01, 0x00, + 0x50, 0x01, 0x6b, + 0x51, 0x01, 0x66, + 0x53, 0x01, 0x73, + 0x55, 0x01, 0x8b, + 0x60, 0x01, 0x1b, + 0x61, 0x01, 0x01, + 0x62, 0x01, 0x0c, + 0x63, 0x01, 0x00, + // Gamma P + 0xa0, 0x01, 0x00, + 0xa1, 0x01, 0x15, + 0xa2, 0x01, 0x1f, + 0xa3, 0x01, 0x13, + 0xa4, 0x01, 0x11, + 0xa5, 0x01, 0x21, + 0xa6, 0x01, 0x17, + 0xa7, 0x01, 0x1b, + 0xa8, 0x01, 0x6b, + 0xa9, 0x01, 0x1e, + 0xaa, 0x01, 0x2b, + 0xab, 0x01, 0x5d, + 0xac, 0x01, 0x19, + 0xad, 0x01, 0x14, + 0xae, 0x01, 0x4b, + 0xaf, 0x01, 0x1d, + 0xb0, 0x01, 0x27, + 0xb1, 0x01, 0x49, + 0xb2, 0x01, 0x5d, + 0xb3, 0x01, 0x39, + // Gamma N + 0xc0, 0x01, 0x00, + 0xc1, 0x01, 0x01, + 0xc2, 0x01, 0x0c, + 0xc3, 0x01, 0x11, + 0xc4, 0x01, 0x15, + 0xc5, 0x01, 0x28, + 0xc6, 0x01, 0x1b, + 0xc7, 0x01, 0x1c, + 0xc8, 0x01, 0x62, + 0xc9, 0x01, 0x1c, + 0xca, 0x01, 0x29, + 0xcb, 0x01, 0x60, + 0xcc, 0x01, 0x16, + 0xcd, 0x01, 0x17, + 0xce, 0x01, 0x4a, + 0xcf, 0x01, 0x23, + 0xd0, 0x01, 0x24, + 0xd1, 0x01, 0x4f, + 0xd2, 0x01, 0x5f, + 0xd3, 0x01, 0x39, + // CMD_Page 0 + 0xff, 0x03, 0x98, 0x81, 0x00, + 0x35, 0x00, + 0xfe, 0x00, + 0x29, 0x00, +}; + +// I2C addresses +#define GOODIX_TOUCH_ADDRESS 0x14 +#define ST7123_ADDRESS 0x55 +#define I2C_DEV_ADDR_PI4IOE1 0x43 + +// PI4IOE GPIO expander registers +#define PI4IO_REG_CHIP_RESET 0x01 +#define PI4IO_REG_IO_DIR 0x03 +#define PI4IO_REG_OUT_SET 0x05 +#define PI4IO_REG_OUT_H_IM 0x07 +#define PI4IO_REG_PULL_SEL 0x0D +#define PI4IO_REG_PULL_EN 0x0B + +void board_init(void) { + // Initialize I2C for GPIO expander and display detection + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + // Initialize PI4IOE GPIO expander to control LCD reset + uint8_t write_buf[2]; + + common_hal_busio_i2c_try_lock(i2c); + + // Chip reset + write_buf[0] = PI4IO_REG_CHIP_RESET; + write_buf[1] = 0xFF; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Set IO direction (bit 7 as output for LCD reset) + write_buf[0] = PI4IO_REG_IO_DIR; + write_buf[1] = 0b01111111; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Set output high-impedance mode + write_buf[0] = PI4IO_REG_OUT_H_IM; + write_buf[1] = 0b00000000; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Set pull select + write_buf[0] = PI4IO_REG_PULL_SEL; + write_buf[1] = 0b01111111; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Enable pull resistors + write_buf[0] = PI4IO_REG_PULL_EN; + write_buf[1] = 0b01111111; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Set output state (including LCD reset) + write_buf[0] = PI4IO_REG_OUT_SET; + write_buf[1] = 0b01110110; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Small delay for reset to take effect + mp_hal_delay_ms(100); + + // Probe I2C bus to detect which display is present + bool has_goodix = common_hal_busio_i2c_probe(i2c, GOODIX_TOUCH_ADDRESS); + bool has_st7123 = common_hal_busio_i2c_probe(i2c, ST7123_ADDRESS); + + common_hal_busio_i2c_unlock(i2c); + + // Configure display parameters based on detected hardware + uint32_t bus_frequency; + uint32_t pixel_clock_frequency; + const uint8_t *init_sequence; + size_t init_sequence_len; + uint32_t hsync_pulse_width, hsync_back_porch, hsync_front_porch; + uint32_t vsync_pulse_width, vsync_back_porch, vsync_front_porch; + + if (has_goodix) { + // ILI9881C display with Goodix touch + bus_frequency = 730000000; + pixel_clock_frequency = 60000000; + init_sequence = ili9881c_init_sequence; + init_sequence_len = sizeof(ili9881c_init_sequence); + hsync_pulse_width = 40; + hsync_back_porch = 140; + hsync_front_porch = 40; + vsync_pulse_width = 4; + vsync_back_porch = 20; + vsync_front_porch = 20; + } else if (has_st7123) { + // ST7123 display + bus_frequency = 965000000; + pixel_clock_frequency = 70000000; + init_sequence = NULL; // ST7123 doesn't use init sequence + init_sequence_len = 0; + hsync_pulse_width = 2; + hsync_back_porch = 40; + hsync_front_porch = 40; + vsync_pulse_width = 2; + vsync_back_porch = 8; + vsync_front_porch = 220; + + // Not tested now + return; + } else { + return; + } + + // Initialize the statically allocated MIPI DSI bus + board_mipidsi_bus.base.type = &mipidsi_bus_type; + common_hal_mipidsi_bus_construct(&board_mipidsi_bus, bus_frequency, 2); // 2 lanes + + // Allocate display bus for the display object + primary_display_bus_t *display_bus_obj = allocate_display_bus_or_raise(); + mipidsi_display_obj_t *display = &display_bus_obj->mipidsi; + display->base.type = &mipidsi_display_type; + + common_hal_mipidsi_display_construct( + display, + &board_mipidsi_bus, // Use statically allocated bus + init_sequence, + init_sequence_len, + 0, // virtual_channel + 720, // width + 1280, // height + 0, // rotation + 16, // color_depth + &pin_GPIO22, // backlight_pin (LCD_BL) + 0.1f, // brightness + 60, // native_frames_per_second + true, // backlight_on_high + hsync_pulse_width, + hsync_back_porch, + hsync_front_porch, + vsync_pulse_width, + vsync_back_porch, + vsync_front_porch, + pixel_clock_frequency + ); + + // Create framebuffer display + framebufferio_framebufferdisplay_obj_t *fb_display = &allocate_display()->framebuffer_display; + fb_display->base.type = &framebufferio_framebufferdisplay_type; + + common_hal_framebufferio_framebufferdisplay_construct( + fb_display, + MP_OBJ_FROM_PTR(display), + 0, // rotation + true // auto_refresh + ); +} // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. From 39332eb957b274d66fae4575eaeea1ee446070a9 Mon Sep 17 00:00:00 2001 From: sam blenny <68084116+samblenny@users.noreply.github.com> Date: Mon, 8 Dec 2025 22:43:10 +0000 Subject: [PATCH 245/517] fix hashlib.new() documentation comment --- shared-bindings/hashlib/__init__.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/hashlib/__init__.c b/shared-bindings/hashlib/__init__.c index bfe19dee10794..2a6517791be95 100644 --- a/shared-bindings/hashlib/__init__.c +++ b/shared-bindings/hashlib/__init__.c @@ -20,7 +20,7 @@ //| //| def new(name: str, data: bytes = b"") -> hashlib.Hash: //| """Returns a Hash object setup for the named algorithm. Raises ValueError when the named -//| algorithm is unsupported. +//| algorithm is unsupported. Supported algorithms for ``name`` are ``'sha1`` and ``'sha256'``. //| //| :return: a hash object for the given algorithm //| :rtype: hashlib.Hash""" From 9133ea2273fd034117d90d8944c0460c9b993937 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 8 Dec 2025 16:15:17 -0800 Subject: [PATCH 246/517] Add board.DISPLAY() --- ports/espressif/boards/m5stack_tab5/pins.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/espressif/boards/m5stack_tab5/pins.c b/ports/espressif/boards/m5stack_tab5/pins.c index 67012cc670df2..c64d8803c02cf 100644 --- a/ports/espressif/boards/m5stack_tab5/pins.c +++ b/ports/espressif/boards/m5stack_tab5/pins.c @@ -6,6 +6,8 @@ #include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS @@ -124,5 +126,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)} }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 7cfb1cb60faa0481bd77f20112c9f0f01c88bfa4 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 8 Dec 2025 16:19:15 -0800 Subject: [PATCH 247/517] Fix USB on P4 EV mpconfigport.h can't set a default to override circuitpy_mpconfig.h So, when using the high speed device we need to set two defines. Also, fix warning from IDF about selecting UTMI with high speed. --- .../boards/espressif_esp32p4_function_ev/mpconfigboard.h | 1 + ports/espressif/mpconfigport.h | 8 -------- ports/espressif/supervisor/usb.c | 4 ++++ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h b/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h index 1bbf3a250fcd2..89a103e619625 100644 --- a/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h +++ b/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h @@ -21,5 +21,6 @@ // Use the second USB device (numbered 0 and 1) #define CIRCUITPY_USB_DEVICE_INSTANCE 1 +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) #define CIRCUITPY_USB_HOST_INSTANCE 0 diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 6b85100608c65..7f1a581571eb2 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -69,14 +69,6 @@ extern portMUX_TYPE background_task_mutex; #define CIRCUITPY_WIFI_DEFAULT_TX_POWER (20) #endif -#ifndef CIRCUITPY_USB_DEVICE_HIGH_SPEED -#if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 1 -#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) -#else -#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (0) -#endif -#endif - #ifndef CIRCUITPY_ESP32P4_SWAP_LSFS #define CIRCUITPY_ESP32P4_SWAP_LSFS (0) #endif diff --git a/ports/espressif/supervisor/usb.c b/ports/espressif/supervisor/usb.c index 56042fce1b7f5..62feea6981e05 100644 --- a/ports/espressif/supervisor/usb.c +++ b/ports/espressif/supervisor/usb.c @@ -66,7 +66,11 @@ void init_usb_hardware(void) { // Configure USB PHY usb_phy_config_t phy_conf = { .controller = USB_PHY_CTRL_OTG, + #if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 1 + .target = USB_PHY_TARGET_UTMI, + #else .target = USB_PHY_TARGET_INT, + #endif .otg_mode = USB_OTG_MODE_DEVICE, #if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 0 .otg_speed = USB_PHY_SPEED_FULL, From 75bf3bd1506f5f2362de27b5300e614f73ad1e84 Mon Sep 17 00:00:00 2001 From: Jerzeek Date: Tue, 9 Dec 2025 10:15:49 +0100 Subject: [PATCH 248/517] correct pin order --- .../studiolab_picoexpander/mpconfigboard.h | 2 +- .../studiolab_picoexpander/mpconfigboard.mk | 2 +- .../boards/studiolab_picoexpander/pins.c | 97 +++++++++++-------- 3 files changed, 60 insertions(+), 41 deletions(-) diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h index 5e2dd69f290d0..98e011cee9968 100644 --- a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h +++ b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT -#define MICROPY_HW_BOARD_NAME "Pico Expander" +#define MICROPY_HW_BOARD_NAME "StudioLabPico Expander" #define MICROPY_HW_MCU_NAME "rp2350a" #define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk index 9db1547907663..eabc2c41e0722 100644 --- a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk +++ b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk @@ -1,6 +1,6 @@ USB_VID = 0x1209 USB_PID = 0xC1C1 -USB_PRODUCT = "Pico 2 W" +USB_PRODUCT = "StudioLab Pico Expander" USB_MANUFACTURER = "Raspberry Pi" CHIP_VARIANT = RP2350 diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/pins.c b/ports/raspberrypi/boards/studiolab_picoexpander/pins.c index f2c89251149e3..6faafbfd177ea 100644 --- a/ports/raspberrypi/boards/studiolab_picoexpander/pins.c +++ b/ports/raspberrypi/boards/studiolab_picoexpander/pins.c @@ -9,84 +9,103 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, - { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, - { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, - { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, - { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, - { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, - { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, - { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, - { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, - { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, - { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, - { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, - { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, - { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, - { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, - { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, - { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_BTN), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO15) }, { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, - { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, - { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO19) }, - { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) }, - { MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_D22), MP_ROM_PTR(&pin_GPIO22) }, - { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, - { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, - { MP_ROM_QSTR(MP_QSTR_BTN), MP_ROM_PTR(&pin_GPIO15) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, - { MP_ROM_QSTR(MP_QSTR_SMPS_MODE), MP_ROM_PTR(&pin_CYW1) }, + { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, - { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_CYW0) }, + { MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_CYW2) }, + { MP_ROM_QSTR(MP_QSTR_D22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_A26), MP_ROM_PTR(&pin_GPIO26) }, { MP_ROM_QSTR(MP_QSTR_GP26_A0), MP_ROM_PTR(&pin_GPIO26) }, { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, - { MP_ROM_QSTR(MP_QSTR_A26), MP_ROM_PTR(&pin_GPIO26) }, { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, - + { MP_ROM_QSTR(MP_QSTR_A27), MP_ROM_PTR(&pin_GPIO27) }, { MP_ROM_QSTR(MP_QSTR_GP27_A1), MP_ROM_PTR(&pin_GPIO27) }, { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, - { MP_ROM_QSTR(MP_QSTR_A27), MP_ROM_PTR(&pin_GPIO27) }, { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A28), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_GP28_A2), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, - { MP_ROM_QSTR(MP_QSTR_A28), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_SMPS_MODE), MP_ROM_PTR(&pin_CYW1) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_CYW0) }, + + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_CYW2) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, From cb8ad1fc90813354d7fc1d2dfac192216790feb7 Mon Sep 17 00:00:00 2001 From: Jerzeek Date: Tue, 9 Dec 2025 10:47:45 +0100 Subject: [PATCH 249/517] Correct board name --- ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h index 98e011cee9968..ea475f6822261 100644 --- a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h +++ b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT -#define MICROPY_HW_BOARD_NAME "StudioLabPico Expander" +#define MICROPY_HW_BOARD_NAME "Studiolab Pico Expander" #define MICROPY_HW_MCU_NAME "rp2350a" #define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) From c8274f99a616788f77d0a3696fa4c8196230d8a1 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 9 Dec 2025 09:32:54 -0600 Subject: [PATCH 250/517] update frozen libs --- frozen/Adafruit_CircuitPython_BLE | 2 +- frozen/Adafruit_CircuitPython_Display_Text | 2 +- frozen/Adafruit_CircuitPython_ESP32SPI | 2 +- frozen/Adafruit_CircuitPython_LSM6DS | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_BLE b/frozen/Adafruit_CircuitPython_BLE index 6744b6869e4c3..0058a10ba53e2 160000 --- a/frozen/Adafruit_CircuitPython_BLE +++ b/frozen/Adafruit_CircuitPython_BLE @@ -1 +1 @@ -Subproject commit 6744b6869e4c3112610132bcac23535ae5ef3983 +Subproject commit 0058a10ba53e2d7bd6bcc84c7bf10dd30ee998dd diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index 727a1022e140b..0c4ea1b8c102c 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit 727a1022e140b971a2f4bde5e6571dd327f6785d +Subproject commit 0c4ea1b8c102cdd98c91914dd09f067ea6f7b1e1 diff --git a/frozen/Adafruit_CircuitPython_ESP32SPI b/frozen/Adafruit_CircuitPython_ESP32SPI index 3fcea236876b0..0b24c52f472f8 160000 --- a/frozen/Adafruit_CircuitPython_ESP32SPI +++ b/frozen/Adafruit_CircuitPython_ESP32SPI @@ -1 +1 @@ -Subproject commit 3fcea236876b05d09ebf95f43cec6016667ccf84 +Subproject commit 0b24c52f472f870fcd417301521a6f7895416a4e diff --git a/frozen/Adafruit_CircuitPython_LSM6DS b/frozen/Adafruit_CircuitPython_LSM6DS index e7da74fd8d7fd..2bf5c7fcadbb0 160000 --- a/frozen/Adafruit_CircuitPython_LSM6DS +++ b/frozen/Adafruit_CircuitPython_LSM6DS @@ -1 +1 @@ -Subproject commit e7da74fd8d7fddd9515e975be5479596283a719c +Subproject commit 2bf5c7fcadbb0034c0d193c5c45409f435e7329c From d38759b297e9405a5017bd9674a25a8985bf88b2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 9 Dec 2025 09:49:53 -0800 Subject: [PATCH 251/517] Fix zephyr_i2c stub --- ports/zephyr-cp/bindings/zephyr_i2c/I2C.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c index 456f3703ff553..5d9f305a479f5 100644 --- a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c +++ b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c @@ -275,9 +275,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_i2c_i2c_writeto_obj, 3, zephyr_i2c_i2c_writeto //| in_buffer: WriteableBuffer, //| *, //| out_start: int = 0, -//| out_end: int = None, +//| out_end: int = sys.maxsize, //| in_start: int = 0, -//| in_end: int = None +//| in_end: int = sys.maxsize //| ) -> None: //| """Write the bytes from ``out_buffer`` to the device selected by ``address``, generate //| no stop bit, generate a repeated start and read into ``in_buffer``. ``out_buffer`` and From cf9f4a5c114b45f37384a90d7328736d898d0d33 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 9 Dec 2025 13:29:59 -0800 Subject: [PATCH 252/517] Add SPI and merge zephyr_(serial|i2c) into busio Also: * Add enabling modules based on other available modules. (Such as displayio when busio is available.) * Implement ticks for display refreshes. * Be more rigorous finding flash and RAM regions. Add tests. --- .github/workflows/run-tests.yml | 84 ++- locale/circuitpython.pot | 14 +- ports/zephyr-cp/Makefile | 13 +- ports/zephyr-cp/bindings/zephyr_i2c/I2C.c | 378 ------------ ports/zephyr-cp/bindings/zephyr_i2c/I2C.h | 32 - .../zephyr-cp/bindings/zephyr_i2c/__init__.c | 27 - ports/zephyr-cp/bindings/zephyr_serial/UART.c | 291 --------- ports/zephyr-cp/bindings/zephyr_serial/UART.h | 41 -- .../bindings/zephyr_serial/__init__.c | 32 - .../bindings/zephyr_serial/__init__.h | 7 - .../nordic/nrf5340dk/autogen_board_info.toml | 42 +- .../nordic/nrf54h20dk/autogen_board_info.toml | 42 +- .../nordic/nrf54l15dk/autogen_board_info.toml | 42 +- .../nordic/nrf7002dk/autogen_board_info.toml | 42 +- .../renesas/ek_ra6m5/autogen_board_info.toml | 42 +- .../renesas/ek_ra8d1/autogen_board_info.toml | 42 +- .../nucleo_n657x0_q/autogen_board_info.toml | 42 +- .../nucleo_u575zi_q/autogen_board_info.toml | 42 +- .../st/stm32h7b3i_dk/autogen_board_info.toml | 42 +- .../common-hal/{zephyr_i2c => busio}/I2C.c | 58 +- .../common-hal/{zephyr_i2c => busio}/I2C.h | 5 +- ports/zephyr-cp/common-hal/busio/SPI.c | 281 +++++++++ ports/zephyr-cp/common-hal/busio/SPI.h | 24 + ports/zephyr-cp/common-hal/busio/UART.c | 149 +++++ .../{zephyr_serial => busio}/UART.h | 8 +- .../{zephyr_spi => busio}/__init__.c | 4 +- .../common-hal/zephyr_i2c/__init__.c | 7 - .../zephyr-cp/common-hal/zephyr_serial/UART.c | 136 ----- .../common-hal/zephyr_serial/__init__.c | 7 - ports/zephyr-cp/common-hal/zephyr_spi/SPI.c | 74 --- ports/zephyr-cp/common-hal/zephyr_spi/SPI.h | 20 - ports/zephyr-cp/cptools/build_all_boards.py | 165 ++++++ .../zephyr-cp/cptools/build_circuitpython.py | 151 ++++- ports/zephyr-cp/cptools/tests/README.md | 28 + .../zephyr-cp/cptools/tests/test_zephyr2cp.py | 550 ++++++++++++++++++ ports/zephyr-cp/cptools/zephyr2cp.py | 245 ++++++-- ports/zephyr-cp/prj.conf | 2 + ports/zephyr-cp/supervisor/port.c | 11 +- ports/zephyr-cp/supervisor/serial.c | 16 +- requirements-dev.txt | 1 + shared-bindings/board/__init__.h | 3 - shared-bindings/busio/I2C.c | 2 +- shared-bindings/busio/SPI.c | 2 +- shared-module/board/__init__.c | 27 - shared-module/displayio/Shape.c | 126 ---- shared-module/displayio/Shape.h | 28 - shared-module/displayio/__init__.c | 45 +- .../sharpdisplay/SharpMemoryFramebuffer.c | 6 +- supervisor/shared/display.c | 2 +- tools/gen_display_resources.py | 4 +- 50 files changed, 1852 insertions(+), 1632 deletions(-) delete mode 100644 ports/zephyr-cp/bindings/zephyr_i2c/I2C.c delete mode 100644 ports/zephyr-cp/bindings/zephyr_i2c/I2C.h delete mode 100644 ports/zephyr-cp/bindings/zephyr_i2c/__init__.c delete mode 100644 ports/zephyr-cp/bindings/zephyr_serial/UART.c delete mode 100644 ports/zephyr-cp/bindings/zephyr_serial/UART.h delete mode 100644 ports/zephyr-cp/bindings/zephyr_serial/__init__.c delete mode 100644 ports/zephyr-cp/bindings/zephyr_serial/__init__.h rename ports/zephyr-cp/common-hal/{zephyr_i2c => busio}/I2C.c (56%) rename ports/zephyr-cp/common-hal/{zephyr_i2c => busio}/I2C.h (67%) create mode 100644 ports/zephyr-cp/common-hal/busio/SPI.c create mode 100644 ports/zephyr-cp/common-hal/busio/SPI.h create mode 100644 ports/zephyr-cp/common-hal/busio/UART.c rename ports/zephyr-cp/common-hal/{zephyr_serial => busio}/UART.h (54%) rename ports/zephyr-cp/common-hal/{zephyr_spi => busio}/__init__.c (55%) delete mode 100644 ports/zephyr-cp/common-hal/zephyr_i2c/__init__.c delete mode 100644 ports/zephyr-cp/common-hal/zephyr_serial/UART.c delete mode 100644 ports/zephyr-cp/common-hal/zephyr_serial/__init__.c delete mode 100644 ports/zephyr-cp/common-hal/zephyr_spi/SPI.c delete mode 100644 ports/zephyr-cp/common-hal/zephyr_spi/SPI.h create mode 100755 ports/zephyr-cp/cptools/build_all_boards.py create mode 100644 ports/zephyr-cp/cptools/tests/README.md create mode 100644 ports/zephyr-cp/cptools/tests/test_zephyr2cp.py delete mode 100644 shared-module/displayio/Shape.c delete mode 100644 shared-module/displayio/Shape.h diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 80bfc72bd9812..06fe3bf6aea29 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -23,36 +23,36 @@ jobs: TEST_native: --emit native TEST_native_mpy: --via-mpy --emit native -d basics float micropython steps: - - name: Set up repository - uses: actions/checkout@v4 - with: - submodules: false - show-progress: false - fetch-depth: 1 - - name: Set up python - uses: actions/setup-python@v5 - with: - python-version: 3.12 - - name: Set up submodules - uses: ./.github/actions/deps/submodules - with: - target: tests - - name: Set up external - if: matrix.test == 'all' - uses: ./.github/actions/deps/external - - name: Set up mpy-cross - uses: ./.github/actions/mpy_cross - with: - cp-version: ${{ inputs.cp-version }} - - name: Build unix port - run: make -C ports/unix VARIANT=coverage -j4 - - name: Run tests - run: ./run-tests.py -j4 ${{ env[format('TEST_{0}', matrix.test)] }} - working-directory: tests - - name: Print failure info - run: ./run-tests.py -j4 --print-failures - if: failure() - working-directory: tests + - name: Set up repository + uses: actions/checkout@v4 + with: + submodules: false + show-progress: false + fetch-depth: 1 + - name: Set up python + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - name: Set up submodules + uses: ./.github/actions/deps/submodules + with: + target: tests + - name: Set up external + if: matrix.test == 'all' + uses: ./.github/actions/deps/external + - name: Set up mpy-cross + uses: ./.github/actions/mpy_cross + with: + cp-version: ${{ inputs.cp-version }} + - name: Build unix port + run: make -C ports/unix VARIANT=coverage -j4 + - name: Run tests + run: ./run-tests.py -j4 ${{ env[format('TEST_{0}', matrix.test)] }} + working-directory: tests + - name: Print failure info + run: ./run-tests.py -j4 --print-failures + if: failure() + working-directory: tests # Not working after MicroPython v1.23 merge. # - name: Build native modules # if: matrix.test == 'all' @@ -66,3 +66,27 @@ jobs: # if: matrix.test == 'all' # run: ./run-natmodtests.py extmod/{heapq*,random*,re*}.py # working-directory: tests + + zephyr: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + env: + CP_VERSION: ${{ inputs.cp-version }} + steps: + - name: Set up repository + uses: actions/checkout@v4 + with: + submodules: false + show-progress: false + fetch-depth: 1 + - name: Set up python + uses: actions/setup-python@v5 + with: + python-version: 3.13 + - name: Set up Zephyr + uses: ./.github/actions/deps/ports/zephyr-cp + - name: Set up external + uses: ./.github/actions/deps/external + - name: Run Zephyr build tests + run: make -C ports/zephyr-cp test diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 81074f3f80464..bcc7d1dd1ca9d 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1117,9 +1117,8 @@ msgid "" "Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c -#: shared-bindings/busio/SPI.c +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "" @@ -1462,8 +1461,7 @@ msgstr "" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -2396,6 +2394,12 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index 217e094e3dc0e..037c14afad4c3 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -8,7 +8,7 @@ BUILD ?= build-$(BOARD) TRANSLATION ?= en_US -.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug clean menuconfig +.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug clean menuconfig all clean-all test $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) @@ -31,3 +31,14 @@ menuconfig: clean: rm -rf $(BUILD) + +# Build all boards. The + prefix allows jobserver file descriptors to be passed through. +# This enables parallel builds across all boards when invoked with `make -jN all`. +all: + +python cptools/build_all_boards.py --continue-on-error + +clean-all: + rm -rf build build-* + +test: + pytest cptools/tests diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c deleted file mode 100644 index 5d9f305a479f5..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.c +++ /dev/null @@ -1,378 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "bindings/zephyr_i2c/I2C.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/util.h" -#include "shared/runtime/buffer_helper.h" -#include "py/binary.h" -#include "py/mperrno.h" -#include "py/runtime.h" - -#include -#include - -//| class I2C: -//| """Two wire serial protocol -//| -//| I2C is a two-wire protocol for communicating between devices. At the -//| physical level it consists of 2 wires: SCL and SDA, the clock and data -//| lines respectively. -//| -//| .. class:: I2C() -//| -//| Cannot be instantiated directly. Instead singletons are created using the -//| ``board`` aliases that match the device tree labels. ``board`` may list multiple -//| aliases for a single device. For example, ``board.I2C1()`` and ``board.ARDUINO_I2C()`` -//| may both refer to the same device. -//| """ -//| - -static zephyr_i2c_i2c_obj_t *native_i2c(mp_obj_t self_in) { - mp_check_self(mp_obj_is_type(self_in, &zephyr_i2c_i2c_type)); - return MP_OBJ_TO_PTR(self_in); -} - -static void check_for_deinit(zephyr_i2c_i2c_obj_t *self) { - if (common_hal_zephyr_i2c_i2c_deinited(self)) { - raise_deinited_error(); - } -} - -static void check_lock(zephyr_i2c_i2c_obj_t *self) { - if (!common_hal_zephyr_i2c_i2c_has_lock(self)) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Function requires lock")); - } -} - -//| def deinit(self) -> None: -//| """Releases control of the underlying hardware so other classes can use it.""" -//| ... -static mp_obj_t zephyr_i2c_i2c_obj_deinit(mp_obj_t self_in) { - zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); - common_hal_zephyr_i2c_i2c_deinit(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c_deinit_obj, zephyr_i2c_i2c_obj_deinit); - -//| def __enter__(self) -> I2C: -//| """No-op used in Context Managers.""" -//| ... -static mp_obj_t zephyr_i2c_i2c_obj___enter__(mp_obj_t self_in) { - zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); - check_for_deinit(self); - return self_in; -} -static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c___enter___obj, zephyr_i2c_i2c_obj___enter__); - -//| def __exit__(self) -> None: -//| """Automatically deinitializes the hardware on context exit.""" -//| ... -static mp_obj_t zephyr_i2c_i2c_obj___exit__(size_t n_args, const mp_obj_t *args) { - (void)n_args; - common_hal_zephyr_i2c_i2c_deinit(args[0]); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(zephyr_i2c_i2c___exit___obj, 4, 4, zephyr_i2c_i2c_obj___exit__); - -//| def probe(self, address: int) -> bool: -//| """Check if a device responds at the address. -//| -//| :param int address: The 7-bit device address -//| :return: True if the device responds, False otherwise -//| :rtype: bool -//| """ -//| ... -static mp_obj_t zephyr_i2c_i2c_probe(mp_obj_t self_in, mp_obj_t addr_obj) { - zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); - check_for_deinit(self); - check_lock(self); - - mp_int_t addr = mp_obj_get_int(addr_obj); - return mp_obj_new_bool(common_hal_zephyr_i2c_i2c_probe(self, addr)); -} -static MP_DEFINE_CONST_FUN_OBJ_2(zephyr_i2c_i2c_probe_obj, zephyr_i2c_i2c_probe); - -//| def scan(self) -> List[int]: -//| """Scan all I2C addresses between 0x08 and 0x77 inclusive and return a -//| list of those that respond. -//| -//| :return: List of device addresses -//| :rtype: list -//| """ -//| ... -static mp_obj_t zephyr_i2c_i2c_scan(mp_obj_t self_in) { - zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); - check_for_deinit(self); - check_lock(self); - - mp_obj_t list = mp_obj_new_list(0, NULL); - // 7-bit addresses 0b0000xxx and 0b1111xxx are reserved - for (uint8_t addr = 0x08; addr <= 0x77; addr++) { - if (common_hal_zephyr_i2c_i2c_probe(self, addr)) { - mp_obj_list_append(list, MP_OBJ_NEW_SMALL_INT(addr)); - } - } - return list; -} -static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c_scan_obj, zephyr_i2c_i2c_scan); - -//| def try_lock(self) -> bool: -//| """Attempts to grab the I2C lock. Returns True on success. -//| -//| :return: True when lock has been grabbed -//| :rtype: bool -//| """ -//| ... -static mp_obj_t zephyr_i2c_i2c_obj_try_lock(mp_obj_t self_in) { - zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); - check_for_deinit(self); - return mp_obj_new_bool(common_hal_zephyr_i2c_i2c_try_lock(self)); -} -static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c_try_lock_obj, zephyr_i2c_i2c_obj_try_lock); - -//| def unlock(self) -> None: -//| """Releases the I2C lock.""" -//| ... -static mp_obj_t zephyr_i2c_i2c_obj_unlock(mp_obj_t self_in) { - zephyr_i2c_i2c_obj_t *self = native_i2c(self_in); - check_for_deinit(self); - common_hal_zephyr_i2c_i2c_unlock(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(zephyr_i2c_i2c_unlock_obj, zephyr_i2c_i2c_obj_unlock); - -//| import sys -//| -//| def readfrom_into( -//| self, address: int, buffer: WriteableBuffer, *, start: int = 0, end: int = sys.maxsize -//| ) -> None: -//| """Read into ``buffer`` from the device selected by ``address``. -//| At least one byte must be read. -//| -//| If ``start`` or ``end`` is provided, then the buffer will be sliced -//| as if ``buffer[start:end]`` were passed, but without copying the data. -//| The number of bytes read will be the length of ``buffer[start:end]``. -//| -//| :param int address: 7-bit device address -//| :param ~circuitpython_typing.WriteableBuffer buffer: buffer to write into -//| :param int start: beginning of buffer slice -//| :param int end: end of buffer slice; if not specified, use ``len(buffer)`` -//| """ -//| ... -static mp_obj_t zephyr_i2c_i2c_readfrom_into(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_address, ARG_buffer, ARG_start, ARG_end }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, - }; - zephyr_i2c_i2c_obj_t *self = native_i2c(pos_args[0]); - check_for_deinit(self); - check_lock(self); - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); - - // Compute bounds in terms of elements, not bytes. - int stride_in_bytes = mp_binary_get_size('@', bufinfo.typecode, NULL); - size_t length = bufinfo.len / stride_in_bytes; - int32_t start = args[ARG_start].u_int; - const int32_t end = args[ARG_end].u_int; - normalize_buffer_bounds(&start, end, &length); - mp_arg_validate_length_min(length, 1, MP_QSTR_buffer); - - // Treat start and length in terms of bytes from now on. - start *= stride_in_bytes; - length *= stride_in_bytes; - - uint8_t status = common_hal_zephyr_i2c_i2c_read(self, args[ARG_address].u_int, - ((uint8_t *)bufinfo.buf) + start, length); - - if (status != 0) { - mp_raise_OSError(status); - } - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_i2c_i2c_readfrom_into_obj, 3, zephyr_i2c_i2c_readfrom_into); - -//| import sys -//| -//| def writeto( -//| self, address: int, buffer: ReadableBuffer, *, start: int = 0, end: int = sys.maxsize -//| ) -> None: -//| """Write the bytes from ``buffer`` to the device selected by ``address`` and -//| then transmit a stop bit. -//| -//| If ``start`` or ``end`` is provided, then the buffer will be sliced -//| as if ``buffer[start:end]`` were passed, but without copying the data. -//| The number of bytes written will be the length of ``buffer[start:end]``. -//| -//| Writing a buffer or slice of length zero is permitted, as it can be used -//| to poll for the existence of a device. -//| -//| :param int address: 7-bit device address -//| :param ~circuitpython_typing.ReadableBuffer buffer: buffer containing the bytes to write -//| :param int start: beginning of buffer slice -//| :param int end: end of buffer slice; if not specified, use ``len(buffer)`` -//| """ -//| ... -static mp_obj_t zephyr_i2c_i2c_writeto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_address, ARG_buffer, ARG_start, ARG_end }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, - }; - zephyr_i2c_i2c_obj_t *self = native_i2c(pos_args[0]); - check_for_deinit(self); - check_lock(self); - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - // get the buffer to write the data from - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); - int stride_in_bytes = mp_binary_get_size('@', bufinfo.typecode, NULL); - - // Compute bounds in terms of elements, not bytes. - size_t length = bufinfo.len / stride_in_bytes; - int32_t start = args[ARG_start].u_int; - const int32_t end = args[ARG_end].u_int; - normalize_buffer_bounds(&start, end, &length); - - // Treat start and length in terms of bytes from now on. - start *= stride_in_bytes; - length *= stride_in_bytes; - - uint8_t status = common_hal_zephyr_i2c_i2c_write(self, args[ARG_address].u_int, - ((uint8_t *)bufinfo.buf) + start, length); - - if (status != 0) { - mp_raise_OSError(status); - } - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_i2c_i2c_writeto_obj, 3, zephyr_i2c_i2c_writeto); - -//| import sys -//| -//| def writeto_then_readfrom( -//| self, -//| address: int, -//| out_buffer: ReadableBuffer, -//| in_buffer: WriteableBuffer, -//| *, -//| out_start: int = 0, -//| out_end: int = sys.maxsize, -//| in_start: int = 0, -//| in_end: int = sys.maxsize -//| ) -> None: -//| """Write the bytes from ``out_buffer`` to the device selected by ``address``, generate -//| no stop bit, generate a repeated start and read into ``in_buffer``. ``out_buffer`` and -//| ``in_buffer`` can be the same buffer because they are used sequentially. -//| -//| If ``out_start`` or ``out_end`` is provided, then the buffer will be sliced -//| as if ``out_buffer[out_start:out_end]`` were passed, but without copying the data. -//| The number of bytes written will be the length of ``out_buffer[start:end]``. -//| -//| If ``in_start`` or ``in_end`` is provided, then the input buffer will be sliced -//| as if ``in_buffer[in_start:in_end]`` were passed, -//| The number of bytes read will be the length of ``out_buffer[in_start:in_end]``. -//| -//| :param int address: 7-bit device address -//| :param ~circuitpython_typing.ReadableBuffer out_buffer: buffer containing the bytes to write -//| :param ~circuitpython_typing.WriteableBuffer in_buffer: buffer to write into -//| :param int out_start: beginning of ``out_buffer`` slice -//| :param int out_end: end of ``out_buffer`` slice; if not specified, use ``len(out_buffer)`` -//| :param int in_start: beginning of ``in_buffer`` slice -//| :param int in_end: end of ``in_buffer`` slice; if not specified, use ``len(in_buffer)`` -//| """ -//| ... -static mp_obj_t zephyr_i2c_i2c_writeto_then_readfrom(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_address, ARG_out_buffer, ARG_in_buffer, ARG_out_start, ARG_out_end, ARG_in_start, ARG_in_end }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, - { MP_QSTR_out_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_in_buffer, MP_ARG_REQUIRED | MP_ARG_OBJ }, - { MP_QSTR_out_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_out_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, - { MP_QSTR_in_start, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_in_end, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = INT_MAX} }, - }; - zephyr_i2c_i2c_obj_t *self = native_i2c(pos_args[0]); - check_for_deinit(self); - check_lock(self); - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_buffer_info_t out_bufinfo; - mp_get_buffer_raise(args[ARG_out_buffer].u_obj, &out_bufinfo, MP_BUFFER_READ); - int out_stride_in_bytes = mp_binary_get_size('@', out_bufinfo.typecode, NULL); - size_t out_length = out_bufinfo.len / out_stride_in_bytes; - int32_t out_start = args[ARG_out_start].u_int; - const int32_t out_end = args[ARG_out_end].u_int; - normalize_buffer_bounds(&out_start, out_end, &out_length); - - mp_buffer_info_t in_bufinfo; - mp_get_buffer_raise(args[ARG_in_buffer].u_obj, &in_bufinfo, MP_BUFFER_WRITE); - int in_stride_in_bytes = mp_binary_get_size('@', in_bufinfo.typecode, NULL); - size_t in_length = in_bufinfo.len / in_stride_in_bytes; - int32_t in_start = args[ARG_in_start].u_int; - const int32_t in_end = args[ARG_in_end].u_int; - normalize_buffer_bounds(&in_start, in_end, &in_length); - mp_arg_validate_length_min(in_length, 1, MP_QSTR_out_buffer); - - // Treat start and length in terms of bytes from now on. - out_start *= out_stride_in_bytes; - out_length *= out_stride_in_bytes; - in_start *= in_stride_in_bytes; - in_length *= in_stride_in_bytes; - - uint8_t status = common_hal_zephyr_i2c_i2c_write_read(self, args[ARG_address].u_int, - ((uint8_t *)out_bufinfo.buf) + out_start, out_length, - ((uint8_t *)in_bufinfo.buf) + in_start, in_length); - - if (status != 0) { - mp_raise_OSError(status); - } - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_i2c_i2c_writeto_then_readfrom_obj, 4, zephyr_i2c_i2c_writeto_then_readfrom); - -static const mp_rom_map_elem_t zephyr_i2c_i2c_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&zephyr_i2c_i2c_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&zephyr_i2c_i2c_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&zephyr_i2c_i2c___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&zephyr_i2c_i2c___exit___obj) }, - { MP_ROM_QSTR(MP_QSTR_probe), MP_ROM_PTR(&zephyr_i2c_i2c_probe_obj) }, - { MP_ROM_QSTR(MP_QSTR_scan), MP_ROM_PTR(&zephyr_i2c_i2c_scan_obj) }, - - { MP_ROM_QSTR(MP_QSTR_try_lock), MP_ROM_PTR(&zephyr_i2c_i2c_try_lock_obj) }, - { MP_ROM_QSTR(MP_QSTR_unlock), MP_ROM_PTR(&zephyr_i2c_i2c_unlock_obj) }, - - { MP_ROM_QSTR(MP_QSTR_readfrom_into), MP_ROM_PTR(&zephyr_i2c_i2c_readfrom_into_obj) }, - { MP_ROM_QSTR(MP_QSTR_writeto), MP_ROM_PTR(&zephyr_i2c_i2c_writeto_obj) }, - { MP_ROM_QSTR(MP_QSTR_writeto_then_readfrom), MP_ROM_PTR(&zephyr_i2c_i2c_writeto_then_readfrom_obj) }, -}; -static MP_DEFINE_CONST_DICT(zephyr_i2c_i2c_locals_dict, zephyr_i2c_i2c_locals_dict_table); - -MP_DEFINE_CONST_OBJ_TYPE( - zephyr_i2c_i2c_type, - MP_QSTR_I2C, - MP_TYPE_FLAG_NONE, - locals_dict, &zephyr_i2c_i2c_locals_dict - ); diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.h b/ports/zephyr-cp/bindings/zephyr_i2c/I2C.h deleted file mode 100644 index 7f7eecbea6c62..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_i2c/I2C.h +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "py/obj.h" -#include "common-hal/zephyr_i2c/I2C.h" - -extern const mp_obj_type_t zephyr_i2c_i2c_type; - -// Check if the I2C object has been deinitialized -bool common_hal_zephyr_i2c_i2c_deinited(zephyr_i2c_i2c_obj_t *self); - -// Deinitialize the I2C bus -void common_hal_zephyr_i2c_i2c_deinit(zephyr_i2c_i2c_obj_t *self); - -// Locking functions -bool common_hal_zephyr_i2c_i2c_try_lock(zephyr_i2c_i2c_obj_t *self); -bool common_hal_zephyr_i2c_i2c_has_lock(zephyr_i2c_i2c_obj_t *self); -void common_hal_zephyr_i2c_i2c_unlock(zephyr_i2c_i2c_obj_t *self); - -// Device communication functions -bool common_hal_zephyr_i2c_i2c_probe(zephyr_i2c_i2c_obj_t *self, uint8_t addr); -uint8_t common_hal_zephyr_i2c_i2c_write(zephyr_i2c_i2c_obj_t *self, uint16_t address, - const uint8_t *data, size_t len); -uint8_t common_hal_zephyr_i2c_i2c_read(zephyr_i2c_i2c_obj_t *self, uint16_t address, - uint8_t *data, size_t len); -uint8_t common_hal_zephyr_i2c_i2c_write_read(zephyr_i2c_i2c_obj_t *self, uint16_t address, - uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len); diff --git a/ports/zephyr-cp/bindings/zephyr_i2c/__init__.c b/ports/zephyr-cp/bindings/zephyr_i2c/__init__.c deleted file mode 100644 index 3274dfdb74099..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_i2c/__init__.c +++ /dev/null @@ -1,27 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "py/obj.h" -#include "py/runtime.h" -#include "bindings/zephyr_i2c/I2C.h" - -//| """Zephyr I2C driver for fixed I2C busses. -//| -//| This module provides access to I2C busses using Zephyr device labels.""" - -static const mp_rom_map_elem_t zephyr_i2c_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr_i2c) }, - { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&zephyr_i2c_i2c_type) }, -}; - -static MP_DEFINE_CONST_DICT(zephyr_i2c_module_globals, zephyr_i2c_module_globals_table); - -const mp_obj_module_t zephyr_i2c_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&zephyr_i2c_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_zephyr_i2c, zephyr_i2c_module); diff --git a/ports/zephyr-cp/bindings/zephyr_serial/UART.c b/ports/zephyr-cp/bindings/zephyr_serial/UART.c deleted file mode 100644 index dbb643645f5b9..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_serial/UART.c +++ /dev/null @@ -1,291 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include - -#include "bindings/zephyr_serial/UART.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/util.h" - -#include "shared/runtime/context_manager_helpers.h" -#include "shared/runtime/interrupt_char.h" - -#include "py/stream.h" -#include "py/objproperty.h" -#include "py/objtype.h" -#include "py/runtime.h" -#include "py/stream.h" - -#define STREAM_DEBUG(...) (void)0 -// #define STREAM_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) - -//| class UART: -//| """A bidirectional serial protocol. Already initialized for Zephyr defined -//| busses in :py:mod:`board`. -//| -//| .. raw:: html -//| -//|

-//|

-//| Available on these boards -//|
    -//| {% for board in support_matrix_reverse["zephyr_serial.UART"] %} -//|
  • {{ board }} -//| {% endfor %} -//|
-//|
-//|

-//| -//| """ -//| - -static void validate_timeout(mp_float_t timeout) { - mp_arg_validate_int_range((int)timeout, 0, 100, MP_QSTR_timeout); -} - -// Helper to ensure we have the native super class instead of a subclass. -static zephyr_serial_uart_obj_t *native_uart(mp_obj_t uart_obj) { - mp_obj_t native_uart = mp_obj_cast_to_native_base(uart_obj, MP_OBJ_FROM_PTR(&zephyr_serial_uart_type)); - if (native_uart == MP_OBJ_NULL) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("Must be a %q subclass."), MP_QSTR_UART); - } - mp_obj_assert_native_inited(native_uart); - return MP_OBJ_TO_PTR(native_uart); -} - - -//| def deinit(self) -> None: -//| """Deinitialises the UART and releases any hardware resources for reuse.""" -//| ... -//| -static mp_obj_t _zephyr_serial_uart_obj_deinit(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - zephyr_serial_uart_deinit(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_deinit_obj, _zephyr_serial_uart_obj_deinit); - -static void check_for_deinit(zephyr_serial_uart_obj_t *self) { - if (zephyr_serial_uart_deinited(self)) { - raise_deinited_error(); - } -} - -//| def __enter__(self) -> UART: -//| """No-op used by Context Managers.""" -//| ... -//| -// Provided by context manager helper. - -//| def __exit__(self) -> None: -//| """Automatically deinitializes the hardware when exiting a context. See -//| :ref:`lifetime-and-contextmanagers` for more info.""" -//| ... -//| -// Provided by context manager helper. - -// These are standard stream methods. Code is in py/stream.c. -// -//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]: -//| """Read bytes. If ``nbytes`` is specified then read at most that many -//| bytes. Otherwise, read everything that arrives until the connection -//| times out. Providing the number of bytes expected is highly recommended -//| because it will be faster. If no bytes are read, return ``None``. -//| -//| .. note:: When no bytes are read due to a timeout, this function returns ``None``. -//| This matches the behavior of `io.RawIOBase.read` in Python 3, but -//| differs from pyserial which returns ``b''`` in that situation. -//| -//| :return: Data read -//| :rtype: bytes or None""" -//| ... -//| - -//| def readinto(self, buf: WriteableBuffer) -> Optional[int]: -//| """Read bytes into the ``buf``. Read at most ``len(buf)`` bytes. -//| -//| :return: number of bytes read and stored into ``buf`` -//| :rtype: int or None (on a non-blocking error) -//| -//| *New in CircuitPython 4.0:* No length parameter is permitted.""" -//| ... -//| - -//| def readline(self) -> bytes: -//| """Read a line, ending in a newline character, or -//| return ``None`` if a timeout occurs sooner, or -//| return everything readable if no newline is found and -//| ``timeout=0`` -//| -//| :return: the line read -//| :rtype: bytes or None""" -//| ... -//| - -//| def write(self, buf: ReadableBuffer) -> Optional[int]: -//| """Write the buffer of bytes to the bus. -//| -//| *New in CircuitPython 4.0:* ``buf`` must be bytes, not a string. -//| -//| :return: the number of bytes written -//| :rtype: int or None""" -//| ... -//| - -// These three methods are used by the shared stream methods. -static mp_uint_t _zephyr_serial_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { - STREAM_DEBUG("_zephyr_serial_uart_read stream %d\n", size); - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - byte *buf = buf_in; - - // make sure we want at least 1 char - if (size == 0) { - return 0; - } - - return zephyr_serial_uart_read(self, buf, size, errcode); -} - -static mp_uint_t _zephyr_serial_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - const byte *buf = buf_in; - - return zephyr_serial_uart_write(self, buf, size, errcode); -} - -static mp_uint_t _zephyr_serial_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - mp_uint_t ret; - if (request == MP_STREAM_POLL) { - mp_uint_t flags = arg; - ret = 0; - if ((flags & MP_STREAM_POLL_RD) && zephyr_serial_uart_rx_characters_available(self) > 0) { - ret |= MP_STREAM_POLL_RD; - } - if ((flags & MP_STREAM_POLL_WR) && zephyr_serial_uart_ready_to_tx(self)) { - ret |= MP_STREAM_POLL_WR; - } - } else { - *errcode = MP_EINVAL; - ret = MP_STREAM_ERROR; - } - return ret; -} - -//| baudrate: int -//| """The current baudrate.""" -static mp_obj_t _zephyr_serial_uart_obj_get_baudrate(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - return MP_OBJ_NEW_SMALL_INT(zephyr_serial_uart_get_baudrate(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_baudrate_obj, _zephyr_serial_uart_obj_get_baudrate); - -static mp_obj_t _zephyr_serial_uart_obj_set_baudrate(mp_obj_t self_in, mp_obj_t baudrate) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - zephyr_serial_uart_set_baudrate(self, mp_obj_get_int(baudrate)); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(_zephyr_serial_uart_set_baudrate_obj, _zephyr_serial_uart_obj_set_baudrate); - - -MP_PROPERTY_GETSET(_zephyr_serial_uart_baudrate_obj, - (mp_obj_t)&_zephyr_serial_uart_get_baudrate_obj, - (mp_obj_t)&_zephyr_serial_uart_set_baudrate_obj); - -//| in_waiting: int -//| """The number of bytes in the input buffer, available to be read""" -static mp_obj_t _zephyr_serial_uart_obj_get_in_waiting(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - return MP_OBJ_NEW_SMALL_INT(zephyr_serial_uart_rx_characters_available(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_in_waiting_obj, _zephyr_serial_uart_obj_get_in_waiting); - -MP_PROPERTY_GETTER(_zephyr_serial_uart_in_waiting_obj, - (mp_obj_t)&_zephyr_serial_uart_get_in_waiting_obj); - -//| timeout: float -//| """The current timeout, in seconds (float).""" -//| -static mp_obj_t _zephyr_serial_uart_obj_get_timeout(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - return mp_obj_new_float(zephyr_serial_uart_get_timeout(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_timeout_obj, _zephyr_serial_uart_obj_get_timeout); - -static mp_obj_t _zephyr_serial_uart_obj_set_timeout(mp_obj_t self_in, mp_obj_t timeout) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - mp_float_t timeout_float = mp_obj_get_float(timeout); - validate_timeout(timeout_float); - zephyr_serial_uart_set_timeout(self, timeout_float); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(_zephyr_serial_uart_set_timeout_obj, _zephyr_serial_uart_obj_set_timeout); - - -MP_PROPERTY_GETSET(_zephyr_serial_uart_timeout_obj, - (mp_obj_t)&_zephyr_serial_uart_get_timeout_obj, - (mp_obj_t)&_zephyr_serial_uart_set_timeout_obj); - -//| def reset_input_buffer(self) -> None: -//| """Discard any unread characters in the input buffer.""" -//| ... -//| -//| -static mp_obj_t _zephyr_serial_uart_obj_reset_input_buffer(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - zephyr_serial_uart_clear_rx_buffer(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_reset_input_buffer_obj, _zephyr_serial_uart_obj_reset_input_buffer); - -static const mp_rom_map_elem_t _zephyr_serial_uart_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&_zephyr_serial_uart_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&_zephyr_serial_uart_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, - - // Standard stream methods. - { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)}, - { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, - { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, - - { MP_ROM_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&_zephyr_serial_uart_reset_input_buffer_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&_zephyr_serial_uart_baudrate_obj) }, - { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&_zephyr_serial_uart_in_waiting_obj) }, - { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&_zephyr_serial_uart_timeout_obj) }, - -}; -static MP_DEFINE_CONST_DICT(_zephyr_serial_uart_locals_dict, _zephyr_serial_uart_locals_dict_table); - -static const mp_stream_p_t uart_stream_p = { - .read = _zephyr_serial_uart_read, - .write = _zephyr_serial_uart_write, - .ioctl = _zephyr_serial_uart_ioctl, - .is_text = false, - // Disallow optional length argument for .readinto() - .pyserial_readinto_compatibility = true, -}; - -MP_DEFINE_CONST_OBJ_TYPE( - zephyr_serial_uart_type, - MP_QSTR_UART, - MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, - locals_dict, &_zephyr_serial_uart_locals_dict, - iter, mp_stream_unbuffered_iter, - protocol, &uart_stream_p - ); diff --git a/ports/zephyr-cp/bindings/zephyr_serial/UART.h b/ports/zephyr-cp/bindings/zephyr_serial/UART.h deleted file mode 100644 index 704b9a2d605a3..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_serial/UART.h +++ /dev/null @@ -1,41 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "common-hal/microcontroller/Pin.h" -#include "common-hal/zephyr_serial/UART.h" -#include "py/ringbuf.h" - -#include - -extern const mp_obj_type_t zephyr_serial_uart_type; - -// Construct an underlying UART object. -extern void zephyr_serial_uart_construct(zephyr_serial_uart_obj_t *self, - const struct device *const uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer); - -extern void zephyr_serial_uart_deinit(zephyr_serial_uart_obj_t *self); -extern bool zephyr_serial_uart_deinited(zephyr_serial_uart_obj_t *self); - -// Read characters. len is in characters NOT bytes! -extern size_t zephyr_serial_uart_read(zephyr_serial_uart_obj_t *self, - uint8_t *data, size_t len, int *errcode); - -// Write characters. len is in characters NOT bytes! -extern size_t zephyr_serial_uart_write(zephyr_serial_uart_obj_t *self, - const uint8_t *data, size_t len, int *errcode); - -extern uint32_t zephyr_serial_uart_get_baudrate(zephyr_serial_uart_obj_t *self); -extern void zephyr_serial_uart_set_baudrate(zephyr_serial_uart_obj_t *self, uint32_t baudrate); -extern mp_float_t zephyr_serial_uart_get_timeout(zephyr_serial_uart_obj_t *self); -extern void zephyr_serial_uart_set_timeout(zephyr_serial_uart_obj_t *self, mp_float_t timeout); - -extern uint32_t zephyr_serial_uart_rx_characters_available(zephyr_serial_uart_obj_t *self); -extern void zephyr_serial_uart_clear_rx_buffer(zephyr_serial_uart_obj_t *self); -extern bool zephyr_serial_uart_ready_to_tx(zephyr_serial_uart_obj_t *self); - -extern void zephyr_serial_uart_never_reset(zephyr_serial_uart_obj_t *self); diff --git a/ports/zephyr-cp/bindings/zephyr_serial/__init__.c b/ports/zephyr-cp/bindings/zephyr_serial/__init__.c deleted file mode 100644 index f4a3c7b92e3be..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_serial/__init__.c +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include - -#include "py/obj.h" -#include "py/runtime.h" - -#include "shared-bindings/microcontroller/Pin.h" -#include "bindings/zephyr_serial/__init__.h" -#include "bindings/zephyr_serial/UART.h" - -#include "py/runtime.h" - -//| """Zephyr UART driver for fixed busses.""" - -static const mp_rom_map_elem_t zephyr_serial_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr_serial) }, - { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&zephyr_serial_uart_type) }, -}; - -static MP_DEFINE_CONST_DICT(zephyr_serial_module_globals, zephyr_serial_module_globals_table); - -const mp_obj_module_t zephyr_serial_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&zephyr_serial_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_zephyr_serial, zephyr_serial_module); diff --git a/ports/zephyr-cp/bindings/zephyr_serial/__init__.h b/ports/zephyr-cp/bindings/zephyr_serial/__init__.h deleted file mode 100644 index 370e233985f74..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_serial/__init__.h +++ /dev/null @@ -1,7 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft -// -// SPDX-License-Identifier: MIT - -#pragma once diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index c59e03c323eb1..5c1e64aedc70b 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = true @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false -zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index d4017cae4dad0..dfb363f2ed87b 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = false @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false -zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index dbffa5944d96c..227e4a2f6c187 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = false @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false -zephyr_i2c = false zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 2d723cbced681..104a21115251a 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = true # Zephyr networking enabled spitarget = false ssl = true # Zephyr networking enabled storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = true @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = true # Zephyr board has wifi -zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 8c522296da6e3..c2d9048604f72 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = false # No TinyUSB settings for r7fa6m5bh3cfc @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false -zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 5c79b0e962efd..4b004746f902d 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = false # No TinyUSB settings for r7fa8d1bhecbd @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false -zephyr_i2c = false zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index 7a5beb79e442c..4b9fbf5c11048 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = false struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = false # No TinyUSB settings for stm32n657xx @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false -zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index a3183f180aed6..01e3314fa5ab4 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = false struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = true @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false -zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index bf6a282874349..9ec442d4cfabf 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -26,31 +26,31 @@ audiomp3 = false audiopwmio = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false hashlib = false -i2cdisplaybus = false +i2cdisplaybus = true # Zephyr board has busio i2ctarget = false imagecapture = false ipaddress = false @@ -59,7 +59,7 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false +lvfontio = true # Zephyr board has busio math = false max3421e = false mdns = false @@ -77,27 +77,27 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +rainbowio = true random = true rclcpy = false rgbmatrix = false rotaryio = false rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = false @@ -106,11 +106,9 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false -zephyr_i2c = true # Zephyr board has zephyr_i2c zephyr_kernel = false -zephyr_serial = true # Zephyr board has zephyr_serial zlib = false diff --git a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c b/ports/zephyr-cp/common-hal/busio/I2C.c similarity index 56% rename from ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c rename to ports/zephyr-cp/common-hal/busio/I2C.c index 12ce95c282d1c..49def727a53ca 100644 --- a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c +++ b/ports/zephyr-cp/common-hal/busio/I2C.c @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT -#include "bindings/zephyr_i2c/I2C.h" +#include "shared-bindings/busio/I2C.h" #include "py/mperrno.h" #include "py/runtime.h" @@ -12,27 +12,40 @@ #include #include -mp_obj_t zephyr_i2c_i2c_zephyr_init(zephyr_i2c_i2c_obj_t *self, const struct device *i2c_device) { - self->base.type = &zephyr_i2c_i2c_type; +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_i2c_construct_from_device(busio_i2c_obj_t *self, const struct device *i2c_device) { + self->base.type = &busio_i2c_type; self->i2c_device = i2c_device; k_mutex_init(&self->mutex); + self->has_lock = false; return MP_OBJ_FROM_PTR(self); } -bool common_hal_zephyr_i2c_i2c_deinited(zephyr_i2c_i2c_obj_t *self) { - // Always leave it active +// Standard busio construct - not used in Zephyr port (devices come from device tree) +void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, + const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, + uint32_t frequency, uint32_t timeout_ms) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("Use device tree to define %q devices"), MP_QSTR_I2C); +} + +bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { + // Always leave it active (managed by Zephyr) return false; } -void common_hal_zephyr_i2c_i2c_deinit(zephyr_i2c_i2c_obj_t *self) { - if (common_hal_zephyr_i2c_i2c_deinited(self)) { +void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { + if (common_hal_busio_i2c_deinited(self)) { return; } - // Always leave it active + // Always leave it active (managed by Zephyr) +} + +void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { + // Not needed for Zephyr port } -bool common_hal_zephyr_i2c_i2c_probe(zephyr_i2c_i2c_obj_t *self, uint8_t addr) { - if (common_hal_zephyr_i2c_i2c_deinited(self)) { +bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { + if (common_hal_busio_i2c_deinited(self)) { return false; } @@ -42,8 +55,8 @@ bool common_hal_zephyr_i2c_i2c_probe(zephyr_i2c_i2c_obj_t *self, uint8_t addr) { return ret == 0; } -bool common_hal_zephyr_i2c_i2c_try_lock(zephyr_i2c_i2c_obj_t *self) { - if (common_hal_zephyr_i2c_i2c_deinited(self)) { +bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { + if (common_hal_busio_i2c_deinited(self)) { return false; } @@ -51,18 +64,19 @@ bool common_hal_zephyr_i2c_i2c_try_lock(zephyr_i2c_i2c_obj_t *self) { return self->has_lock; } -bool common_hal_zephyr_i2c_i2c_has_lock(zephyr_i2c_i2c_obj_t *self) { +bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { return self->has_lock; } -void common_hal_zephyr_i2c_i2c_unlock(zephyr_i2c_i2c_obj_t *self) { +void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { + self->has_lock = false; k_mutex_unlock(&self->mutex); } -uint8_t common_hal_zephyr_i2c_i2c_write(zephyr_i2c_i2c_obj_t *self, uint16_t addr, +uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { - if (common_hal_zephyr_i2c_i2c_deinited(self)) { + if (common_hal_busio_i2c_deinited(self)) { return MP_EIO; } @@ -82,10 +96,10 @@ uint8_t common_hal_zephyr_i2c_i2c_write(zephyr_i2c_i2c_obj_t *self, uint16_t add return 0; } -uint8_t common_hal_zephyr_i2c_i2c_read(zephyr_i2c_i2c_obj_t *self, uint16_t addr, +uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { - if (common_hal_zephyr_i2c_i2c_deinited(self)) { + if (common_hal_busio_i2c_deinited(self)) { return MP_EIO; } @@ -109,10 +123,10 @@ uint8_t common_hal_zephyr_i2c_i2c_read(zephyr_i2c_i2c_obj_t *self, uint16_t addr return 0; } -uint8_t common_hal_zephyr_i2c_i2c_write_read(zephyr_i2c_i2c_obj_t *self, uint16_t addr, +uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - if (common_hal_zephyr_i2c_i2c_deinited(self)) { + if (common_hal_busio_i2c_deinited(self)) { return MP_EIO; } @@ -132,3 +146,7 @@ uint8_t common_hal_zephyr_i2c_i2c_write_read(zephyr_i2c_i2c_obj_t *self, uint16_ return 0; } + +void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { + // Not needed for Zephyr port (devices are managed by Zephyr) +} diff --git a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h b/ports/zephyr-cp/common-hal/busio/I2C.h similarity index 67% rename from ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h rename to ports/zephyr-cp/common-hal/busio/I2C.h index 0265511e20894..4fa877739b781 100644 --- a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h +++ b/ports/zephyr-cp/common-hal/busio/I2C.h @@ -14,6 +14,7 @@ typedef struct { const struct device *i2c_device; struct k_mutex mutex; bool has_lock; -} zephyr_i2c_i2c_obj_t; +} busio_i2c_obj_t; -mp_obj_t zephyr_i2c_i2c_zephyr_init(zephyr_i2c_i2c_obj_t *self, const struct device *i2c_device); +// Helper function to construct from Zephyr device tree device +mp_obj_t common_hal_busio_i2c_construct_from_device(busio_i2c_obj_t *self, const struct device *i2c_device); diff --git a/ports/zephyr-cp/common-hal/busio/SPI.c b/ports/zephyr-cp/common-hal/busio/SPI.c new file mode 100644 index 0000000000000..2864c90b49092 --- /dev/null +++ b/ports/zephyr-cp/common-hal/busio/SPI.c @@ -0,0 +1,281 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/busio/SPI.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "shared/runtime/interrupt_char.h" +#include "supervisor/port.h" + +#include +#include +#include + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_spi_construct_from_device(busio_spi_obj_t *self, const struct device *spi_device) { + self->base.type = &busio_spi_type; + self->spi_device = spi_device; + k_mutex_init(&self->mutex); + self->has_lock = false; + self->active_config = 0; + + k_poll_signal_init(&self->signal); + + // Default configuration for both config slots + self->config[0].frequency = 100000; + self->config[0].operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_LINES_SINGLE; + self->config[1].frequency = 100000; + self->config[1].operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_LINES_SINGLE; + + return MP_OBJ_FROM_PTR(self); +} + +// Standard busio construct - not used in Zephyr port (devices come from device tree) +void common_hal_busio_spi_construct(busio_spi_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *miso, bool half_duplex) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("Use device tree to define %q devices"), MP_QSTR_SPI); +} + +bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { + // Always leave it active + return false; +} + +void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { + if (common_hal_busio_spi_deinited(self)) { + return; + } + // Always leave it active +} + +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + // Not needed for Zephyr port +} + +bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + self->has_lock = k_mutex_lock(&self->mutex, K_NO_WAIT) == 0; + return self->has_lock; +} + +bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { + return self->has_lock; +} + +void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { + self->has_lock = false; + k_mutex_unlock(&self->mutex); +} + +bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + // Set operation mode based on polarity and phase + uint16_t operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(bits) | SPI_LINES_SINGLE; + + if (polarity) { + operation |= SPI_MODE_CPOL; + } + if (phase) { + operation |= SPI_MODE_CPHA; + } + + // Check if settings have changed. We must switch to the other config slot if they have because + // Zephyr drivers are allowed to use the pointer value to know if it has changed. + struct spi_config *current_config = &self->config[self->active_config]; + if (current_config->frequency != baudrate || current_config->operation != operation) { + // Settings changed, switch to the other config slot + self->active_config = 1 - self->active_config; + + // Update the new active configuration + self->config[self->active_config].frequency = baudrate; + self->config[self->active_config].operation = operation; + } + + return true; +} + +bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + if (len == 0) { + return true; + } + + const struct spi_buf tx_buf = { + .buf = (void *)data, + .len = len + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + // Initialize the signal for async operation + k_poll_signal_reset(&self->signal); + + int ret = spi_transceive_signal(self->spi_device, &self->config[self->active_config], &tx, NULL, &self->signal); + if (ret != 0) { + return false; + } + + // Wait for the transfer to complete while running background tasks + int signaled = 0; + int result = 0; + while (!signaled && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + k_poll_signal_check(&self->signal, &signaled, &result); + } + + return signaled && result == 0; +} + +bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + if (len == 0) { + return true; + } + + // For read, we need to write dummy bytes + // We'll allocate a temporary buffer if write_value is not 0 + uint8_t *tx_data = NULL; + bool need_free = false; + bool used_port_malloc = false; + + if (write_value != 0) { + // Use port_malloc if GC isn't active, otherwise use m_malloc + if (gc_alloc_possible()) { + tx_data = m_malloc(len); + } else { + tx_data = port_malloc(len, false); + used_port_malloc = true; + } + if (tx_data == NULL) { + return false; + } + memset(tx_data, write_value, len); + need_free = true; + } + + const struct spi_buf tx_buf = { + .buf = tx_data, + .len = tx_data ? len : 0 + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = tx_data ? 1 : 0 + }; + + const struct spi_buf rx_buf = { + .buf = data, + .len = len + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1 + }; + + // Initialize the signal for async operation + k_poll_signal_reset(&self->signal); + + int ret = spi_transceive_signal(self->spi_device, &self->config[self->active_config], &tx, &rx, &self->signal); + + if (need_free) { + if (used_port_malloc) { + port_free(tx_data); + } else { + m_free(tx_data); + } + } + + if (ret != 0) { + return false; + } + + // Wait for the transfer to complete while running background tasks + int signaled = 0; + int result = 0; + while (!signaled && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + k_poll_signal_check(&self->signal, &signaled, &result); + } + + return signaled && result == 0; +} + +bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + if (len == 0) { + return true; + } + + const struct spi_buf tx_buf = { + .buf = (void *)data_out, + .len = len + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + const struct spi_buf rx_buf = { + .buf = data_in, + .len = len + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1 + }; + + // Initialize the signal for async operation + k_poll_signal_reset(&self->signal); + + int ret = spi_transceive_signal(self->spi_device, &self->config[self->active_config], &tx, &rx, &self->signal); + if (ret != 0) { + return false; + } + + // Wait for the transfer to complete while running background tasks + int signaled = 0; + int result = 0; + while (!signaled && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + k_poll_signal_check(&self->signal, &signaled, &result); + } + + return signaled && result == 0; +} + +uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) { + return self->config[self->active_config].frequency; +} + +uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { + return (self->config[self->active_config].operation & SPI_MODE_CPHA) ? 1 : 0; +} + +uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { + return (self->config[self->active_config].operation & SPI_MODE_CPOL) ? 1 : 0; +} + +void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { + // Not needed for Zephyr port (devices are managed by Zephyr) +} diff --git a/ports/zephyr-cp/common-hal/busio/SPI.h b/ports/zephyr-cp/common-hal/busio/SPI.h new file mode 100644 index 0000000000000..87411c9825ce6 --- /dev/null +++ b/ports/zephyr-cp/common-hal/busio/SPI.h @@ -0,0 +1,24 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include +#include + +typedef struct { + mp_obj_base_t base; + const struct device *spi_device; + struct k_mutex mutex; + bool has_lock; + struct spi_config config[2]; // Two configs for pointer comparison by driver + uint8_t active_config; // Index of currently active config (0 or 1) + struct k_poll_signal signal; +} busio_spi_obj_t; + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_spi_construct_from_device(busio_spi_obj_t *self, const struct device *spi_device); diff --git a/ports/zephyr-cp/common-hal/busio/UART.c b/ports/zephyr-cp/common-hal/busio/UART.c new file mode 100644 index 0000000000000..cecce48d45b24 --- /dev/null +++ b/ports/zephyr-cp/common-hal/busio/UART.c @@ -0,0 +1,149 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/busio/UART.h" + +#include "shared/runtime/interrupt_char.h" +#include "py/mpconfig.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include +#include + +#include +#include +LOG_MODULE_REGISTER(busio_uart); + +/* + * Read characters from UART until line end is detected. Afterwards push the + * data to the message queue. + */ +static void serial_cb(const struct device *dev, void *user_data) { + busio_uart_obj_t *self = (busio_uart_obj_t *)user_data; + + uint8_t c; + + if (!uart_irq_update(dev)) { + return; + } + + if (!uart_irq_rx_ready(dev)) { + return; + } + + /* read until FIFO empty */ + while (uart_fifo_read(dev, &c, 1) == 1) { + if (mp_interrupt_char == c) { + common_hal_busio_uart_clear_rx_buffer(self); + mp_sched_keyboard_interrupt(); + } else if (!self->rx_paused) { + if (k_msgq_put(&self->msgq, &c, K_NO_WAIT) != 0) { + self->rx_paused = true; + } + } + } +} + +void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { + // Not needed for Zephyr port (devices are managed by Zephyr) +} + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_uart_construct_from_device(busio_uart_obj_t *self, const struct device *uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer) { + self->base.type = &busio_uart_type; + self->uart_device = uart_device; + int ret = uart_irq_callback_user_data_set(uart_device, serial_cb, self); + + if (ret < 0) { + LOG_ERR("Failed to set UART IRQ callback: %d", ret); + } + + k_msgq_init(&self->msgq, receiver_buffer, 1, receiver_buffer_size); + + self->timeout = K_USEC(100); + self->rx_paused = false; + uart_irq_rx_enable(uart_device); + + return MP_OBJ_FROM_PTR(self); +} + +// Standard busio construct - not used in Zephyr port (devices come from device tree) +void common_hal_busio_uart_construct(busio_uart_obj_t *self, + const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, + const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts, + const mcu_pin_obj_t *rs485_dir, bool rs485_invert, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, + mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer, + bool sigint_enabled) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("Use device tree to define %q devices"), MP_QSTR_UART); +} + +bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { + return !device_is_ready(self->uart_device); +} + +void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { + // Leave it active (managed by Zephyr) +} + +// Read characters. +size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) { + size_t count = 0; + while (count < len && k_msgq_get(&self->msgq, data + count, self->timeout) == 0) { + count++; + } + if (count > 0) { + self->rx_paused = false; + } + + return count; +} + +// Write characters. +size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + for (int i = 0; i < len; i++) { + uart_poll_out(self->uart_device, data[i]); + } + + return len; +} + +uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { + struct uart_config config; + uart_config_get(self->uart_device, &config); + return config.baudrate; +} + +void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { + struct uart_config config; + uart_config_get(self->uart_device, &config); + config.baudrate = baudrate; + uart_config_set(self->uart_device, &config); +} + +mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) { + return (mp_float_t)self->timeout.ticks / 1000000.0; +} + +void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) { + self->timeout = K_USEC((uint64_t)(timeout * 1000000)); +} + +uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { + return k_msgq_num_used_get(&self->msgq); +} + +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { + k_msgq_purge(&self->msgq); +} + +bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { + return true; +} diff --git a/ports/zephyr-cp/common-hal/zephyr_serial/UART.h b/ports/zephyr-cp/common-hal/busio/UART.h similarity index 54% rename from ports/zephyr-cp/common-hal/zephyr_serial/UART.h rename to ports/zephyr-cp/common-hal/busio/UART.h index eb3cdd746c7d0..27cd76c537b6d 100644 --- a/ports/zephyr-cp/common-hal/zephyr_serial/UART.h +++ b/ports/zephyr-cp/common-hal/busio/UART.h @@ -19,6 +19,10 @@ typedef struct { k_timeout_t timeout; bool rx_paused; // set by irq if no space in rbuf -} zephyr_serial_uart_obj_t; +} busio_uart_obj_t; -mp_obj_t zephyr_serial_uart_zephyr_init(zephyr_serial_uart_obj_t *self, const struct device *uart_device); +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_uart_construct_from_device(busio_uart_obj_t *self, const struct device *uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer); + +// Internal helper for clearing buffer +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self); diff --git a/ports/zephyr-cp/common-hal/zephyr_spi/__init__.c b/ports/zephyr-cp/common-hal/busio/__init__.c similarity index 55% rename from ports/zephyr-cp/common-hal/zephyr_spi/__init__.c rename to ports/zephyr-cp/common-hal/busio/__init__.c index d3a4d4727339f..135a53f490734 100644 --- a/ports/zephyr-cp/common-hal/zephyr_spi/__init__.c +++ b/ports/zephyr-cp/common-hal/busio/__init__.c @@ -1,7 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// SPDX-FileCopyrightText: Copyright (c) 2025 Adafruit Industries LLC // // SPDX-License-Identifier: MIT -// No zephyr_spi module functions. +// No busio module functions. diff --git a/ports/zephyr-cp/common-hal/zephyr_i2c/__init__.c b/ports/zephyr-cp/common-hal/zephyr_i2c/__init__.c deleted file mode 100644 index db93a442f0480..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_i2c/__init__.c +++ /dev/null @@ -1,7 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC -// -// SPDX-License-Identifier: MIT - -// No zephyr_i2c module functions. diff --git a/ports/zephyr-cp/common-hal/zephyr_serial/UART.c b/ports/zephyr-cp/common-hal/zephyr_serial/UART.c deleted file mode 100644 index 55e9c424ffed5..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_serial/UART.c +++ /dev/null @@ -1,136 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "shared-bindings/microcontroller/__init__.h" -#include "bindings/zephyr_serial/UART.h" - -#include "shared/runtime/interrupt_char.h" -#include "py/mpconfig.h" -#include "py/gc.h" -#include "py/mperrno.h" -#include "py/runtime.h" -#include "py/stream.h" - -#include -#include - -#include - -/* - * Read characters from UART until line end is detected. Afterwards push the - * data to the message queue. - */ -static void serial_cb(const struct device *dev, void *user_data) { - zephyr_serial_uart_obj_t *self = (zephyr_serial_uart_obj_t *)user_data; - - uint8_t c; - - if (!uart_irq_update(dev)) { - return; - } - - if (!uart_irq_rx_ready(dev)) { - return; - } - - /* read until FIFO empty */ - while (uart_fifo_read(dev, &c, 1) == 1) { - if (mp_interrupt_char == c) { - zephyr_serial_uart_clear_rx_buffer(self); - mp_sched_keyboard_interrupt(); - } else if (!self->rx_paused) { - if (k_msgq_put(&self->msgq, &c, K_NO_WAIT) != 0) { - self->rx_paused = true; - } - } - } -} - -void zephyr_serial_uart_never_reset(zephyr_serial_uart_obj_t *self) { -} - - -void zephyr_serial_uart_construct(zephyr_serial_uart_obj_t *self, const struct device *const uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer) { - self->uart_device = uart_device; - int ret = uart_irq_callback_user_data_set(uart_device, serial_cb, self); - - - k_msgq_init(&self->msgq, receiver_buffer, 1, receiver_buffer_size); - - if (ret < 0) { - if (ret == -ENOTSUP) { - printk("Interrupt-driven UART API support not enabled\n"); - } else if (ret == -ENOSYS) { - printk("UART device does not support interrupt-driven API\n"); - } else { - printk("Error setting UART callback: %d\n", ret); - } - return; - } - self->timeout = K_USEC(100); - uart_irq_rx_enable(uart_device); -} - -mp_obj_t zephyr_serial_uart_zephyr_init(zephyr_serial_uart_obj_t *self, const struct device *uart_device) { - self->base.type = &zephyr_serial_uart_type; - zephyr_serial_uart_construct(self, uart_device, 128, NULL); - return MP_OBJ_FROM_PTR(self); -} - -bool zephyr_serial_uart_deinited(zephyr_serial_uart_obj_t *self) { - return !device_is_ready(self->uart_device); -} - -void zephyr_serial_uart_deinit(zephyr_serial_uart_obj_t *self) { -} - -// Read characters. -size_t zephyr_serial_uart_read(zephyr_serial_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) { - size_t count = 0; - while (count < len && k_msgq_get(&self->msgq, data + count, self->timeout) == 0) { - count++; - } - if (count > 0) { - self->rx_paused = false; - } - - return count; -} - -// Write characters. -size_t zephyr_serial_uart_write(zephyr_serial_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { - for (int i = 0; i < len; i++) { - uart_poll_out(self->uart_device, data[i]); - } - - return len; -} - -uint32_t zephyr_serial_uart_get_baudrate(zephyr_serial_uart_obj_t *self) { - return 0; -} - -void zephyr_serial_uart_set_baudrate(zephyr_serial_uart_obj_t *self, uint32_t baudrate) { -} - -mp_float_t zephyr_serial_uart_get_timeout(zephyr_serial_uart_obj_t *self) { - return 0; -} - -void zephyr_serial_uart_set_timeout(zephyr_serial_uart_obj_t *self, mp_float_t timeout) { -} - -uint32_t zephyr_serial_uart_rx_characters_available(zephyr_serial_uart_obj_t *self) { - return k_msgq_num_used_get(&self->msgq); -} - -void zephyr_serial_uart_clear_rx_buffer(zephyr_serial_uart_obj_t *self) { - k_msgq_purge(&self->msgq); -} - -bool zephyr_serial_uart_ready_to_tx(zephyr_serial_uart_obj_t *self) { - return true; -} diff --git a/ports/zephyr-cp/common-hal/zephyr_serial/__init__.c b/ports/zephyr-cp/common-hal/zephyr_serial/__init__.c deleted file mode 100644 index 2784446ba15a0..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_serial/__init__.c +++ /dev/null @@ -1,7 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC -// -// SPDX-License-Identifier: MIT - -// No zephyr_serial module functions. diff --git a/ports/zephyr-cp/common-hal/zephyr_spi/SPI.c b/ports/zephyr-cp/common-hal/zephyr_spi/SPI.c deleted file mode 100644 index 5601492fd697b..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_spi/SPI.c +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries -// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec -// -// SPDX-License-Identifier: MIT - -#include - -#include "shared-bindings/busio/SPI.h" -#include "py/mperrno.h" -#include "py/runtime.h" - -void spi_reset(void) { -} - -void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { -} - -void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { - -} - -bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { -} - -void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { - if (common_hal_busio_spi_deinited(self)) { - return; - } -} - -bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { - return true; -} - -bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { - if (common_hal_busio_spi_deinited(self)) { - return false; - } - bool grabbed_lock = false; - return grabbed_lock; -} - -bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { - return self->has_lock; -} - -void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { - self->has_lock = false; -} - -bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) { - return true; -} - -bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) { - return true; -} - -bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) { - return true; -} - -uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) { -} - -uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { - return 0; -} - -uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { - return 0; -} diff --git a/ports/zephyr-cp/common-hal/zephyr_spi/SPI.h b/ports/zephyr-cp/common-hal/zephyr_spi/SPI.h deleted file mode 100644 index 5a1c91c8f0fc7..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_spi/SPI.h +++ /dev/null @@ -1,20 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; - // const spim_peripheral_t *spim_peripheral; - bool has_lock; - uint8_t clock_pin_number; - uint8_t MOSI_pin_number; - uint8_t MISO_pin_number; -} busio_spi_obj_t; - -void spi_reset(void); diff --git a/ports/zephyr-cp/cptools/build_all_boards.py b/ports/zephyr-cp/cptools/build_all_boards.py new file mode 100755 index 0000000000000..8ad6bd05ea9c7 --- /dev/null +++ b/ports/zephyr-cp/cptools/build_all_boards.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +""" +Build all CircuitPython boards for the Zephyr port. + +This script discovers all boards by finding circuitpython.toml files +and builds each one sequentially, passing through the jobserver for +parallelism within each build. +""" + +import argparse +import pathlib +import subprocess +import sys +import time + + +def discover_boards(port_dir): + """ + Discover all boards by finding circuitpython.toml files. + + Returns a list of (vendor, board) tuples. + """ + boards = [] + boards_dir = port_dir / "boards" + + # Find all circuitpython.toml files + for toml_file in boards_dir.glob("*/*/circuitpython.toml"): + # Extract vendor and board from path: boards/vendor/board/circuitpython.toml + parts = toml_file.relative_to(boards_dir).parts + if len(parts) == 3: + vendor = parts[0] + board = parts[1] + boards.append((vendor, board)) + + return sorted(boards) + + +def build_board(port_dir, vendor, board, extra_args=None): + """ + Build a single board using make. + + Args: + port_dir: Path to the zephyr-cp port directory + vendor: Board vendor name + board: Board name + extra_args: Additional arguments to pass to make + + Returns: + (success: bool, elapsed_time: float) + """ + board_id = f"{vendor}_{board}" + start_time = time.time() + + cmd = ["make", f"BOARD={board_id}"] + + # Add extra arguments (like -j) + if extra_args: + cmd.extend(extra_args) + + try: + subprocess.run( + cmd, + cwd=port_dir, + check=True, + # Inherit stdin to pass through jobserver file descriptors + stdin=sys.stdin, + # Show output in real-time + stdout=None, + stderr=None, + capture_output=True, + ) + elapsed = time.time() - start_time + return True, elapsed + except subprocess.CalledProcessError: + elapsed = time.time() - start_time + return False, elapsed + except KeyboardInterrupt: + raise + + +def main(): + parser = argparse.ArgumentParser( + description="Build all CircuitPython boards for the Zephyr port", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + # Build all boards sequentially with 32 parallel jobs per board + %(prog)s -j32 + + # Build all boards using make's jobserver (recommended) + make -j32 all +""", + ) + parser.add_argument( + "-j", + "--jobs", + type=str, + default=None, + help="Number of parallel jobs for each board build (passed to make)", + ) + parser.add_argument( + "--continue-on-error", + action="store_true", + help="Continue building remaining boards even if one fails", + ) + + args = parser.parse_args() + + # Get the port directory + port_dir = pathlib.Path(__file__).parent.resolve().parent + + # Discover all boards + boards = discover_boards(port_dir) + + if not boards: + print("ERROR: No boards found!") + return 1 + + # Prepare extra make arguments + extra_args = [] + if args.jobs: + extra_args.append(f"-j{args.jobs}") + + # Build all boards + start_time = time.time() + results = [] + + try: + for index, (vendor, board) in enumerate(boards): + board_id = f"{vendor}_{board}" + print(f"{index + 1}/{len(boards)} {board_id}: ", end="", flush=True) + + success, elapsed = build_board(port_dir, vendor, board, extra_args) + if success: + print(f"✅ SUCCESS ({elapsed:.1f}s)") + else: + print(f"❌ FAILURE ({elapsed:.1f}s)") + results.append((vendor, board, success, elapsed)) + + if not success and not args.continue_on_error: + print("\nStopping due to build failure.") + break + except KeyboardInterrupt: + print("\n\nBuild interrupted by user.") + return 130 # Standard exit code for SIGINT + + # Print summary + total_time = time.time() - start_time + print(f"\n{'=' * 80}") + print("Build Summary") + print(f"{'=' * 80}") + + successful = [r for r in results if r[2]] + failed = [r for r in results if not r[2]] + + print(f"\nTotal boards: {len(results)}/{len(boards)}") + print(f"Successful: {len(successful)}") + print(f"Failed: {len(failed)}") + print(f"Total time: {total_time:.1f}s") + + return 0 if len(failed) == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index abb1969470a70..87ff00a450321 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -51,8 +51,34 @@ "json", "random", "digitalio", + "rainbowio", + "traceback", + "warnings", + "supervisor", ] -MPCONFIG_FLAGS = ["ulab", "nvm", "displayio", "warnings", "alarm", "array", "json"] +# Flags that don't match with with a *bindings module. +MPCONFIG_FLAGS = ["array", "json"] + +# List of other modules (the value) that can be enabled when another one (the key) is. +REVERSE_DEPENDENCIES = { + "busio": ["fourwire", "i2cdisplaybus", "sdcardio", "sharpdisplay"], + "fourwire": ["displayio", "busdisplay", "epaperdisplay"], + "i2cdisplaybus": ["displayio", "busdisplay", "epaperdisplay"], + "displayio": [ + "vectorio", + "bitmapfilter", + "bitmaptools", + "terminalio", + "lvfontio", + "tilepalettemapper", + "fontio", + ], + "sharpdisplay": ["framebufferio"], + "framebufferio": ["displayio"], +} + +# Other flags to set when a module is enabled +EXTRA_FLAGS = {"busio": ["BUSIO_SPI", "BUSIO_I2C"]} async def preprocess_and_split_defs(compiler, source_file, build_path, flags): @@ -83,10 +109,17 @@ async def preprocess_and_split_defs(compiler, source_file, build_path, flags): async def collect_defs(mode, build_path): output_file = build_path / f"{mode}defs.collected" splitdir = build_path / "genhdr" / mode + to_collect = list(splitdir.glob(f"**/*.{mode}")) + batch_size = 50 await cpbuild.run_command( - ["cat", "-s", *splitdir.glob(f"**/*.{mode}"), ">", output_file], + ["cat", "-s", *to_collect[:batch_size], ">", output_file], splitdir, ) + for i in range(0, len(to_collect), batch_size): + await cpbuild.run_command( + ["cat", "-s", *to_collect[i : i + batch_size], ">>", output_file], + splitdir, + ) return output_file @@ -161,6 +194,79 @@ async def generate_root_pointer_header(build_path): ) +async def generate_display_resources(output_path, translation, font, extra_characters): + await cpbuild.run_command( + [ + "python", + srcdir / "tools" / "gen_display_resources.py", + "--font", + srcdir / font, + "--sample_file", + srcdir / "locale" / f"{translation}.po", + "--extra_characters", + repr(extra_characters), + "--output_c_file", + output_path, + ], + srcdir, + check_hash=[output_path], + ) + + +def determine_enabled_modules(board_info, portdir, srcdir): + """Determine which CircuitPython modules should be enabled based on board capabilities. + + Args: + board_info: Dictionary containing board hardware capabilities + portdir: Path to the port directory (ports/zephyr-cp) + srcdir: Path to the CircuitPython source root + + Returns: + tuple: (enabled_modules set, module_reasons dict) + """ + enabled_modules = set(DEFAULT_MODULES) + module_reasons = {} + + if board_info["wifi"]: + enabled_modules.add("wifi") + module_reasons["wifi"] = "Zephyr board has wifi" + + if board_info["flash_count"] > 0: + enabled_modules.add("storage") + module_reasons["storage"] = "Zephyr board has flash" + + if "wifi" in enabled_modules: + enabled_modules.add("socketpool") + enabled_modules.add("ssl") + module_reasons["socketpool"] = "Zephyr networking enabled" + module_reasons["ssl"] = "Zephyr networking enabled" + + for port_module in (portdir / "bindings").iterdir(): + if not board_info.get(port_module.name, False): + continue + enabled_modules.add(port_module.name) + module_reasons[port_module.name] = f"Zephyr board has {port_module.name}" + + for shared_module in (srcdir / "shared-bindings").iterdir(): + if not board_info.get(shared_module.name, False) or not shared_module.glob("*.c"): + continue + enabled_modules.add(shared_module.name) + module_reasons[shared_module.name] = f"Zephyr board has {shared_module.name}" + + more_modules = [] + more_modules.extend(REVERSE_DEPENDENCIES.get(shared_module.name, [])) + while more_modules: + reverse_dependency = more_modules.pop(0) + if reverse_dependency in enabled_modules: + continue + logger.debug(f"Enabling {reverse_dependency} because {shared_module.name} is enabled") + enabled_modules.add(reverse_dependency) + more_modules.extend(REVERSE_DEPENDENCIES.get(reverse_dependency, [])) + module_reasons[reverse_dependency] = f"Zephyr board has {shared_module.name}" + + return enabled_modules, module_reasons + + TINYUSB_SETTINGS = { "": { "CFG_TUSB_MCU": "OPT_MCU_MIMXRT10XX", @@ -275,27 +381,7 @@ async def build_circuitpython(): autogen_board_info_fn = mpconfigboard_fn.parent / "autogen_board_info.toml" - enabled_modules = set(DEFAULT_MODULES) - module_reasons = {} - if board_info["wifi"]: - enabled_modules.add("wifi") - module_reasons["wifi"] = "Zephyr board has wifi" - - if board_info["flash_count"] > 0: - enabled_modules.add("storage") - module_reasons["storage"] = "Zephyr board has flash" - - if "wifi" in enabled_modules: - enabled_modules.add("socketpool") - enabled_modules.add("ssl") - module_reasons["socketpool"] = "Zephyr networking enabled" - module_reasons["ssl"] = "Zephyr networking enabled" - - for port_module in (portdir / "bindings").iterdir(): - if not board_info.get(port_module.name, False): - continue - enabled_modules.add(port_module.name) - module_reasons[port_module.name] = f"Zephyr board has {port_module.name}" + enabled_modules, module_reasons = determine_enabled_modules(board_info, portdir, srcdir) circuitpython_flags.extend(board_info["cflags"]) supervisor_source = [ @@ -484,7 +570,8 @@ async def build_circuitpython(): list(top.glob("shared-bindings/*")) + list(portdir.glob("bindings/*")), key=lambda x: x.name, ): - if not module.is_dir(): + # Skip files and directories without C source files (like artifacts from a docs build) + if not module.is_dir() or len(list(module.glob("*.c"))) == 0: continue enabled = module.name in enabled_modules # print(f"Module {module.name} enabled: {enabled}") @@ -494,6 +581,11 @@ async def build_circuitpython(): autogen_modules.add(module.name, v) circuitpython_flags.append(f"-DCIRCUITPY_{module.name.upper()}={1 if enabled else 0}") + if enabled: + if module.name in EXTRA_FLAGS: + for flag in EXTRA_FLAGS[module.name]: + circuitpython_flags.append(f"-DCIRCUITPY_{flag}=1") + if enabled: hal_source.extend(portdir.glob(f"bindings/{module.name}/*.c")) hal_source.extend(top.glob(f"ports/zephyr-cp/common-hal/{module.name}/*.c")) @@ -563,6 +655,17 @@ async def build_circuitpython(): tg.create_task(generate_module_header(board_build)) tg.create_task(generate_root_pointer_header(board_build)) + if "terminalio" in enabled_modules: + output_path = board_build / f"autogen_display_resources-{translation}.c" + font_path = srcdir / mpconfigboard.get( + "CIRCUITPY_DISPLAY_FONT", "tools/fonts/ter-u12n.bdf" + ) + extra_characters = mpconfigboard.get("CIRCUITPY_FONT_EXTRA_CHARACTERS", "") + tg.create_task( + generate_display_resources(output_path, translation, font_path, extra_characters) + ) + source_files.append(output_path) + # This file is generated by the QSTR/translation process. source_files.append(builddir / f"translations-{translation}.c") # These files don't include unique QSTRs. They just need to be compiled. diff --git a/ports/zephyr-cp/cptools/tests/README.md b/ports/zephyr-cp/cptools/tests/README.md new file mode 100644 index 0000000000000..6bc8aa43ce694 --- /dev/null +++ b/ports/zephyr-cp/cptools/tests/README.md @@ -0,0 +1,28 @@ +# Zephyr2CP Tests + +This directory contains unit tests for the `zephyr2cp.py` module using real device tree parsing and pytest. + +## Running Tests + +To run all tests: +```bash +cd /home/tannewt/repos/circuitpython/ports/zephyr-cp +make test +``` + +For verbose output: +```bash +pytest test_zephyr2cp.py -v +``` + +To run specific test classes: +```bash +pytest test_zephyr2cp.py::TestFindFlashDevices -v +pytest test_zephyr2cp.py::TestFindRAMRegions -v +pytest test_zephyr2cp.py::TestIntegration -v +``` + +To run a specific test: +```bash +pytest test_zephyr2cp.py::TestFindFlashDevices::test_valid_flash_device -v +``` diff --git a/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py b/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py new file mode 100644 index 0000000000000..95a07930e98c8 --- /dev/null +++ b/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py @@ -0,0 +1,550 @@ +import sys +import pathlib +import tempfile + +# Add devicetree library to path +portdir = pathlib.Path(__file__).parent.parent.parent +sys.path.append(str(portdir / "zephyr/scripts/dts/python-devicetree/src/")) + +from devicetree import dtlib + +# Add parent directory to path to import zephyr2cp +sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) + +# Mock cpbuild before importing +sys.modules["cpbuild"] = type(sys)("cpbuild") +sys.modules["cpbuild"].run_in_thread = lambda x: x + +from zephyr2cp import find_flash_devices, find_ram_regions, BLOCKED_FLASH_COMPAT, MINIMUM_RAM_SIZE + + +def parse_dts_string(dts_content): + """ + Parse a device tree string and return the dtlib.DT object. + + Args: + dts_content: String containing device tree source + + Returns: + dtlib.DT object with parsed device tree + """ + with tempfile.NamedTemporaryFile(mode="w", suffix=".dts", delete=False) as f: + f.write(dts_content) + f.flush() + temp_path = f.name + + try: + dt = dtlib.DT(temp_path) + return dt + finally: + pathlib.Path(temp_path).unlink() + + +class TestFindFlashDevices: + """Test suite for find_flash_devices function.""" + + def test_no_compatible_returns_empty(self): + """Test that device tree with no flash devices returns empty list.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + memory@0 { + reg = <0x0 0x100000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert result == [] + + def test_chosen_flash_excluded(self): + """Test that chosen flash nodes are excluded.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "soc-nv-flash"; + reg = <0x0 0x100000>; + }; + + chosen { + zephyr,flash = &flash0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert result == [], "Chosen flash should be excluded" + + def test_blocked_compat_excluded(self): + """Test that blocked compatible strings are excluded.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + qspi0: qspi@40000 { + compatible = "renesas,ra-qspi"; + reg = <0x40000 0x1000>; + }; + + spi0: spi@50000 { + compatible = "nordic,nrf-spim"; + reg = <0x50000 0x1000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert result == [], "Blocked flash controllers should be excluded" + + def test_valid_flash_device(self): + """Test that valid flash device is detected.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0 0x100000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert len(result) == 1 + assert result[0] == "flash0" + + def test_valid_flash_device_multiple_drivers(self): + """Test that valid flash device is detected.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "other,driver", "jedec,spi-nor"; + reg = <0x0 0x100000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert len(result) == 1 + assert result[0] == "flash0" + + def test_external_flash_not_chosen(self): + """Test external flash is included when internal is chosen.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + internal_flash: flash@0 { + compatible = "soc-nv-flash"; + reg = <0x0 0x100000>; + }; + + external_flash: flash@1000000 { + compatible = "jedec,spi-nor"; + reg = <0x1000000 0x800000>; + }; + + chosen { + zephyr,flash = &internal_flash; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + + # Should only include external flash, not chosen internal flash + assert len(result) == 1 + assert "external_flash" in result[0] + assert "internal_flash" not in result[0] + + def test_disabled_flash_excluded(self): + """Test that disabled flash devices are excluded.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0 0x100000>; + status = "disabled"; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert result == [], "Disabled flash should be excluded" + + +class TestFindRAMRegions: + """Test suite for find_ram_regions function.""" + + def test_no_ram_returns_empty(self): + """Test that device tree with no RAM returns empty list.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash@0 { + compatible = "soc-nv-flash"; + reg = <0x0 0x100000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + assert result == [] + + def test_chosen_sram_basic(self): + """Test chosen sram region is detected correctly.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + + assert len(result) == 1 + label, start, end, size, path = result[0] + + assert label == "sram0" + assert start == "z_mapped_end" + assert ( + end + == "(uint32_t*) (DT_REG_ADDR(DT_NODELABEL(sram0)) + DT_REG_SIZE(DT_NODELABEL(sram0)))" + ) + assert size == 0x40000 + + def test_memory_region_with_custom_name(self): + """Test memory region with zephyr,memory-region property.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + + reserved_mem: memory@30000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x30000000 0x10000>; + zephyr,memory-region = "CUSTOM_REGION"; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + + # Should have both regions, chosen first + assert len(result) == 2 + + # First should be chosen SRAM + assert result[0][0] == "sram0" + + # Second should be custom region + label, start, end, size, path = result[1] + assert label == "reserved_mem" + assert start == "__CUSTOM_REGION_end" + + def test_disabled_ram_excluded(self): + """Test that disabled RAM regions are excluded.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + + sram1: memory@30000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x30000000 0x10000>; + status = "disabled"; + zephyr,memory-region = "CUSTOM_REGION"; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + + # Should only have chosen SRAM, not disabled one + assert len(result) == 1 + assert result[0][0] == "sram0" + + +class TestIntegration: + """Integration tests with realistic device tree configurations.""" + + def test_typical_nrf_board_configuration(self): + """Test typical Nordic nRF board with internal and external flash.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + soc { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "soc-nv-flash"; + reg = <0x0 0x100000>; + }; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + }; + + external_flash: spi_flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0 0x800000>; + }; + + chosen { + zephyr,flash = &flash0; + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + + # Test flash detection + flashes = find_flash_devices(dt) + assert len(flashes) == 1, "Should find external flash only" + assert "external_flash" in flashes[0] + + # Test RAM detection + rams = find_ram_regions(dt) + assert len(rams) == 1, "Should find chosen SRAM only" + assert rams[0][0] == "sram0" + + def test_board_with_nrf5340_regions(self): + """Test that RAM subregions are included with the right addresses.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + chosen { + zephyr,sram = &sram0_image; + }; + /* node '/soc/peripheral@50000000/qspi@2b000' defined in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:475 */ + qspi: qspi@2b000 { + compatible = "nordic,nrf-qspi"; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:476 */ + #address-cells = < 0x1 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:477 */ + #size-cells = < 0x0 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:478 */ + reg = < 0x2b000 0x1000 >, + < 0x10000000 0x10000000 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:479 */ + reg-names = "qspi", + "qspi_mm"; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:480 */ + interrupts = < 0x2b 0x1 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:481 */ + status = "okay"; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:118 */ + + /* node '/soc/peripheral@50000000/qspi@2b000/mx25r6435f@0' defined in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:123 */ + mx25r64: mx25r6435f@0 { + compatible = "nordic,qspi-nor"; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:124 */ + reg = < 0x0 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:125 */ + writeoc = "pp4io"; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:127 */ + readoc = "read4io"; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:129 */ + sck-frequency = < 0x7a1200 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:130 */ + jedec-id = [ C2 28 17 ]; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:131 */ + sfdp-bfp = [ E5 20 F1 FF FF FF FF 03 44 EB 08 6B 08 3B 04 BB EE FF FF FF FF FF 00 FF FF FF 00 + FF 0C 20 0F 52 10 D8 00 FF 23 72 F5 00 82 ED 04 CC 44 83 68 44 30 B0 30 B0 F7 C4 + D5 5C 00 BE 29 FF F0 D0 FF FF ]; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:132 */ + size = < 0x4000000 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:136 */ + has-dpd; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:137 */ + t-enter-dpd = < 0x2710 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:138 */ + t-exit-dpd = < 0x88b8 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:139 */ + }; + }; + + /* node '/soc/memory@20000000' defined in zephyr/dts/arm/nordic/nrf5340_cpuapp.dtsi:55 */ + sram0: memory@20000000 { + compatible = "mmio-sram"; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp.dtsi:56 */ + #address-cells = < 0x1 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp.dtsi:57 */ + #size-cells = < 0x1 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp.dtsi:58 */ + reg = < 0x20000000 0x80000 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_qkaa.dtsi:15 */ + ranges = < 0x0 0x20000000 0x80000 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_qkaa.dtsi:16 */ + + /* node '/soc/memory@20000000/sram@0' defined in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:16 */ + sram0_image: sram@0 { + reg = < 0x0 0x70000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:18 */ + ranges = < 0x0 0x0 0x70000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:19 */ + #address-cells = < 0x1 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:20 */ + #size-cells = < 0x1 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:21 */ + + /* node '/soc/memory@20000000/sram@0/sram0_image@0' defined in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:23 */ + sram0_s: sram0_image@0 { + reg = < 0x0 0x40000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:25 */ + }; + }; + + /* node '/soc/memory@20000000/sram@40000' defined in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:33 */ + sram0_ns: sram@40000 { + reg = < 0x40000 0x40000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:35 */ + ranges = < 0x0 0x40000 0x40000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:36 */ + #address-cells = < 0x1 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:37 */ + #size-cells = < 0x1 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:38 */ + + /* node '/soc/memory@20000000/sram@40000/sram0_ns@0' defined in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:40 */ + sram0_ns_app: sram0_ns@0 { + reg = < 0x0 0x30000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:42 */ + }; + }; + + /* node '/soc/memory@20000000/sram@70000' defined in zephyr/dts/vendor/nordic/nrf5340_shared_sram_partition.dtsi:27 */ + sram0_shared: sram@70000 { + reg = < 0x70000 0x10000 >; /* in zephyr/dts/vendor/nordic/nrf5340_shared_sram_partition.dtsi:29 */ + phandle = < 0x11 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_ipc.dtsi:9 */ + }; + }; +}; +""" + dt = parse_dts_string(dts) + flashes = find_flash_devices(dt) + rams = find_ram_regions(dt) + + # Should only get chosen SRAM + assert len(rams) == 1 + assert rams[0][0] == "sram0_image" + + assert len(flashes) == 1 + assert flashes[0] == "mx25r64" + + def test_board_with_chosen_memory_region(self): + """Test that RAM subregions are included with the right addresses.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + chosen { + zephyr,sram = &axisram2; /* in zephyr/boards/st/nucleo_n657x0_q/nucleo_n657x0_q_common.dtsi:18 */ + }; + + + /* node '/memory@34000000' defined in zephyr/dts/arm/st/n6/stm32n6.dtsi:42 */ + axisram1: memory@34000000 { + compatible = "zephyr,memory-region", + "mmio-sram"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:43 */ + zephyr,memory-region = "AXISRAM1"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:44 */ + reg = < 0x34000000 0x200000 >; /* in zephyr/dts/arm/st/n6/stm32n657X0.dtsi:12 */ + }; + + /* node '/memory@34180400' defined in zephyr/dts/arm/st/n6/stm32n6.dtsi:47 */ + axisram2: memory@34180400 { + compatible = "zephyr,memory-region", + "mmio-sram"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:48 */ + zephyr,memory-region = "AXISRAM2"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:49 */ + reg = < 0x34180400 0x7fc00 >; /* in zephyr/dts/arm/st/n6/stm32n657X0.dtsi:17 */ + }; + + /* node '/soc/ramcfg@42023100' defined in zephyr/dts/arm/st/n6/stm32n6.dtsi:251 */ + ramcfg_sram3_axi: ramcfg@42023100 { + compatible = "st,stm32n6-ramcfg"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:252 */ + #address-cells = < 0x1 >; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:253 */ + #size-cells = < 0x1 >; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:254 */ + reg = < 0x42023100 0x80 >; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:255 */ + + /* node '/soc/ramcfg@42023100/memory@34200000' defined in zephyr/dts/arm/st/n6/stm32n6.dtsi:259 */ + axisram3: memory@34200000 { + compatible = "zephyr,memory-region", + "mmio-sram"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:260 */ + zephyr,memory-region = "AXISRAM3"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:261 */ + zephyr,memory-attr = < 0x100000 >; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:262 */ + reg = < 0x34200000 0x70000 >; /* in zephyr/dts/arm/st/n6/stm32n657X0.dtsi:23 */ + status = "disabled"; /* in zephyr/dts/arm/st/n6/stm32n657X0.dtsi:24 */ + }; + }; +}; +""" + dt = parse_dts_string(dts) + rams = find_ram_regions(dt) + + print(rams) + + # Should only get chosen SRAM + assert len(rams) == 2 + assert rams[0][0] == "axisram2" + assert rams[1][0] == "axisram1" diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 9d238a03bd810..8cfbc0118a7f9 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -19,6 +19,7 @@ "nordic_nrf_uarte": "serial", "nordic_nrf_uart": "serial", "nordic_nrf_twim": "i2c", + "nordic_nrf_spim": "spi", } # These are controllers, not the flash devices themselves. @@ -28,7 +29,7 @@ "nordic,nrf-spim", ) -DRIVER_CLASSES = {"serial": "UART", "i2c": "I2C", "spi": "SPI"} +BUSIO_CLASSES = {"serial": "UART", "i2c": "I2C", "spi": "SPI"} CONNECTORS = { "mikro-bus": [ @@ -194,6 +195,151 @@ EXCEPTIONAL_DRIVERS = ["entropy", "gpio", "led"] +def find_flash_devices(device_tree): + """ + Find all flash devices from a device tree. + + Args: + device_tree: Parsed device tree (dtlib.DT object) + + Returns: + List of device tree flash device reference strings + """ + # Build path2chosen mapping + path2chosen = {} + for k in device_tree.root.nodes["chosen"].props: + value = device_tree.root.nodes["chosen"].props[k] + path2chosen[value.to_path()] = k + + flashes = [] + logger.debug("Flash devices:") + + # Traverse all nodes in the device tree + remaining_nodes = set([device_tree.root]) + while remaining_nodes: + node = remaining_nodes.pop() + remaining_nodes.update(node.nodes.values()) + + # Get compatible strings + compatible = [] + if "compatible" in node.props: + compatible = node.props["compatible"].to_strings() + + # Get status + status = node.props.get("status", None) + if status is None: + status = "okay" + else: + status = status.to_string() + + # Check if this is a flash device + if not compatible or status != "okay": + continue + + # Check for flash driver via compat2driver + drivers = [] + for c in compatible: + underscored = c.replace(",", "_").replace("-", "_") + driver = COMPAT_TO_DRIVER.get(underscored, None) + if not driver: + driver = MANUAL_COMPAT_TO_DRIVER.get(underscored, None) + if driver: + drivers.append(driver) + logger.debug(f" {node.labels[0] if node.labels else node.name} drivers: {drivers}") + + if "flash" not in drivers: + continue + + # Skip chosen nodes because they are used by Zephyr + if node in path2chosen: + logger.debug( + f" skipping flash {node.labels[0] if node.labels else node.name} (chosen)" + ) + continue + + # Skip blocked flash compatibles (controllers, not actual flash devices) + if compatible[0] in BLOCKED_FLASH_COMPAT: + logger.debug( + f" skipping flash {node.labels[0] if node.labels else node.name} (blocked compat)" + ) + continue + + if node.labels: + flashes.append(node.labels[0]) + + logger.debug("Flash devices:") + for flash in flashes: + logger.debug(f" {flash}") + + return flashes + + +def _label_to_end(label): + return f"(uint32_t*) (DT_REG_ADDR(DT_NODELABEL({label})) + DT_REG_SIZE(DT_NODELABEL({label})))" + + +def find_ram_regions(device_tree): + """ + Find all RAM regions from a device tree. Includes the zephyr,sram node and + any zephyr,memory-region nodes. + + Returns: + List of RAM region info tuples: (label, start, end, size, path) + """ + rams = [] + chosen = None + # Get the chosen SRAM node directly + if "zephyr,sram" in device_tree.root.nodes["chosen"].props: + chosen = device_tree.root.nodes["chosen"].props["zephyr,sram"].to_path() + label = chosen.labels[0] + size = chosen.props["reg"].to_nums()[1] + logger.debug(f"Found chosen SRAM node: {label} with size {size}") + rams.append((label, "z_mapped_end", _label_to_end(label), size, chosen.path)) + + # Traverse all nodes in the device tree to find memory-region nodes + remaining_nodes = set([device_tree.root]) + while remaining_nodes: + node = remaining_nodes.pop() + + # Check status first so we don't add child nodes that aren't active. + status = node.props.get("status", None) + if status is None: + status = "okay" + else: + status = status.to_string() + + if status != "okay": + continue + + if node == chosen: + continue + + remaining_nodes.update(node.nodes.values()) + + if "compatible" not in node.props or not node.labels: + continue + + compatible = node.props["compatible"].to_strings() + + if "zephyr,memory-region" not in compatible or "zephyr,memory-region" not in node.props: + continue + + size = node.props["reg"].to_nums()[1] + + start = "__" + node.props["zephyr,memory-region"].to_string() + "_end" + end = _label_to_end(node.labels[0]) + + # Filter by minimum size + if size >= MINIMUM_RAM_SIZE: + logger.debug( + f"Adding extra RAM info: ({node.labels[0]}, {start}, {end}, {size}, {node.path})" + ) + info = (node.labels[0], start, end, size, node.path) + rams.append(info) + + return rams + + @cpbuild.run_in_thread def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 board_dir = builddir / "board" @@ -228,31 +374,33 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 # board_name = board_id_yaml["name"] dts = zephyrbuilddir / "zephyr.dts" - edt_pickle = dtlib.DT(dts) + device_tree = dtlib.DT(dts) node2alias = {} - for alias in edt_pickle.alias2node: - node = edt_pickle.alias2node[alias] + for alias in device_tree.alias2node: + node = device_tree.alias2node[alias] if node not in node2alias: node2alias[node] = [] node2alias[node].append(alias) ioports = {} all_ioports = [] board_names = {} - flashes = [] - rams = [] status_led = None status_led_inverted = False path2chosen = {} chosen2path = {} + # Find flash and RAM regions using extracted functions + flashes = find_flash_devices(device_tree) + rams = find_ram_regions(device_tree) # Returns filtered and sorted list + # Store active Zephyr device labels per-driver so that we can make them available via board. active_zephyr_devices = {} usb_num_endpoint_pairs = 0 - for k in edt_pickle.root.nodes["chosen"].props: - value = edt_pickle.root.nodes["chosen"].props[k] + for k in device_tree.root.nodes["chosen"].props: + value = device_tree.root.nodes["chosen"].props[k] path2chosen[value.to_path()] = k chosen2path[k] = value.to_path() - remaining_nodes = set([edt_pickle.root]) + remaining_nodes = set([device_tree.root]) while remaining_nodes: node = remaining_nodes.pop() remaining_nodes.update(node.nodes.values()) @@ -273,50 +421,16 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 if node in path2chosen: chosen = path2chosen[node] logger.debug(f" chosen: {chosen}") - if not compatible and chosen == "zephyr,sram": - # The designated sram region may not have any compatible properties, - # so we assume it is compatible with mmio - compatible.append("mmio") for c in compatible: underscored = c.replace(",", "_").replace("-", "_") driver = COMPAT_TO_DRIVER.get(underscored, None) - if "mmio" in c: - address, size = node.props["reg"].to_nums() - end = address + size - if chosen == "zephyr,sram": - start = "z_mapped_end" - elif "zephyr,memory-region" in node.props: - start = "__" + node.props["zephyr,memory-region"].to_string() + "_end" - else: - # Check to see if the chosen sram is a subset of this region. If it is, - # then do as above for a smaller region and assume the rest is reserved. - chosen_sram = chosen2path["zephyr,sram"] - chosen_address, chosen_size = chosen_sram.props["reg"].to_nums() - chosen_end = chosen_address + chosen_size - if address <= chosen_address <= end and address <= chosen_end <= end: - start = "z_mapped_end" - address = chosen_address - size = chosen_size - end = chosen_end - else: - start = address - info = (node.labels[0], start, end, size, node.path) - if chosen == "zephyr,sram": - rams.insert(0, info) - elif status == "okay" and size > MINIMUM_RAM_SIZE: - logger.debug(f"Adding RAM info: {info}") - rams.append(info) if not driver: driver = MANUAL_COMPAT_TO_DRIVER.get(underscored, None) logger.debug(f" {c} -> {underscored} -> {driver}") if not driver or status != "okay": continue if driver == "flash": - if not chosen and compatible[0] not in BLOCKED_FLASH_COMPAT: - # Skip chosen nodes because they are used by Zephyr. - flashes.append(f"DEVICE_DT_GET(DT_NODELABEL({node.labels[0]}))") - else: - logger.debug(" skipping due to blocked compat") + pass # Handled by find_flash_devices() elif driver == "usb/udc": board_info["usb_device"] = True props = node.props @@ -335,9 +449,10 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 board_info["wifi"] = True elif driver in EXCEPTIONAL_DRIVERS: pass - elif (portdir / f"bindings/zephyr_{driver}").exists(): - board_info[f"zephyr_{driver}"] = True - logger.info(f"Supported driver: {driver}") + elif driver in BUSIO_CLASSES: + # busio driver (i2c, spi, uart) + board_info["busio"] = True + logger.info(f"Supported busio driver: {driver}") if driver not in active_zephyr_devices: active_zephyr_devices[driver] = [] active_zephyr_devices[driver].append(node.labels) @@ -439,8 +554,8 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 zephyr_binding_objects = [] zephyr_binding_labels = [] for driver, instances in active_zephyr_devices.items(): - driverclass = DRIVER_CLASSES[driver] - zephyr_binding_headers.append(f'#include "bindings/zephyr_{driver}/{driverclass}.h"') + driverclass = BUSIO_CLASSES[driver] + zephyr_binding_headers.append(f'#include "shared-bindings/busio/{driverclass}.h"') # Designate a main bus such as board.I2C. if len(instances) == 1: @@ -453,7 +568,7 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 if label == driverclass: found_main = True if not found_main: - for priority_label in ("zephyr_i2c", "arduino_i2c"): + for priority_label in (f"zephyr_{driver}", f"arduino_{driver}"): for labels in instances: if priority_label in labels: labels.append(driverclass) @@ -466,18 +581,28 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 c_function_name = f"_{instance_name}" singleton_ptr = f"{c_function_name}_singleton" function_object = f"{c_function_name}_obj" - binding_prefix = f"zephyr_{driver}_{driverclass.lower()}" + busio_type = f"busio_{driverclass.lower()}" + + # UART needs a receiver buffer + if driver == "serial": + buffer_decl = f"static byte {instance_name}_buffer[128];" + construct_call = f"common_hal_busio_uart_construct_from_device(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]})), 128, {instance_name}_buffer)" + else: + buffer_decl = "" + construct_call = f"common_hal_busio_{driverclass.lower()}_construct_from_device(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]})))" + zephyr_binding_objects.append( - f"""static {binding_prefix}_obj_t {instance_name}_obj; + f"""{buffer_decl} +static {busio_type}_obj_t {instance_name}_obj; static mp_obj_t {singleton_ptr} = mp_const_none; static mp_obj_t {c_function_name}(void) {{ if ({singleton_ptr} != mp_const_none) {{ return {singleton_ptr}; }} - {singleton_ptr} = {binding_prefix}_zephyr_init(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]}))); + {singleton_ptr} = {construct_call}; return {singleton_ptr}; }} -static MP_DEFINE_CONST_FUN_OBJ_0({function_object}, {c_function_name});""" +static MP_DEFINE_CONST_FUN_OBJ_0({function_object}, {c_function_name});""".lstrip() ) for label in labels: zephyr_binding_labels.append( @@ -503,15 +628,15 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 for ram in rams: device, start, end, size, path = ram max_size = max(max_size, size) - if isinstance(start, str): - ram_externs.append(f"extern uint32_t {start};") - start = "&" + start - else: - start = f"(uint32_t*) 0x{start:08x}" - ram_list.append(f" {start}, (uint32_t*) 0x{end:08x}, // {path}") + # We always start at the end of a Zephyr linker section so we need the externs and &. + ram_externs.append(f"extern uint32_t {start};") + start = "&" + start + ram_list.append(f" {start}, {end}, // {path}") ram_list = "\n".join(ram_list) ram_externs = "\n".join(ram_externs) + flashes = [f"DEVICE_DT_GET(DT_NODELABEL({flash}))" for flash in flashes] + new_header_content = f"""#pragma once #define MICROPY_HW_BOARD_NAME "{board_name}" diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 8651b2715cc1a..3d2f1fe69aaba 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -28,3 +28,5 @@ CONFIG_LOG_BLOCK_IN_THREAD=y CONFIG_EVENTS=y CONFIG_I2C=y +CONFIG_SPI=y +CONFIG_SPI_ASYNC=y diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 860584c2b19d1..c58de56d707ae 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -26,7 +26,14 @@ static pool_t pools[CIRCUITPY_RAM_DEVICE_COUNT]; static K_EVENT_DEFINE(main_needed); +static struct k_timer tick_timer; + +static void _tick_function(struct k_timer *timer_id) { + supervisor_tick(); +} + safe_mode_t port_init(void) { + k_timer_init(&tick_timer, _tick_function, NULL); return SAFE_MODE_NONE; } @@ -94,12 +101,12 @@ uint64_t port_get_raw_ticks(uint8_t *subticks) { // Enable 1/1024 second tick. void port_enable_tick(void) { - + k_timer_start(&tick_timer, K_USEC(1000000 / 1024), K_USEC(1000000 / 1024)); } // Disable 1/1024 second tick. void port_disable_tick(void) { - + k_timer_stop(&tick_timer); } static k_timeout_t next_timeout; diff --git a/ports/zephyr-cp/supervisor/serial.c b/ports/zephyr-cp/supervisor/serial.c index 97e2bee9ce576..a62e00a774274 100644 --- a/ports/zephyr-cp/supervisor/serial.c +++ b/ports/zephyr-cp/supervisor/serial.c @@ -6,15 +6,17 @@ #include "supervisor/shared/serial.h" -#include "bindings/zephyr_serial/UART.h" +#if CIRCUITPY_USB_DEVICE == 0 +#include "shared-bindings/busio/UART.h" -static zephyr_serial_uart_obj_t zephyr_console; +static busio_uart_obj_t zephyr_console; static uint8_t buffer[64]; +#endif void port_serial_early_init(void) { #if CIRCUITPY_USB_DEVICE == 0 - zephyr_console.base.type = &zephyr_serial_uart_type; - zephyr_serial_uart_construct(&zephyr_console, DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), sizeof(buffer), buffer); + zephyr_console.base.type = &busio_uart_type; + common_hal_busio_uart_construct_from_device(&zephyr_console, DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), sizeof(buffer), buffer); #endif } @@ -32,7 +34,7 @@ bool port_serial_connected(void) { char port_serial_read(void) { #if CIRCUITPY_USB_DEVICE == 0 char buf[1]; - size_t count = zephyr_serial_uart_read(&zephyr_console, buf, 1, NULL); + size_t count = common_hal_busio_uart_read(&zephyr_console, buf, 1, NULL); if (count == 0) { return -1; } @@ -44,7 +46,7 @@ char port_serial_read(void) { uint32_t port_serial_bytes_available(void) { #if CIRCUITPY_USB_DEVICE == 0 - return zephyr_serial_uart_rx_characters_available(&zephyr_console); + return common_hal_busio_uart_rx_characters_available(&zephyr_console); #else return 0; #endif @@ -52,6 +54,6 @@ uint32_t port_serial_bytes_available(void) { void port_serial_write_substring(const char *text, uint32_t length) { #if CIRCUITPY_USB_DEVICE == 0 - zephyr_serial_uart_write(&zephyr_console, text, length, NULL); + common_hal_busio_uart_write(&zephyr_console, text, length, NULL); #endif } diff --git a/requirements-dev.txt b/requirements-dev.txt index 0e8426d5174d4..cdfb62da1fa76 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -41,3 +41,4 @@ setuptools # For zephyr port tomlkit +pytest diff --git a/shared-bindings/board/__init__.h b/shared-bindings/board/__init__.h index 7c9a59d2dbd0d..fbc39317a30ce 100644 --- a/shared-bindings/board/__init__.h +++ b/shared-bindings/board/__init__.h @@ -14,19 +14,16 @@ extern const mp_obj_dict_t board_module_globals; static const MP_DEFINE_STR_OBJ(board_module_id_obj, CIRCUITPY_BOARD_ID); -bool common_hal_board_is_i2c(mp_obj_t obj); mp_obj_t common_hal_board_get_i2c(const mp_int_t instance); mp_obj_t common_hal_board_create_i2c(const mp_int_t instance); mp_obj_t board_i2c(size_t n_args, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ_0(board_i2c_obj); -bool common_hal_board_is_spi(mp_obj_t obj); mp_obj_t common_hal_board_get_spi(const mp_int_t instance); mp_obj_t common_hal_board_create_spi(const mp_int_t instance); mp_obj_t board_spi(size_t n_args, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ_0(board_spi_obj); -bool common_hal_board_is_uart(mp_obj_t obj); mp_obj_t common_hal_board_get_uart(const mp_int_t instance); mp_obj_t common_hal_board_create_uart(const mp_int_t instance); mp_obj_t board_uart(size_t n_args, const mp_obj_t *args); diff --git a/shared-bindings/busio/I2C.c b/shared-bindings/busio/I2C.c index 8aa35ec6e0709..165b6a5ba936b 100644 --- a/shared-bindings/busio/I2C.c +++ b/shared-bindings/busio/I2C.c @@ -106,7 +106,7 @@ static void check_for_deinit(busio_i2c_obj_t *self) { // Provided by context manager helper. static void check_lock(busio_i2c_obj_t *self) { - asm (""); + __asm__ (""); if (!common_hal_busio_i2c_has_lock(self)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Function requires lock")); } diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 1513a7cf0944a..24c02a16e634b 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -140,7 +140,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_deinit_obj, busio_spi_obj_deinit); // Provided by context manager helper. static void check_lock(busio_spi_obj_t *self) { - asm (""); + __asm__ (""); if (!common_hal_busio_spi_has_lock(self)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Function requires lock")); } diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 96735c8dbaa40..a9c0e9a6c80cf 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -43,15 +43,6 @@ static const board_i2c_pin_t i2c_pin[CIRCUITPY_BOARD_I2C] = CIRCUITPY_BOARD_I2C_ static busio_i2c_obj_t i2c_obj[CIRCUITPY_BOARD_I2C]; static bool i2c_obj_created[CIRCUITPY_BOARD_I2C]; -bool common_hal_board_is_i2c(mp_obj_t obj) { - for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_I2C; instance++) { - if (obj == &i2c_obj[instance]) { - return true; - } - } - return false; -} - mp_obj_t common_hal_board_get_i2c(const mp_int_t instance) { return i2c_obj_created[instance] ? &i2c_obj[instance] : NULL; } @@ -89,15 +80,6 @@ static const board_spi_pin_t spi_pin[CIRCUITPY_BOARD_SPI] = CIRCUITPY_BOARD_SPI_ static busio_spi_obj_t spi_obj[CIRCUITPY_BOARD_SPI]; static bool spi_obj_created[CIRCUITPY_BOARD_SPI]; -bool common_hal_board_is_spi(mp_obj_t obj) { - for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_SPI; instance++) { - if (obj == &spi_obj[instance]) { - return true; - } - } - return false; -} - mp_obj_t common_hal_board_get_spi(const mp_int_t instance) { return spi_obj_created[instance] ? &spi_obj[instance] : NULL; } @@ -136,15 +118,6 @@ static const board_uart_pin_t uart_pin[CIRCUITPY_BOARD_UART] = CIRCUITPY_BOARD_U static busio_uart_obj_t uart_obj[CIRCUITPY_BOARD_UART]; static bool uart_obj_created[CIRCUITPY_BOARD_UART]; -bool common_hal_board_is_uart(mp_obj_t obj) { - for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_UART; instance++) { - if (obj == &uart_obj[instance]) { - return true; - } - } - return false; -} - mp_obj_t common_hal_board_get_uart(const mp_int_t instance) { return uart_obj_created[instance] ? &uart_obj[instance] : NULL; } diff --git a/shared-module/displayio/Shape.c b/shared-module/displayio/Shape.c deleted file mode 100644 index 143d37c6c9b35..0000000000000 --- a/shared-module/displayio/Shape.c +++ /dev/null @@ -1,126 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "shared-bindings/displayio/Shape.h" - -#include - -#include "py/runtime.h" -#include "py/misc.h" - -void common_hal_displayio_shape_construct(displayio_shape_t *self, uint32_t width, - uint32_t height, bool mirror_x, bool mirror_y) { - self->mirror_x = mirror_x; - self->mirror_y = mirror_y; - self->width = width; - if (self->mirror_x) { - width /= 2; - width += self->width % 2; - } - self->half_width = width; - - self->height = height; - if (self->mirror_y) { - height /= 2; - height += self->height % 2; - } - self->half_height = height; - - self->data = m_malloc_without_collect(height * sizeof(uint32_t)); - - for (uint16_t i = 0; i < height; i++) { - self->data[2 * i] = 0; - self->data[2 * i + 1] = width; - } - - self->dirty_area.x1 = 0; - self->dirty_area.x2 = width; - self->dirty_area.y1 = 0; - self->dirty_area.y2 = height; -} - -void common_hal_displayio_shape_set_boundary(displayio_shape_t *self, uint16_t y, uint16_t start_x, uint16_t end_x) { - uint16_t max_y = self->height - 1; - if (self->mirror_y) { - max_y = self->half_height - 1; - } - mp_arg_validate_int_range(y, 0, max_y, MP_QSTR_y); - uint16_t max_x = self->width - 1; - if (self->mirror_x) { - max_x = self->half_width - 1; - } - mp_arg_validate_int_range(start_x, 0, max_x, MP_QSTR_start_x); - mp_arg_validate_int_range(end_x, 0, max_x, MP_QSTR_end_x); - - uint16_t lower_x, upper_x, lower_y, upper_y; - - // find x-boundaries for updating based on current data and start_x, end_x, and mirror_x - lower_x = MIN(start_x, self->data[2 * y]); - - if (self->mirror_x) { - upper_x = self->width - lower_x + 1; // dirty rectangles are treated with max value exclusive - } else { - upper_x = MAX(end_x, self->data[2 * y + 1]) + 1; // dirty rectangles are treated with max value exclusive - } - - // find y-boundaries based on y and mirror_y - lower_y = y; - - if (self->mirror_y) { - upper_y = self->height - lower_y + 1; // dirty rectangles are treated with max value exclusive - } else { - upper_y = y + 1; // dirty rectangles are treated with max value exclusive - } - - self->data[2 * y] = start_x; // update the data array with the new boundaries - self->data[2 * y + 1] = end_x; - - if (self->dirty_area.x1 == self->dirty_area.x2) { // Dirty region is empty - self->dirty_area.x1 = lower_x; - self->dirty_area.x2 = upper_x; - self->dirty_area.y1 = lower_y; - self->dirty_area.y2 = upper_y; - - } else { // Dirty region is not empty - self->dirty_area.x1 = MIN(lower_x, self->dirty_area.x1); - self->dirty_area.x2 = MAX(upper_x, self->dirty_area.x2); - - self->dirty_area.y1 = MIN(lower_y, self->dirty_area.y1); - self->dirty_area.y2 = MAX(upper_y, self->dirty_area.y2); - } -} - -uint32_t common_hal_displayio_shape_get_pixel(void *obj, int16_t x, int16_t y) { - displayio_shape_t *self = obj; - if (x >= self->width || x < 0 || y >= self->height || y < 0) { - return 0; - } - if (self->mirror_x && x >= self->half_width) { - x = self->width - x - 1; - } - if (self->mirror_y && y >= self->half_height) { - y = self->height - y - 1; - } - uint16_t start_x = self->data[2 * y]; - uint16_t end_x = self->data[2 * y + 1]; - if (x < start_x || x > end_x) { - return 0; - } - return 1; -} - -displayio_area_t *displayio_shape_get_refresh_areas(displayio_shape_t *self, displayio_area_t *tail) { - if (self->dirty_area.x1 == self->dirty_area.x2) { - return tail; - } - self->dirty_area.next = tail; - return &self->dirty_area; -} - -void displayio_shape_finish_refresh(displayio_shape_t *self) { - self->dirty_area.x1 = 0; - self->dirty_area.x2 = 0; -} diff --git a/shared-module/displayio/Shape.h b/shared-module/displayio/Shape.h deleted file mode 100644 index 20ee182023fe9..0000000000000 --- a/shared-module/displayio/Shape.h +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -#include "py/obj.h" -#include "shared-module/displayio/area.h" - -typedef struct { - mp_obj_base_t base; - uint16_t width; - uint16_t height; - uint16_t half_width; - uint16_t half_height; - uint16_t *data; - bool mirror_x; - bool mirror_y; - displayio_area_t dirty_area; -} displayio_shape_t; - -void displayio_shape_finish_refresh(displayio_shape_t *self); -displayio_area_t *displayio_shape_get_refresh_areas(displayio_shape_t *self, displayio_area_t *tail); diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 747fdbec5a75a..e325a7857f464 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -10,6 +10,7 @@ #include "shared-bindings/displayio/__init__.h" #include "shared/runtime/interrupt_char.h" +#include "py/gc.h" #include "py/runtime.h" #include "shared-bindings/board/__init__.h" #include "shared-bindings/busio/I2C.h" @@ -215,19 +216,9 @@ void reset_displays(void) { if (((size_t)fourwire->bus) < ((size_t)&display_buses) || ((size_t)fourwire->bus) > ((size_t)&display_buses + CIRCUITPY_DISPLAY_LIMIT * sizeof(primary_display_bus_t))) { busio_spi_obj_t *original_spi = fourwire->bus; - #if CIRCUITPY_BOARD_SPI - // We don't need to move original_spi if it is a board.SPI object because it is - // statically allocated already. (Doing so would also make it impossible to reference in - // a subsequent VM run.) - if (common_hal_board_is_spi(original_spi)) { + if (!gc_ptr_on_heap(original_spi)) { continue; } - #endif - #ifdef BOARD_USE_INTERNAL_SPI - if (original_spi == (mp_obj_t)(&supervisor_flash_spi_bus)) { - continue; - } - #endif memcpy(&fourwire->inline_bus, original_spi, sizeof(busio_spi_obj_t)); fourwire->bus = &fourwire->inline_bus; @@ -249,14 +240,9 @@ void reset_displays(void) { if (((size_t)i2c->bus) < ((size_t)&display_buses) || ((size_t)i2c->bus) > ((size_t)&display_buses + CIRCUITPY_DISPLAY_LIMIT * sizeof(primary_display_bus_t))) { busio_i2c_obj_t *original_i2c = i2c->bus; - #if CIRCUITPY_BOARD_I2C - // We don't need to move original_i2c if it is a board.I2C object because it is - // statically allocated already. (Doing so would also make it impossible to reference in - // a subsequent VM run.) - if (common_hal_board_is_i2c(original_i2c)) { + if (!gc_ptr_on_heap(original_i2c)) { continue; } - #endif memcpy(&i2c->inline_bus, original_i2c, sizeof(busio_i2c_obj_t)); i2c->bus = &i2c->inline_bus; // Check for other displays that use the same i2c bus and swap them too. @@ -285,22 +271,17 @@ void reset_displays(void) { if (((uint32_t)is31fb->is31fl3741->i2c) < ((uint32_t)&display_buses) || ((uint32_t)is31fb->is31fl3741->i2c) > ((uint32_t)&display_buses + CIRCUITPY_DISPLAY_LIMIT)) { - #if CIRCUITPY_BOARD_I2C - // We don't need to move original_i2c if it is the board.I2C object because it is - // statically allocated already. (Doing so would also make it impossible to reference in - // a subsequent VM run.) - if (common_hal_board_is_i2c(is31fb->is31fl3741->i2c)) { - continue; - } - #endif - is31fl3741_IS31FL3741_obj_t *original_is31 = is31fb->is31fl3741; - memcpy(&is31fb->inline_is31fl3741, original_is31, sizeof(is31fl3741_IS31FL3741_obj_t)); - is31fb->is31fl3741 = &is31fb->inline_is31fl3741; + if (gc_ptr_on_heap(original_is31)) { + memcpy(&is31fb->inline_is31fl3741, original_is31, sizeof(is31fl3741_IS31FL3741_obj_t)); + is31fb->is31fl3741 = &is31fb->inline_is31fl3741; + } busio_i2c_obj_t *original_i2c = is31fb->is31fl3741->i2c; - memcpy(&is31fb->is31fl3741->inline_i2c, original_i2c, sizeof(busio_i2c_obj_t)); - is31fb->is31fl3741->i2c = &is31fb->is31fl3741->inline_i2c; + if (gc_ptr_on_heap(original_i2c)) { + memcpy(&is31fb->is31fl3741->inline_i2c, original_i2c, sizeof(busio_i2c_obj_t)); + is31fb->is31fl3741->i2c = &is31fb->is31fl3741->inline_i2c; + } } if (!any_display_uses_this_framebuffer(&is31fb->base)) { @@ -332,12 +313,10 @@ void reset_displays(void) { #endif #if CIRCUITPY_AURORA_EPAPER } else if (display_bus_type == &aurora_framebuffer_type) { - #if CIRCUITPY_BOARD_SPI aurora_epaper_framebuffer_obj_t *aurora = &display_buses[i].aurora_epaper; - if (common_hal_board_is_spi(aurora->bus)) { + if (gc_ptr_on_heap(aurora->bus)) { common_hal_aurora_epaper_framebuffer_set_free_bus(false); } - #endif // Set to None, gets deinit'd up by display_base display_buses[i].bus_base.type = &mp_type_NoneType; #endif diff --git a/shared-module/sharpdisplay/SharpMemoryFramebuffer.c b/shared-module/sharpdisplay/SharpMemoryFramebuffer.c index 3c6eaf065df72..8b9629b002f45 100644 --- a/shared-module/sharpdisplay/SharpMemoryFramebuffer.c +++ b/shared-module/sharpdisplay/SharpMemoryFramebuffer.c @@ -54,11 +54,7 @@ static bool common_hal_sharpdisplay_framebuffer_get_pixels_in_byte_share_row(sha } void common_hal_sharpdisplay_framebuffer_reset(sharpdisplay_framebuffer_obj_t *self) { - if (self->bus != &self->inline_bus - #if CIRCUITPY_BOARD_SPI - && !common_hal_board_is_spi(self->bus) - #endif - ) { + if (self->bus != &self->inline_bus && gc_ptr_on_heap(self->bus)) { memcpy(&self->inline_bus, self->bus, sizeof(busio_spi_obj_t)); self->bus = &self->inline_bus; } diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index 8d53b0e62be7e..99c1497f02300 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -125,7 +125,7 @@ static uint8_t *tilegrid_tiles = NULL; static size_t tilegrid_tiles_size = 0; #endif -#if CIRCUITPY_LVFONTIO +#if CIRCUITPY_LVFONTIO && CIRCUITPY_TERMINALIO static lvfontio_ondiskfont_t *lvfont = NULL; #endif diff --git a/tools/gen_display_resources.py b/tools/gen_display_resources.py index 7b6de6d95d445..9ed829f7d3738 100644 --- a/tools/gen_display_resources.py +++ b/tools/gen_display_resources.py @@ -8,8 +8,8 @@ import struct import sys -sys.path.insert(0, "bitmap_font") -sys.path.insert(0, "../../tools/bitmap_font") +sys.path.insert(0, "tools/bitmap_font") # For running from root +sys.path.insert(0, "../../tools/bitmap_font") # For running from a port directory from adafruit_bitmap_font import bitmap_font From 9e7edf2d1a76586f85d4ae5b5097bce9d7232516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Alse=CC=81n?= Date: Mon, 15 Dec 2025 20:01:49 +0100 Subject: [PATCH 253/517] Fixed compilation error when disabling CIRCUITPY_SERIAL_BLE || CIRCUITPY_BLE_FILE_SERVICE + variable name mismatch --- ports/espressif/common-hal/_bleio/PacketBuffer.c | 6 ++++++ shared-bindings/_bleio/PacketBuffer.h | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ports/espressif/common-hal/_bleio/PacketBuffer.c b/ports/espressif/common-hal/_bleio/PacketBuffer.c index db035157ceb6b..00aaae44e38ec 100644 --- a/ports/espressif/common-hal/_bleio/PacketBuffer.c +++ b/ports/espressif/common-hal/_bleio/PacketBuffer.c @@ -159,7 +159,13 @@ static int packet_buffer_on_ble_client_evt(struct ble_gap_event *event, void *pa return false; } +#if CIRCUITPY_SERIAL_BLE || CIRCUITPY_BLE_FILE_SERVICE +// Exposed via shared-bindings header when enabled void _common_hal_bleio_packet_buffer_construct( +#else +// Internal static helper when disabled (avoids "missing prototype" error) +static void _common_hal_bleio_packet_buffer_construct( +#endif bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, uint32_t *incoming_buffer, size_t incoming_buffer_size, uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, diff --git a/shared-bindings/_bleio/PacketBuffer.h b/shared-bindings/_bleio/PacketBuffer.h index 1a872512da27c..e58a2c39371fb 100644 --- a/shared-bindings/_bleio/PacketBuffer.h +++ b/shared-bindings/_bleio/PacketBuffer.h @@ -25,7 +25,7 @@ void common_hal_bleio_packet_buffer_construct( void _common_hal_bleio_packet_buffer_construct( bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, uint32_t *incoming_buffer, size_t incoming_buffer_size, - uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t outgoing_buffer_size, + uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, ble_event_handler_t *static_handler_entry); #endif mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len); From 5457fc8b22e1beeca2f7829c2e1702390394ae5e Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 17 Dec 2025 19:34:11 +0100 Subject: [PATCH 254/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 14 +++++++++----- locale/el.po | 14 +++++++++----- locale/hi.po | 14 +++++++++----- locale/ko.po | 14 +++++++++----- locale/ru.po | 14 +++++++++----- locale/tr.po | 14 +++++++++----- 6 files changed, 54 insertions(+), 30 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index 35196a280e3c7..b1e50a11e1271 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -1137,9 +1137,8 @@ msgstr "" "Frekvence musí být 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 nebo 1008 " "Mhz" -#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c -#: shared-bindings/busio/SPI.c +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "Funkce vyžaduje zámek" @@ -1482,8 +1481,7 @@ msgstr "Chybí jmp_pin. %q[%u] skáče na pin" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "Musí být podtřída %q." @@ -2420,6 +2418,12 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c diff --git a/locale/el.po b/locale/el.po index bde4522cc60f0..10d7d4d1f51bd 100644 --- a/locale/el.po +++ b/locale/el.po @@ -1143,9 +1143,8 @@ msgid "" "Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c -#: shared-bindings/busio/SPI.c +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "" @@ -1488,8 +1487,7 @@ msgstr "" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -2424,6 +2422,12 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c diff --git a/locale/hi.po b/locale/hi.po index 43737b6cdd827..ebbedfe7639cc 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -1119,9 +1119,8 @@ msgid "" "Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c -#: shared-bindings/busio/SPI.c +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "" @@ -1464,8 +1463,7 @@ msgstr "" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -2398,6 +2396,12 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c diff --git a/locale/ko.po b/locale/ko.po index f77519cf701d9..182f4d08d6bb8 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -1170,9 +1170,8 @@ msgstr "" "주파수는 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 또는 1008 Mhz 여야 " "합니다" -#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c -#: shared-bindings/busio/SPI.c +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "이 함수에는 잠금이 필요합니다" @@ -1518,8 +1517,7 @@ msgstr "jmp_pin이 누락되었습니다. %q[%u] 핀으로 점프합니다" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "%q의 하위클래스여야 합니다." @@ -2473,6 +2471,12 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c diff --git a/locale/ru.po b/locale/ru.po index bbd39319823d3..653eed6c0bfb9 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -1154,9 +1154,8 @@ msgstr "" "Частота должна быть 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 или 1008 " "МГц" -#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c -#: shared-bindings/busio/SPI.c +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "Функция требует блокировки" @@ -1503,8 +1502,7 @@ msgstr "Не хватает jmp_pin.%q [%u] прыгает на пин" msgid "Mount point directory missing" msgstr "Отсутствует каталог точки монтирования" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "Должен быть субклассом %q." @@ -2457,6 +2455,12 @@ msgstr "Неподдерживаемый тип сокета" msgid "Update failed" msgstr "Обновление не удалось" +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c diff --git a/locale/tr.po b/locale/tr.po index 966a9123083b8..511e9cb7f4654 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -1137,9 +1137,8 @@ msgstr "" "Frekans 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 ya da 1008 Mhz " "olmalıdır" -#: ports/zephyr-cp/bindings/zephyr_i2c/I2C.c shared-bindings/bitbangio/I2C.c -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/I2C.c -#: shared-bindings/busio/SPI.c +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c msgid "Function requires lock" msgstr "Fonksiyon kilit gerektirir" @@ -1483,8 +1482,7 @@ msgstr "" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -2420,6 +2418,12 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c From 3653803de0c600c7564d746192f2d5704e12670a Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 16 Dec 2025 13:31:02 -0800 Subject: [PATCH 255/517] Switch to Zephyr USB + QoL changes * Enable full builds * Two additional NXP boards --- main.c | 30 +- ports/zephyr-cp/app.overlay | 10 + ports/zephyr-cp/boards/board_aliases.cmake | 2 + ports/zephyr-cp/boards/ek_ra8d1.overlay | 2 + .../boards/frdm_mcxn947_mcxn947_cpu0.overlay | 5 + .../mimxrt1170_evk_mimxrt1176_cm7.overlay | 11 + .../nordic/nrf54h20dk/autogen_board_info.toml | 2 +- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 2 + .../boards/nrf54l15dk_nrf54l15_cpuapp.overlay | 1 + .../zephyr-cp/boards/nucleo_n657x0_q.overlay | 0 .../nxp/frdm_mcxn947/autogen_board_info.toml | 114 +++++ .../nxp/frdm_mcxn947/circuitpython.toml | 1 + .../mimxrt1170_evk/autogen_board_info.toml | 114 +++++ .../nxp/mimxrt1170_evk/circuitpython.toml | 1 + .../renesas/ek_ra6m5/autogen_board_info.toml | 2 +- .../renesas/ek_ra6m5/circuitpython.toml | 2 + .../renesas/ek_ra8d1/autogen_board_info.toml | 2 +- .../nucleo_n657x0_q/autogen_board_info.toml | 2 +- ports/zephyr-cp/common-hal/busio/UART.c | 11 +- ports/zephyr-cp/common-hal/busio/UART.h | 1 + ports/zephyr-cp/common-hal/usb_cdc/Serial.c | 68 +++ ports/zephyr-cp/common-hal/usb_cdc/Serial.h | 14 + ports/zephyr-cp/common-hal/usb_cdc/__init__.c | 40 ++ ports/zephyr-cp/common-hal/usb_cdc/__init__.h | 16 + .../zephyr-cp/cptools/build_circuitpython.py | 188 +------ ports/zephyr-cp/cptools/zephyr2cp.py | 33 +- ports/zephyr-cp/prj.conf | 11 +- ports/zephyr-cp/supervisor/port.c | 8 + ports/zephyr-cp/supervisor/serial.c | 54 +- ports/zephyr-cp/supervisor/usb.c | 474 ++++++++++++------ ports/zephyr-cp/supervisor/zephyr-cp.h | 9 + py/circuitpy_mpconfig.h | 2 +- shared-bindings/sdcardio/SDCard.c | 4 +- shared-bindings/supervisor/Runtime.c | 10 +- shared-bindings/usb_cdc/Serial.c | 4 +- shared-bindings/usb_cdc/Serial.h | 4 + shared-bindings/usb_cdc/__init__.h | 4 + supervisor/shared/serial.c | 18 +- supervisor/shared/usb.c | 65 +++ supervisor/shared/usb/usb.c | 38 +- supervisor/shared/workflow.c | 2 +- supervisor/usb.h | 1 + 42 files changed, 975 insertions(+), 407 deletions(-) create mode 100644 ports/zephyr-cp/app.overlay create mode 100644 ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay create mode 100644 ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay create mode 100644 ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay delete mode 100644 ports/zephyr-cp/boards/nucleo_n657x0_q.overlay create mode 100644 ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/nxp/frdm_mcxn947/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/nxp/mimxrt1170_evk/circuitpython.toml create mode 100644 ports/zephyr-cp/common-hal/usb_cdc/Serial.c create mode 100644 ports/zephyr-cp/common-hal/usb_cdc/Serial.h create mode 100644 ports/zephyr-cp/common-hal/usb_cdc/__init__.c create mode 100644 ports/zephyr-cp/common-hal/usb_cdc/__init__.h create mode 100644 ports/zephyr-cp/supervisor/zephyr-cp.h create mode 100644 supervisor/shared/usb.c diff --git a/main.c b/main.c index f4f67b0434787..d636c851b700e 100644 --- a/main.c +++ b/main.c @@ -219,21 +219,23 @@ static void stop_mp(void) { // Unmount all heap allocated vfs mounts. mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); - do { - if (gc_ptr_on_heap(vfs)) { - // mp_vfs_umount will splice out an unmounted vfs from the vfs_mount_table linked list. - mp_vfs_umount(vfs->obj); - // Start over at the beginning of the list since the first entry may have been removed. - vfs = MP_STATE_VM(vfs_mount_table); - continue; - } - vfs = vfs->next; - } while (vfs != NULL); + if (vfs != NULL) { + do { + if (gc_ptr_on_heap(vfs)) { + // mp_vfs_umount will splice out an unmounted vfs from the vfs_mount_table linked list. + mp_vfs_umount(vfs->obj); + // Start over at the beginning of the list since the first entry may have been removed. + vfs = MP_STATE_VM(vfs_mount_table); + continue; + } + vfs = vfs->next; + } while (vfs != NULL); - // The last vfs is CIRCUITPY and the root directory. - vfs = MP_STATE_VM(vfs_mount_table); - while (vfs->next != NULL) { - vfs = vfs->next; + // The last vfs is CIRCUITPY and the root directory. + vfs = MP_STATE_VM(vfs_mount_table); + while (vfs->next != NULL) { + vfs = vfs->next; + } } MP_STATE_VM(vfs_cur) = vfs; #endif diff --git a/ports/zephyr-cp/app.overlay b/ports/zephyr-cp/app.overlay new file mode 100644 index 0000000000000..0f34d64a80f1b --- /dev/null +++ b/ports/zephyr-cp/app.overlay @@ -0,0 +1,10 @@ +&zephyr_udc0 { + cdc_acm_console: cdc_acm_console { + compatible = "zephyr,cdc-acm-uart"; + label = "CircuitPython Console"; + }; + cdc_acm_data: cdc_acm_data { + compatible = "zephyr,cdc-acm-uart"; + label = "CircuitPython Data"; + }; +}; diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index bb400f188dbe9..7fc973efc7852 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -5,6 +5,8 @@ set(nordic_nrf54l15dk_BOARD_ALIAS nrf54l15dk/nrf54l15/cpuapp) set(nordic_nrf54h20dk_BOARD_ALIAS nrf54h20dk/nrf54h20/cpuapp) set(nordic_nrf5340dk_BOARD_ALIAS nrf5340dk/nrf5340/cpuapp) set(nordic_nrf7002dk_BOARD_ALIAS nrf7002dk/nrf5340/cpuapp) +set(nxp_frdm_mcxn947_BOARD_ALIAS frdm_mcxn947/mcxn947/cpu0) +set(nxp_mimxrt1170_evk_BOARD_ALIAS mimxrt1170_evk@A/mimxrt1176/cm7) set(st_stm32h7b3i_dk_BOARD_ALIAS stm32h7b3i_dk) set(st_nucleo_u575zi_q_BOARD_ALIAS nucleo_u575zi_q/stm32u575xx) set(st_nucleo_n657x0_q_BOARD_ALIAS nucleo_n657x0_q/stm32n657xx) diff --git a/ports/zephyr-cp/boards/ek_ra8d1.overlay b/ports/zephyr-cp/boards/ek_ra8d1.overlay index 29735d02b8fab..c13dfc457f508 100644 --- a/ports/zephyr-cp/boards/ek_ra8d1.overlay +++ b/ports/zephyr-cp/boards/ek_ra8d1.overlay @@ -1,3 +1,5 @@ &s28hl512t { /delete-node/ partitions; }; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay new file mode 100644 index 0000000000000..83242201c5b12 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay @@ -0,0 +1,5 @@ +&w25q64jvssiq { + /delete-node/ partitions; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay new file mode 100644 index 0000000000000..d6f6d4f0c15e7 --- /dev/null +++ b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay @@ -0,0 +1,11 @@ +&is25wp128 { + partitions{ + /delete-node/ storage_partition; + circuitpy_partition: partition@E20000 { + label = "circuitpy"; + reg = <0x00E20000 (DT_SIZE_M(2) - DT_SIZE_K(128))>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index dfb363f2ed87b..2cd411fbc0651 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -100,7 +100,7 @@ touchio = false traceback = true uheap = false usb = false -usb_cdc = false +usb_cdc = true usb_hid = false usb_host = false usb_midi = false diff --git a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay index 0d90c87931b61..a70eede551ed8 100644 --- a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -43,3 +43,5 @@ memory-regions = <&cpuapp_dma_region>; status = "okay"; }; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay new file mode 100644 index 0000000000000..39db1e981afe7 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay @@ -0,0 +1 @@ +// No app.overlay because it doesn't have USB. diff --git a/ports/zephyr-cp/boards/nucleo_n657x0_q.overlay b/ports/zephyr-cp/boards/nucleo_n657x0_q.overlay deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml new file mode 100644 index 0000000000000..f22d066fe64c2 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -0,0 +1,114 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "NXP Semiconductors FRDM-MCXN947" + +[modules] +__future__ = false +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = true # Zephyr board has busio +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/circuitpython.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml new file mode 100644 index 0000000000000..1768f33118079 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -0,0 +1,114 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "NXP Semiconductors MIMXRT1170-EVK/EVKB" + +[modules] +__future__ = false +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = true # Zephyr board has busio +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/circuitpython.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index c2d9048604f72..0a4bf3ce19fca 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -100,7 +100,7 @@ touchio = false traceback = true uheap = false usb = false -usb_cdc = false # No TinyUSB settings for r7fa6m5bh3cfc +usb_cdc = true usb_hid = false usb_host = false usb_midi = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/circuitpython.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/circuitpython.toml index 3272dd4c5f319..13ff40aa042db 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/circuitpython.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/circuitpython.toml @@ -1 +1,3 @@ +# TX is on pin P411. RX is on pin P410. + CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 4b004746f902d..518af74c405d9 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -100,7 +100,7 @@ touchio = false traceback = true uheap = false usb = false -usb_cdc = false # No TinyUSB settings for r7fa8d1bhecbd +usb_cdc = true usb_hid = false usb_host = false usb_midi = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index 4b9fbf5c11048..9806cd8a8116b 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -100,7 +100,7 @@ touchio = false traceback = true uheap = false usb = false -usb_cdc = false # No TinyUSB settings for stm32n657xx +usb_cdc = true usb_hid = false usb_host = false usb_midi = false diff --git a/ports/zephyr-cp/common-hal/busio/UART.c b/ports/zephyr-cp/common-hal/busio/UART.c index cecce48d45b24..b5aaadb3bb51d 100644 --- a/ports/zephyr-cp/common-hal/busio/UART.c +++ b/ports/zephyr-cp/common-hal/busio/UART.c @@ -67,7 +67,8 @@ mp_obj_t common_hal_busio_uart_construct_from_device(busio_uart_obj_t *self, con k_msgq_init(&self->msgq, receiver_buffer, 1, receiver_buffer_size); - self->timeout = K_USEC(100); + self->timeout = K_FOREVER; + self->write_timeout = K_FOREVER; self->rx_paused = false; uart_irq_rx_enable(uart_device); @@ -136,6 +137,14 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou self->timeout = K_USEC((uint64_t)(timeout * 1000000)); } +mp_float_t common_hal_busio_uart_get_write_timeout(busio_uart_obj_t *self) { + return (mp_float_t)self->write_timeout.ticks / 1000000.0; +} + +void common_hal_busio_uart_set_write_timeout(busio_uart_obj_t *self, mp_float_t write_timeout) { + self->write_timeout = K_USEC((uint64_t)(write_timeout * 1000000)); +} + uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { return k_msgq_num_used_get(&self->msgq); } diff --git a/ports/zephyr-cp/common-hal/busio/UART.h b/ports/zephyr-cp/common-hal/busio/UART.h index 27cd76c537b6d..25e767c5b285a 100644 --- a/ports/zephyr-cp/common-hal/busio/UART.h +++ b/ports/zephyr-cp/common-hal/busio/UART.h @@ -17,6 +17,7 @@ typedef struct { struct k_msgq msgq; k_timeout_t timeout; + k_timeout_t write_timeout; bool rx_paused; // set by irq if no space in rbuf } busio_uart_obj_t; diff --git a/ports/zephyr-cp/common-hal/usb_cdc/Serial.c b/ports/zephyr-cp/common-hal/usb_cdc/Serial.c new file mode 100644 index 0000000000000..272a78b31419e --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_cdc/Serial.c @@ -0,0 +1,68 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared/runtime/interrupt_char.h" +#include "shared-bindings/usb_cdc/Serial.h" +#include "shared-bindings/busio/UART.h" +#include "supervisor/shared/tick.h" + +mp_obj_t common_hal_usb_cdc_serial_construct_from_device(usb_cdc_serial_obj_t *self, const struct device *uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer) { + common_hal_busio_uart_construct_from_device(self, uart_device, receiver_buffer_size, receiver_buffer); + self->base.type = &usb_cdc_serial_type; + return MP_OBJ_FROM_PTR(self); +} + +size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { + return common_hal_busio_uart_read(self, data, len, errcode); +} + +size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + return common_hal_busio_uart_write(self, data, len, errcode); +} + +uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self) { + return common_hal_busio_uart_rx_characters_available(self); +} + +uint32_t common_hal_usb_cdc_serial_get_out_waiting(usb_cdc_serial_obj_t *self) { + // Return number of FIFO bytes currently occupied. + // return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(self->idx); + return 0; +} + +void common_hal_usb_cdc_serial_reset_input_buffer(usb_cdc_serial_obj_t *self) { + common_hal_busio_uart_clear_rx_buffer(self); +} + +uint32_t common_hal_usb_cdc_serial_reset_output_buffer(usb_cdc_serial_obj_t *self) { + // return tud_cdc_n_write_clear(self->idx); + return 0; +} + +uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self) { + // return tud_cdc_n_write_flush(self->idx); + return 0; +} + +bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self) { + return !common_hal_busio_uart_deinited(self); +} + +mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self) { + return common_hal_busio_uart_get_timeout(self); +} + +void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout) { + common_hal_busio_uart_set_timeout(self, timeout); +} + +mp_float_t common_hal_usb_cdc_serial_get_write_timeout(usb_cdc_serial_obj_t *self) { + return common_hal_busio_uart_get_write_timeout(self); +} + +void common_hal_usb_cdc_serial_set_write_timeout(usb_cdc_serial_obj_t *self, mp_float_t write_timeout) { + common_hal_busio_uart_set_write_timeout(self, write_timeout); +} diff --git a/ports/zephyr-cp/common-hal/usb_cdc/Serial.h b/ports/zephyr-cp/common-hal/usb_cdc/Serial.h new file mode 100644 index 0000000000000..9a110545a3700 --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_cdc/Serial.h @@ -0,0 +1,14 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-bindings/busio/UART.h" + +typedef busio_uart_obj_t usb_cdc_serial_obj_t; + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_usb_cdc_serial_construct_from_device(usb_cdc_serial_obj_t *self, const struct device *uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer); diff --git a/ports/zephyr-cp/common-hal/usb_cdc/__init__.c b/ports/zephyr-cp/common-hal/usb_cdc/__init__.c new file mode 100644 index 0000000000000..7bcae39673320 --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_cdc/__init__.c @@ -0,0 +1,40 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/gc.h" +#include "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "py/objtuple.h" +#include "shared-bindings/usb_cdc/__init__.h" +#include "shared-bindings/usb_cdc/Serial.h" +#include "supervisor/usb.h" + +static bool usb_cdc_console_is_enabled; +static bool usb_cdc_data_is_enabled; + +void usb_cdc_set_defaults(void) { + common_hal_usb_cdc_enable(true, + false); +} + +bool usb_cdc_console_enabled(void) { + return usb_cdc_console_is_enabled; +} + +bool usb_cdc_data_enabled(void) { + return usb_cdc_data_is_enabled; +} + +bool common_hal_usb_cdc_disable(void) { + return common_hal_usb_cdc_enable(false, false); +} + +bool common_hal_usb_cdc_enable(bool console, bool data) { + usb_cdc_console_is_enabled = console; + usb_cdc_data_is_enabled = data; + return true; +} diff --git a/ports/zephyr-cp/common-hal/usb_cdc/__init__.h b/ports/zephyr-cp/common-hal/usb_cdc/__init__.h new file mode 100644 index 0000000000000..daf06d580ad63 --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_cdc/__init__.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/mpconfig.h" +#include "py/objtuple.h" +#include "supervisor/usb.h" + +bool usb_cdc_console_enabled(void); +bool usb_cdc_data_enabled(void); + +void usb_cdc_set_defaults(void); diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 87ff00a450321..a5e4d72b902bb 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -55,9 +55,11 @@ "traceback", "warnings", "supervisor", + "errno", + "io", ] -# Flags that don't match with with a *bindings module. -MPCONFIG_FLAGS = ["array", "json"] +# Flags that don't match with with a *bindings module. Some used by adafruit_requests +MPCONFIG_FLAGS = ["array", "errno", "io", "json"] # List of other modules (the value) that can be enabled when another one (the key) is. REVERSE_DEPENDENCIES = { @@ -80,6 +82,8 @@ # Other flags to set when a module is enabled EXTRA_FLAGS = {"busio": ["BUSIO_SPI", "BUSIO_I2C"]} +SHARED_MODULE_AND_COMMON_HAL = ["os"] + async def preprocess_and_split_defs(compiler, source_file, build_path, flags): build_file = source_file.with_suffix(".pp") @@ -267,51 +271,12 @@ def determine_enabled_modules(board_info, portdir, srcdir): return enabled_modules, module_reasons -TINYUSB_SETTINGS = { - "": { - "CFG_TUSB_MCU": "OPT_MCU_MIMXRT10XX", - "CFG_TUD_CDC_RX_BUFSIZE": 640, - "CFG_TUD_CDC_TX_BUFSIZE": 512, - }, - "stm32u575xx": {"CFG_TUSB_MCU": "OPT_MCU_STM32U5"}, - "nrf52840": {"CFG_TUSB_MCU": "OPT_MCU_NRF5X"}, - "nrf5340": {"CFG_TUSB_MCU": "OPT_MCU_NRF5X"}, - # "r7fa8d1bhecbd": {"CFG_TUSB_MCU": "OPT_MCU_RAXXX", "USB_HIGHSPEED": "1", "USBHS_USB_INT_RESUME_IRQn": "54", "USBFS_INT_IRQn": "54", "CIRCUITPY_USB_DEVICE_INSTANCE": "1"}, - # ifeq ($(CHIP_FAMILY),$(filter $(CHIP_FAMILY),MIMXRT1011 MIMXRT1015)) - # CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=64 -DCFG_TUD_MSC_BUFSIZE=512 - # else - # CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_MSC_BUFSIZE=1024 - # endif -} - -TINYUSB_SOURCE = { - "stm32u575xx": [ - "src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c", - "src/portable/synopsys/dwc2/dcd_dwc2.c", - "src/portable/synopsys/dwc2/hcd_dwc2.c", - "src/portable/synopsys/dwc2/dwc2_common.c", - ], - "nrf52840": [ - "src/portable/nordic/nrf5x/dcd_nrf5x.c", - ], - "nrf5340": [ - "src/portable/nordic/nrf5x/dcd_nrf5x.c", - ], - # "r7fa8d1bhecbd": [ - # "src/portable/renesas/rusb2/dcd_rusb2.c", - # "src/portable/renesas/rusb2/hcd_rusb2.c", - # "src/portable/renesas/rusb2/rusb2_common.c", - # ], -} - - async def build_circuitpython(): circuitpython_flags = ["-DCIRCUITPY"] port_flags = [] enable_mpy_native = False - full_build = False + full_build = True usb_host = False - tusb_mem_align = 4 board = cmake_args["BOARD_ALIAS"] if not board: board = cmake_args["BOARD"] @@ -325,20 +290,14 @@ async def build_circuitpython(): circuitpython_flags.append(f"-DCIRCUITPY_FULL_BUILD={1 if full_build else 0}") circuitpython_flags.append(f"-DCIRCUITPY_USB_HOST={1 if usb_host else 0}") circuitpython_flags.append(f"-DCIRCUITPY_BOARD_ID='\"{board}\"'") - circuitpython_flags.append(f"-DCIRCUITPY_TUSB_MEM_ALIGN={tusb_mem_align}") circuitpython_flags.append(f"-DCIRCUITPY_TRANSLATE_OBJECT={1 if lto else 0}") circuitpython_flags.append("-DINTERNAL_FLASH_FILESYSTEM") circuitpython_flags.append("-DLONGINT_IMPL_MPZ") circuitpython_flags.append("-DCIRCUITPY_SSL_MBEDTLS") circuitpython_flags.append("-DFFCONF_H='\"lib/oofatfs/ffconf.h\"'") circuitpython_flags.extend(("-I", srcdir)) - circuitpython_flags.extend(("-I", srcdir / "lib/tinyusb/src")) - circuitpython_flags.extend(("-I", srcdir / "supervisor/shared/usb")) circuitpython_flags.extend(("-I", builddir)) circuitpython_flags.extend(("-I", portdir)) - # circuitpython_flags.extend(("-I", srcdir / "ports" / port / "peripherals")) - - # circuitpython_flags.extend(("-I", build_path / board_id)) genhdr = builddir / "genhdr" genhdr.mkdir(exist_ok=True, parents=True) @@ -364,10 +323,7 @@ async def build_circuitpython(): ) board_info = board_autogen_task.result() mpconfigboard_fn = board_tools.find_mpconfigboard(portdir, board) - mpconfigboard = { - "USB_VID": 0x1209, - "USB_PID": 0x000C, - } + mpconfigboard = {"USB_VID": 0x1209, "USB_PID": 0x000C, "USB_INTERFACE_NAME": "CircuitPython"} if mpconfigboard_fn is None: mpconfigboard_fn = ( portdir / "boards" / board_info["vendor_id"] / board / "circuitpython.toml" @@ -377,7 +333,7 @@ async def build_circuitpython(): ) elif mpconfigboard_fn.exists(): with mpconfigboard_fn.open("rb") as f: - mpconfigboard = tomllib.load(f) + mpconfigboard.update(tomllib.load(f)) autogen_board_info_fn = mpconfigboard_fn.parent / "autogen_board_info.toml" @@ -386,6 +342,7 @@ async def build_circuitpython(): circuitpython_flags.extend(board_info["cflags"]) supervisor_source = [ "main.c", + "extmod/modjson.c", "extmod/vfs_fat.c", "lib/tlsf/tlsf.c", portdir / "background.c", @@ -417,20 +374,18 @@ async def build_circuitpython(): # if web_workflow: # supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c")) - usb_num_endpoint_pairs = board_info.get("usb_num_endpoint_pairs", 0) - soc = board_info["soc"] - usb_ok = usb_num_endpoint_pairs > 0 and soc in TINYUSB_SETTINGS - circuitpython_flags.append(f"-DCIRCUITPY_TINYUSB={1 if usb_ok else 0}") + usb_ok = board_info.get("usb_device", False) circuitpython_flags.append(f"-DCIRCUITPY_USB_DEVICE={1 if usb_ok else 0}") - tinyusb_files = [] if usb_ok: enabled_modules.add("usb_cdc") - for setting in TINYUSB_SETTINGS[soc]: - circuitpython_flags.append(f"-D{setting}={TINYUSB_SETTINGS[soc][setting]}") - tinyusb_files.extend((top / "lib" / "tinyusb" / path for path in TINYUSB_SOURCE[soc])) + for macro in ("USB_PID", "USB_VID"): + print(f"Setting {macro} to {mpconfigboard.get(macro)}") circuitpython_flags.append(f"-D{macro}=0x{mpconfigboard.get(macro):04x}") + circuitpython_flags.append( + f"-DUSB_INTERFACE_NAME='\"{mpconfigboard['USB_INTERFACE_NAME']}\"'" + ) for macro, limit, value in ( ("USB_PRODUCT", 16, board_info["name"]), ("USB_MANUFACTURER", 8, board_info["vendor"]), @@ -438,107 +393,15 @@ async def build_circuitpython(): circuitpython_flags.append(f"-D{macro}='\"{value}\"'") circuitpython_flags.append(f"-D{macro}_{limit}='\"{value[:limit]}\"'") - usb_interface_name = "CircuitPython" - - circuitpython_flags.append("-DCFG_TUSB_OS=OPT_OS_ZEPHYR") - circuitpython_flags.append(f"-DUSB_INTERFACE_NAME='\"{usb_interface_name}\"'") - circuitpython_flags.append(f"-DUSB_NUM_ENDPOINT_PAIRS={usb_num_endpoint_pairs}") - for direction in ("IN", "OUT"): - circuitpython_flags.append(f"-DUSB_NUM_{direction}_ENDPOINTS={usb_num_endpoint_pairs}") - # USB is special because it doesn't have a matching module. - msc_enabled = board_info["flash_count"] > 0 - if msc_enabled: - circuitpython_flags.append("-DCFG_TUD_MSC_BUFSIZE=1024") - circuitpython_flags.append("-DCIRCUITPY_USB_MSC_ENABLED_DEFAULT=1") - tinyusb_files.append(top / "lib/tinyusb/src/class/msc/msc_device.c") - supervisor_source.append(top / "supervisor/shared/usb/usb_msc_flash.c") - circuitpython_flags.append(f"-DCIRCUITPY_USB_MSC={1 if msc_enabled else 0}") - if "usb_cdc" in enabled_modules: - tinyusb_files.extend(top.glob("lib/tinyusb/*.c")) - tinyusb_files.append(top / "lib/tinyusb/src/class/cdc/cdc_device.c") - circuitpython_flags.append("-DCFG_TUD_CDC_RX_BUFSIZE=640") - circuitpython_flags.append("-DCFG_TUD_CDC_TX_BUFSIZE=512") - circuitpython_flags.append("-DCFG_TUD_CDC=2") - circuitpython_flags.append("-DCIRCUITPY_USB_CDC_CONSOLE_ENABLED_DEFAULT=1") - circuitpython_flags.append("-DCIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT=0") - - if "usb_hid_enabled_default" not in mpconfigboard: - mpconfigboard["usb_hid_enabled_default"] = usb_num_endpoint_pairs >= 5 - if "usb_midi_enabled_default" not in mpconfigboard: - mpconfigboard["usb_midi_enabled_default"] = usb_num_endpoint_pairs >= 8 - - tinyusb_files.extend( - (top / "lib/tinyusb/src/common/tusb_fifo.c", top / "lib/tinyusb/src/tusb.c") - ) - supervisor_source.extend( - (portdir / "supervisor/usb.c", top / "supervisor/shared/usb/usb.c") - ) + circuitpython_flags.append("-DCIRCUITPY_USB_CDC_CONSOLE_ENABLED_DEFAULT=1") + circuitpython_flags.append("-DCIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT=0") - tinyusb_files.extend( - ( - top / "lib/tinyusb/src/device/usbd.c", - top / "lib/tinyusb/src/device/usbd_control.c", - ) - ) supervisor_source.extend( - (top / "supervisor/shared/usb/usb_desc.c", top / "supervisor/shared/usb/usb_device.c") + (portdir / "supervisor/usb.c", srcdir / "supervisor/shared/usb.c") ) - elif usb_num_endpoint_pairs > 0: - module_reasons["usb_cdc"] = f"No TinyUSB settings for {soc}" - - circuitpython_flags.append(f"-DCIRCUITPY_PORT_SERIAL={0 if usb_ok else 1}") - # ifeq ($(CIRCUITPY_USB_HID), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/class/hid/hid_device.c \ - # shared-bindings/usb_hid/__init__.c \ - # shared-bindings/usb_hid/Device.c \ - # shared-module/usb_hid/__init__.c \ - # shared-module/usb_hid/Device.c \ - - # endif - - # ifeq ($(CIRCUITPY_USB_MIDI), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/class/midi/midi_device.c \ - # shared-bindings/usb_midi/__init__.c \ - # shared-bindings/usb_midi/PortIn.c \ - # shared-bindings/usb_midi/PortOut.c \ - # shared-module/usb_midi/__init__.c \ - # shared-module/usb_midi/PortIn.c \ - # shared-module/usb_midi/PortOut.c \ - - # endif - - # ifeq ($(CIRCUITPY_USB_VIDEO), 1) - # SRC_SUPERVISOR += \ - # shared-bindings/usb_video/__init__.c \ - # shared-module/usb_video/__init__.c \ - # shared-bindings/usb_video/USBFramebuffer.c \ - # shared-module/usb_video/USBFramebuffer.c \ - # lib/tinyusb/src/class/video/video_device.c \ - - # CFLAGS += -DCFG_TUD_VIDEO=1 -DCFG_TUD_VIDEO_STREAMING=1 -DCFG_TUD_VIDEO_STREAMING_EP_BUFSIZE=256 -DCFG_TUD_VIDEO_STREAMING_BULK=1 - # endif - - # ifeq ($(CIRCUITPY_USB_VENDOR), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/class/vendor/vendor_device.c \ - - # endif - - # ifeq ($(CIRCUITPY_TINYUSB_HOST), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/host/hub.c \ - # lib/tinyusb/src/host/usbh.c \ - - # endif - - # ifeq ($(CIRCUITPY_USB_KEYBOARD_WORKFLOW), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/class/hid/hid_host.c \ - # supervisor/shared/usb/host_keyboard.c \ - - # endif + + # Always use port serial. It'll switch between USB and UART automatically. + circuitpython_flags.append("-DCIRCUITPY_PORT_SERIAL=1") if "ssl" in enabled_modules: # TODO: Figure out how to get these paths from zephyr @@ -588,9 +451,12 @@ async def build_circuitpython(): if enabled: hal_source.extend(portdir.glob(f"bindings/{module.name}/*.c")) + len_before = len(hal_source) hal_source.extend(top.glob(f"ports/zephyr-cp/common-hal/{module.name}/*.c")) + # Only include shared-module/*.c if no common-hal/*.c files were found + if len(hal_source) == len_before or module.name in SHARED_MODULE_AND_COMMON_HAL: + hal_source.extend(top.glob(f"shared-module/{module.name}/*.c")) hal_source.extend(top.glob(f"shared-bindings/{module.name}/*.c")) - hal_source.extend(top.glob(f"shared-module/{module.name}/*.c")) if os.environ.get("CI", "false") == "true": # Fail the build if it isn't up to date. @@ -687,8 +553,6 @@ async def build_circuitpython(): source_files.extend(assembly_files) - source_files.extend(tinyusb_files) - objects = [] async with asyncio.TaskGroup() as tg: for source_file in source_files: diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 8cfbc0118a7f9..77404ec2bec3f 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -113,6 +113,35 @@ "GPIO0", "GPIO1", ], + "nxp,cam-44pins-connector": ["CAM_RESETB", "CAM_PWDN"], + "nxp,lcd-8080": [ + "TOUCH_SCL", + "TOUCH_SDA", + "TOUCH_INT", + "BACKLIGHT", + "RESET", + "LCD_DC", + "LCD_CS", + "LCD_WR", + "LCD_RD", + "LCD_TE", + "LCD_D0", + "LCD_D1", + "LCD_D2", + "LCD_D3", + "LCD_D4", + "LCD_D5", + "LCD_D6", + "LCD_D7", + "LCD_D8", + "LCD_D9", + "LCD_D10", + "LCD_D11", + "LCD_D12", + "LCD_D13", + "LCD_D14", + "LCD_D15", + ], "raspberrypi,csi-connector": [ "CSI_D0_N", "CSI_D0_P", @@ -431,7 +460,7 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 continue if driver == "flash": pass # Handled by find_flash_devices() - elif driver == "usb/udc": + elif driver == "usb/udc" or "zephyr_udc0" in node.labels: board_info["usb_device"] = True props = node.props if "num-bidir-endpoints" not in props: @@ -467,7 +496,7 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 all_ioports.append(node.labels[0]) if status == "okay": ioports[node.labels[0]] = set(range(0, ngpios)) - if gpio_map: + if gpio_map and compatible[0] != "gpio-nexus": i = 0 for offset, t, label in gpio_map._markers: if not label: diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 3d2f1fe69aaba..485559fe48073 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -16,7 +16,13 @@ CONFIG_STACK_SENTINEL=y CONFIG_DEBUG_THREAD_INFO=y # CONFIG_DEBUG_INFO=y -CONFIG_USB_DEVICE_STACK=n +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USBD_CDC_ACM_CLASS=y +CONFIG_USBD_MAX_SPEED=1 +CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n + +CONFIG_USBD_MSC_CLASS=y +CONFIG_USBD_MSC_LUNS_PER_INSTANCE=1 CONFIG_HWINFO=y CONFIG_REBOOT=y @@ -27,6 +33,9 @@ CONFIG_LOG_BLOCK_IN_THREAD=y CONFIG_EVENTS=y +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y + CONFIG_I2C=y CONFIG_SPI=y CONFIG_SPI_ASYNC=y diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index c58de56d707ae..799e44800cf9a 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -178,3 +178,11 @@ size_t port_heap_get_largest_free_size(void) { // IDF does this. Not sure why. return tlsf_fit_size(heap, max_size); } + +void assert_post_action(const char *file, unsigned int line) { + printk("Assertion failed at %s:%u\n", file, line); + __asm__ ("bkpt"); + while (1) { + ; + } +} diff --git a/ports/zephyr-cp/supervisor/serial.c b/ports/zephyr-cp/supervisor/serial.c index a62e00a774274..3ae3e73fa10c1 100644 --- a/ports/zephyr-cp/supervisor/serial.c +++ b/ports/zephyr-cp/supervisor/serial.c @@ -6,54 +6,80 @@ #include "supervisor/shared/serial.h" -#if CIRCUITPY_USB_DEVICE == 0 -#include "shared-bindings/busio/UART.h" +#include "supervisor/zephyr-cp.h" -static busio_uart_obj_t zephyr_console; +#if CIRCUITPY_USB_DEVICE == 1 +#include "shared-bindings/usb_cdc/Serial.h" +usb_cdc_serial_obj_t *usb_console; +#else +#include "shared-bindings/busio/UART.h" +static busio_uart_obj_t uart_console; static uint8_t buffer[64]; #endif void port_serial_early_init(void) { #if CIRCUITPY_USB_DEVICE == 0 - zephyr_console.base.type = &busio_uart_type; - common_hal_busio_uart_construct_from_device(&zephyr_console, DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), sizeof(buffer), buffer); + uart_console.base.type = &busio_uart_type; + common_hal_busio_uart_construct_from_device(&uart_console, DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), sizeof(buffer), buffer); #endif } void port_serial_init(void) { + #if CIRCUITPY_USB_DEVICE == 1 + usb_console = usb_cdc_serial_get_console(); + #endif } bool port_serial_connected(void) { #if CIRCUITPY_USB_DEVICE == 1 - return false; + if (usb_console == NULL) { + return false; + } + return common_hal_usb_cdc_serial_get_connected(usb_console); #else return true; #endif } char port_serial_read(void) { - #if CIRCUITPY_USB_DEVICE == 0 + #if CIRCUITPY_USB_DEVICE == 1 + if (usb_console == NULL) { + return -1; + } char buf[1]; - size_t count = common_hal_busio_uart_read(&zephyr_console, buf, 1, NULL); + size_t count = common_hal_usb_cdc_serial_read(usb_console, buf, 1, NULL); if (count == 0) { return -1; } return buf[0]; #else - return -1; + char buf[1]; + size_t count = common_hal_busio_uart_read(&uart_console, buf, 1, NULL); + if (count == 0) { + return -1; + } + return buf[0]; #endif } uint32_t port_serial_bytes_available(void) { - #if CIRCUITPY_USB_DEVICE == 0 - return common_hal_busio_uart_rx_characters_available(&zephyr_console); + #if CIRCUITPY_USB_DEVICE == 1 + if (usb_console == NULL) { + return 0; + } + return common_hal_usb_cdc_serial_get_in_waiting(usb_console); #else - return 0; + return common_hal_busio_uart_rx_characters_available(&uart_console); #endif } void port_serial_write_substring(const char *text, uint32_t length) { - #if CIRCUITPY_USB_DEVICE == 0 - common_hal_busio_uart_write(&zephyr_console, text, length, NULL); + #if CIRCUITPY_USB_DEVICE == 1 + if (usb_console == NULL) { + return; + } + common_hal_usb_cdc_serial_write(usb_console, text, length, NULL); + #else + common_hal_busio_uart_write(&uart_console, text, length, NULL); #endif } diff --git a/ports/zephyr-cp/supervisor/usb.c b/ports/zephyr-cp/supervisor/usb.c index 18eb2847ad981..844d4fae75934 100644 --- a/ports/zephyr-cp/supervisor/usb.c +++ b/ports/zephyr-cp/supervisor/usb.c @@ -1,202 +1,364 @@ #include "supervisor/usb.h" -#include "tusb_option.h" +#include "shared-bindings/usb_cdc/__init__.h" +#include "shared-bindings/usb_cdc/Serial.h" -#if CFG_TUSB_MCU == OPT_MCU_STM32U5 -#include -#endif +#include "supervisor/zephyr-cp.h" -#if CFG_TUSB_MCU == OPT_MCU_NRF5X -#include -#include -#endif +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "lib/oofatfs/diskio.h" +#include "lib/oofatfs/ff.h" #include +#include #include -#include #include +#include +#include -#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) -#define UDC_IRQ_NAME otghs -#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs) -#define UDC_IRQ_NAME otgfs -#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usb) -#define UDC_IRQ_NAME usb -#elif DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_usb) -#define UDC_IRQ_NAME usbhs_ir -#endif +#include "shared-module/storage/__init__.h" +#include "supervisor/filesystem.h" +#include "supervisor/shared/reload.h" -#if DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_usb) -#define USB_NAME usbhs -#else -#define USB_NAME zephyr_udc0 -#endif +#include +LOG_MODULE_REGISTER(usb, LOG_LEVEL_INF); + +#define USB_DEVICE DT_NODELABEL(zephyr_udc0) + +USBD_DEVICE_DEFINE(main_usbd, + DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + USB_VID, USB_PID); + +USBD_DESC_LANG_DEFINE(main_lang); +USBD_DESC_MANUFACTURER_DEFINE(main_mfr, USB_MANUFACTURER); +USBD_DESC_PRODUCT_DEFINE(main_product, USB_PRODUCT); + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); -#define USB_DEVICE DT_NODELABEL(USB_NAME) +/* doc configuration instantiation start */ +static const uint8_t attributes = 0; -#ifdef UDC_IRQ_NAME -#define UDC_IRQ DT_IRQ_BY_NAME(USB_DEVICE, UDC_IRQ_NAME, irq) -#define UDC_IRQ_PRI DT_IRQ_BY_NAME(USB_DEVICE, UDC_IRQ_NAME, priority) -#else -#define UDC_IRQ DT_IRQ(USB_DEVICE, irq) -#define UDC_IRQ_PRI DT_IRQ(USB_DEVICE, priority) +USBD_CONFIGURATION_DEFINE(main_fs_config, + attributes, + 100, &fs_cfg_desc); + +USBD_CONFIGURATION_DEFINE(main_hs_config, + attributes, + 100, &hs_cfg_desc); + +static usb_cdc_serial_obj_t usb_cdc_console_obj; +static usb_cdc_serial_obj_t usb_cdc_data_obj; + +#ifndef USBD_DEFINE_MSC_LUN +#error "MSC not enabled" #endif -PINCTRL_DT_DEFINE(USB_DEVICE); -static const struct pinctrl_dev_config *usb_pcfg = - PINCTRL_DT_DEV_CONFIG_GET(USB_DEVICE); +#define LUN_COUNT 1 +#define MSC_FLASH_BLOCK_SIZE 512 + +// The ellipsis range in the designated initializer of `ejected` is not standard C, +// but it works in both gcc and clang. +static bool locked[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; + +// Set to true if a write was in a file data or metadata area, +// as opposed to in the filesystem metadata area (e.g., dirty bit). +// Used to determine if an auto-reload is warranted. +static bool content_write[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; + +int _zephyr_disk_init(struct disk_info *disk); +int _zephyr_disk_status(struct disk_info *disk); +int _zephyr_disk_read(struct disk_info *disk, uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector); +int _zephyr_disk_write(struct disk_info *disk, const uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector); +int _zephyr_disk_ioctl(struct disk_info *disk, uint8_t cmd, void *buff); + +static const struct disk_operations disk_ops = { + .init = _zephyr_disk_init, + .status = _zephyr_disk_status, + .read = _zephyr_disk_read, + .write = _zephyr_disk_write, + .ioctl = _zephyr_disk_ioctl, +}; -#if CFG_TUSB_MCU == OPT_MCU_NRF5X -// Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h -enum { - USB_EVT_DETECTED = 0, - USB_EVT_REMOVED = 1, - USB_EVT_READY = 2 +static struct disk_info circuitpy_disk = { + .name = "CIRCUITPY", + .ops = &disk_ops, + .dev = NULL }; -#ifdef NRF5340_XXAA - #define LFCLK_SRC_RC CLOCK_LFCLKSRC_SRC_LFRC - #define VBUSDETECT_Msk USBREG_USBREGSTATUS_VBUSDETECT_Msk - #define OUTPUTRDY_Msk USBREG_USBREGSTATUS_OUTPUTRDY_Msk - #define GPIOTE_IRQn GPIOTE1_IRQn -#else - #define LFCLK_SRC_RC CLOCK_LFCLKSRC_SRC_RC - #define VBUSDETECT_Msk POWER_USBREGSTATUS_VBUSDETECT_Msk - #define OUTPUTRDY_Msk POWER_USBREGSTATUS_OUTPUTRDY_Msk -#endif +USBD_DEFINE_MSC_LUN(circuitpy_lun, "CIRCUITPY", "Zephyr", "FlashDisk", "0.00"); -// tinyusb function that handles power event (detected, ready, removed) -// We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled. -extern void tusb_hal_nrf_power_event(uint32_t event); -// nrf power callback, could be unused if SD is enabled or usb is disabled (board_test example) -TU_ATTR_UNUSED static void power_event_handler(nrfx_power_usb_evt_t event) { - tusb_hal_nrf_power_event((uint32_t)event); +int _zephyr_disk_init(struct disk_info *disk) { + printk("Initializing disk\n"); + return 0; } -#endif -void init_usb_hardware(void) { - #if CFG_TUSB_MCU == OPT_MCU_RAXXX - #if !USBHS_PHY_CLOCK_SOURCE_IS_XTAL - if (data->udc_cfg.usb_speed == USBD_SPEED_HS) { - LOG_ERR("High-speed operation is not supported in case PHY clock source is not " - "XTAL"); - return; +int _zephyr_disk_status(struct disk_info *disk) { + fs_user_mount_t *root = filesystem_circuitpy(); + int lun = 0; + if (root == NULL) { + printk("Status: No media\n"); + return DISK_STATUS_NOMEDIA; } - #endif + if (!filesystem_is_writable_by_usb(root)) { + printk("Status: Read-only\n"); + return DISK_STATUS_WR_PROTECT; + } + // Lock the blockdev once we say we're writable. + if (!locked[lun] && !blockdev_lock(root)) { + printk("Status: Locked\n"); + return DISK_STATUS_WR_PROTECT; + } + locked[lun] = true; + return DISK_STATUS_OK; +} + +int _zephyr_disk_read(struct disk_info *disk, uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector) { + fs_user_mount_t *root = filesystem_circuitpy(); + + uint32_t disk_block_count; + disk_ioctl(root, GET_SECTOR_COUNT, &disk_block_count); + + if (start_sector + num_sector > disk_block_count) { + return -EIO; + } + disk_read(root, data_buf, start_sector, num_sector); + return 0; +} - R_ICU->IELSR[UDC_IRQ] = ELC_EVENT_USBHS_USB_INT_RESUME; +int _zephyr_disk_write(struct disk_info *disk, const uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector) { + fs_user_mount_t *root = filesystem_circuitpy(); + int lun = 0; + autoreload_suspend(AUTORELOAD_SUSPEND_USB); + disk_write(root, data_buf, start_sector, num_sector); + // Since by getting here we assume the mount is read-only to + // CircuitPython let's update the cached FatFs sector if it's the one + // we just wrote. + if + #if FF_MAX_SS != FF_MIN_SS + (root->fatfs.ssize == MSC_FLASH_BLOCK_SIZE) + #else + // The compiler can optimize this away. + (FF_MAX_SS == FILESYSTEM_BLOCK_SIZE) #endif + { + if (start_sector == root->fatfs.winsect && start_sector > 0) { + memcpy(root->fatfs.win, + data_buf + MSC_FLASH_BLOCK_SIZE * (root->fatfs.winsect - start_sector), + MSC_FLASH_BLOCK_SIZE); + } + } + + // A write to an lba below fatbase is in the filesystem metadata (BPB) area or the "Reserved Region", + // and is probably setting or clearing the dirty bit. This should not trigger auto-reload. + // All other writes will trigger auto-reload. + if (start_sector >= root->fatfs.fatbase) { + content_write[lun] = true; + } + return 0; +} + +int _zephyr_disk_ioctl(struct disk_info *disk, uint8_t cmd, void *buff) { + + fs_user_mount_t *root = filesystem_circuitpy(); + int lun = 0; + switch (cmd) { + case DISK_IOCTL_GET_SECTOR_COUNT: + disk_ioctl(root, GET_SECTOR_COUNT, buff); + return 0; + case DISK_IOCTL_GET_SECTOR_SIZE: + disk_ioctl(root, GET_SECTOR_SIZE, buff); + return 0; + case DISK_IOCTL_CTRL_SYNC: + disk_ioctl(root, CTRL_SYNC, buff); + autoreload_resume(AUTORELOAD_SUSPEND_USB); + + // This write is complete; initiate an autoreload if this was a file data or metadata write, + // not just a dirty-bit write. + if (content_write[lun] && lun == 0) { + autoreload_trigger(); + content_write[lun] = false; + } + return 0; + default: + printk("Unsupported disk ioctl %02x\n", cmd); + return -ENOTSUP; + } + return 0; +} +static void _msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) { + LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); +} + +void usb_init(void) { + printk("Initializing USB\n"); + int err; - IRQ_CONNECT(UDC_IRQ, UDC_IRQ_PRI, usb_irq_handler, 0, 0); + printk("Adding language descriptor\n"); + err = usbd_add_descriptor(&main_usbd, &main_lang); + if (err) { + LOG_ERR("Failed to initialize language descriptor (%d)", err); + return; + } - /* Configure USB GPIOs */ - int err = pinctrl_apply_state(usb_pcfg, PINCTRL_STATE_DEFAULT); - if (err < 0) { - printk("USB pinctrl setup failed (%d)\n", err); - } else { - printk("USB pins setup\n"); + err = usbd_add_descriptor(&main_usbd, &main_mfr); + if (err) { + LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err); + return; } -// #ifdef USB_DRD_FS -// // STM32U535/STM32U545 + err = usbd_add_descriptor(&main_usbd, &main_product); + if (err) { + LOG_ERR("Failed to initialize product descriptor (%d)", err); + return; + } -// /* Enable USB power on Pwrctrl CR2 register */ -// HAL_PWREx_EnableVddUSB(); + bool console = usb_cdc_console_enabled(); + if (console) { + uint8_t *receiver_buffer = port_malloc(128, true); + if (receiver_buffer != NULL) { + common_hal_usb_cdc_serial_construct_from_device(&usb_cdc_console_obj, DEVICE_DT_GET(DT_NODELABEL(cdc_acm_console)), 128, receiver_buffer); + } else { + console = false; + } + } + usb_cdc_set_console(console ? MP_OBJ_FROM_PTR(&usb_cdc_console_obj) : mp_const_none); + + bool data = usb_cdc_data_enabled(); + if (data) { + uint8_t *receiver_buffer = port_malloc(128, true); + if (receiver_buffer != NULL) { + common_hal_usb_cdc_serial_construct_from_device(&usb_cdc_data_obj, DEVICE_DT_GET(DT_NODELABEL(cdc_acm_data)), 128, receiver_buffer); + } else { + data = false; + } + } + usb_cdc_set_data(data ? MP_OBJ_FROM_PTR(&usb_cdc_data_obj) : mp_const_none); -// /* USB clock enable */ -// __HAL_RCC_USB_FS_CLK_ENABLE(); + err = disk_access_register(&circuitpy_disk); + if (err) { + printk("Failed to register disk access %d\n", err); + return; + } -// #endif + if (USBD_SUPPORTS_HIGH_SPEED && + usbd_caps_speed(&main_usbd) == USBD_SPEED_HS) { + printk("Adding High-Speed configuration\n"); + err = usbd_add_configuration(&main_usbd, USBD_SPEED_HS, + &main_hs_config); + if (err) { + LOG_ERR("Failed to add High-Speed configuration"); + return; + } + + printk("Registering High-Speed cdc_acm class\n"); + if (usb_cdc_console_enabled()) { + err = usbd_register_class(&main_usbd, "cdc_acm_0", USBD_SPEED_HS, 1); + if (err) { + printk("Failed to add register classes %d\n", err); + return; + } + } + + if (usb_cdc_data_enabled()) { + err = usbd_register_class(&main_usbd, "cdc_acm_1", USBD_SPEED_HS, 1); + if (err) { + printk("Failed to add register classes %d\n", err); + return; + } + } + + err = usbd_register_class(&main_usbd, "msc_0", USBD_SPEED_HS, 1); + if (err) { + printk("Failed to add register MSC class %d\n", err); + return; + } else { + printk("Registered MSC class for high speed\n"); + } + + usbd_device_set_code_triple(&main_usbd, USBD_SPEED_HS, + USB_BCC_MISCELLANEOUS, 0x02, 0x01); + } - #if CFG_TUSB_MCU == OPT_MCU_STM32U5 && defined(USB_OTG_FS) - /* Enable USB power on Pwrctrl CR2 register */ - // HAL_PWREx_EnableVddUSB(); - LL_PWR_EnableVddUSB(); + /* doc configuration register start */ + printk("Adding Full-Speed configuration\n"); + err = usbd_add_configuration(&main_usbd, USBD_SPEED_FS, + &main_fs_config); + if (err) { + LOG_ERR("Failed to add Full-Speed configuration"); + return; + } + /* doc configuration register end */ - /* USB clock enable */ - __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + /* doc functions register start */ - #endif + if (usb_cdc_console_enabled()) { + printk("Registering Full-Speed cdc_acm class\n"); + err = usbd_register_class(&main_usbd, "cdc_acm_0", USBD_SPEED_FS, 1); + if (err) { + printk("Failed to add register classes\n"); + return; + } + } -// #ifdef USB_OTG_HS -// // STM59x/Ax/Fx/Gx only have 1 USB HS port + if (usb_cdc_data_enabled()) { + printk("Registering Full-Speed cdc_acm class\n"); + err = usbd_register_class(&main_usbd, "cdc_acm_1", USBD_SPEED_FS, 1); + if (err) { + printk("Failed to add register classes\n"); + return; + } + } -// #if CFG_TUSB_OS == OPT_OS_FREERTOS -// // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) -// NVIC_SetPriority(OTG_HS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); -// #endif + err = usbd_register_class(&main_usbd, "msc_0", USBD_SPEED_FS, 1); + if (err) { + printk("Failed to add register MSC class %d\n", err); + return; + } + /* doc functions register end */ -// /* USB clock enable */ -// __HAL_RCC_USB_OTG_HS_CLK_ENABLE(); -// __HAL_RCC_USBPHYC_CLK_ENABLE(); + usbd_device_set_code_triple(&main_usbd, USBD_SPEED_FS, + USB_BCC_MISCELLANEOUS, 0x02, 0x01); + printk("Setting self powered\n"); + usbd_self_powered(&main_usbd, attributes & USB_SCD_SELF_POWERED); -// /* Enable USB power on Pwrctrl CR2 register */ -// HAL_PWREx_EnableVddUSB(); -// HAL_PWREx_EnableUSBHSTranceiverSupply(); + printk("Registering callback\n"); + err = usbd_msg_register_cb(&main_usbd, _msg_cb); + if (err) { + LOG_ERR("Failed to register message callback"); + return; + } -// /*Configuring the SYSCFG registers OTG_HS PHY*/ -// HAL_SYSCFG_EnableOTGPHY(SYSCFG_OTG_HS_PHY_ENABLE); + printk("usbd_init\n"); + err = usbd_init(&main_usbd); + if (err) { + LOG_ERR("Failed to initialize device support"); + return; + } -// // Disable VBUS sense (B device) -// USB_OTG_HS->GCCFG &= ~USB_OTG_GCCFG_VBDEN; + printk("USB initialized\n"); -// // B-peripheral session valid override enable -// USB_OTG_HS->GCCFG |= USB_OTG_GCCFG_VBVALEXTOEN; -// USB_OTG_HS->GCCFG |= USB_OTG_GCCFG_VBVALOVAL; -// #endif // USB_OTG_FS + err = usbd_enable(&main_usbd); + if (err) { + LOG_ERR("Failed to enable device support"); + return; + } + printk("usbd enabled\n"); +} +bool usb_connected(void) { + return false; +} - #if CFG_TUSB_MCU == OPT_MCU_NRF5X - #ifdef CONFIG_HAS_HW_NRF_USBREG - /* Use CLOCK/POWER priority for compatibility with other series where - * USB events are handled by CLOCK interrupt handler. - */ - IRQ_CONNECT(USBREGULATOR_IRQn, - DT_IRQ(DT_INST(0, nordic_nrf_clock), priority), - nrfx_isr, nrfx_usbreg_irq_handler, 0); - irq_enable(USBREGULATOR_IRQn); - #endif - // USB power may already be ready at this time -> no event generated - // We need to invoke the handler based on the status initially - uint32_t usb_reg; - { - // Power module init - static const nrfx_power_config_t pwr_cfg = { - .dcdcen = (DT_PROP(DT_INST(0, nordic_nrf5x_regulator), regulator_initial_mode) - == NRF5X_REG_MODE_DCDC), - #if NRFX_POWER_SUPPORTS_DCDCEN_VDDH - .dcdcenhv = COND_CODE_1(CONFIG_SOC_SERIES_NRF52X, - (DT_NODE_HAS_STATUS_OKAY(DT_INST(0, nordic_nrf52x_regulator_hv))), - (DT_NODE_HAS_STATUS_OKAY(DT_INST(0, nordic_nrf53x_regulator_hv)))), - #endif - }; - nrfx_power_init(&pwr_cfg); - - // Register tusb function as USB power handler - // cause cast-function-type warning - const nrfx_power_usbevt_config_t config = {.handler = power_event_handler}; - nrfx_power_usbevt_init(&config); - nrfx_power_usbevt_enable(); - - // USB power may already be ready at this time -> no event generated - // We need to invoke the handler based on the status initially - #ifdef NRF5340_XXAA - usb_reg = NRF_USBREGULATOR->USBREGSTATUS; - #else - usb_reg = NRF_POWER->USBREGSTATUS; - #endif - } - - if (usb_reg & VBUSDETECT_Msk) { - tusb_hal_nrf_power_event(USB_EVT_DETECTED); - } - if (usb_reg & OUTPUTRDY_Msk) { - tusb_hal_nrf_power_event(USB_EVT_READY); - } - - printk("usb started hopefully\n"); - #endif +void usb_disconnect(void) { +} +usb_cdc_serial_obj_t *usb_cdc_serial_get_console(void) { + if (usb_cdc_console_enabled()) { + return &usb_cdc_console_obj; + } + return NULL; } diff --git a/ports/zephyr-cp/supervisor/zephyr-cp.h b/ports/zephyr-cp/supervisor/zephyr-cp.h new file mode 100644 index 0000000000000..ecf7d42e3b48c --- /dev/null +++ b/ports/zephyr-cp/supervisor/zephyr-cp.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/usb_cdc/Serial.h" + +usb_cdc_serial_obj_t *usb_cdc_serial_get_console(void); diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 4acbf96db038e..386d1ee3d1e12 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -650,7 +650,7 @@ void background_callback_run_all(void); // Align the internal sector buffer. Useful when it is passed into TinyUSB for // loads. #ifndef MICROPY_FATFS_WINDOW_ALIGNMENT -#define MICROPY_FATFS_WINDOW_ALIGNMENT CIRCUITPY_TUSB_MEM_ALIGN +#define MICROPY_FATFS_WINDOW_ALIGNMENT 64 // Espressif is strictest #endif #define FF_FS_CASE_INSENSITIVE_COMPARISON_ASCII_ONLY (1) diff --git a/shared-bindings/sdcardio/SDCard.c b/shared-bindings/sdcardio/SDCard.c index 26892a79462d3..65c5f80b52b02 100644 --- a/shared-bindings/sdcardio/SDCard.c +++ b/shared-bindings/sdcardio/SDCard.c @@ -53,10 +53,12 @@ //| import sdcardio //| import storage //| +//| # Make sure to make an "sd" folder on CIRCUITPY +//| //| sd = sdcardio.SDCard(board.SPI(), board.SD_CS) //| vfs = storage.VfsFat(sd) //| storage.mount(vfs, '/sd') -//| os.listdir('/sd')""" +//| print(os.listdir('/sd'))""" //| static void check_for_deinit(sdcardio_sdcard_obj_t *self) { diff --git a/shared-bindings/supervisor/Runtime.c b/shared-bindings/supervisor/Runtime.c index 7b497639939f0..20cfd2e82007a 100644 --- a/shared-bindings/supervisor/Runtime.c +++ b/shared-bindings/supervisor/Runtime.c @@ -20,12 +20,12 @@ #include "supervisor/shared/status_leds.h" #include "supervisor/shared/bluetooth/bluetooth.h" -#if CIRCUITPY_DISPLAYIO -#include "shared-bindings/displayio/__init__.h" +#if CIRCUITPY_USB_DEVICE +#include "supervisor/usb.h" #endif -#if CIRCUITPY_TINYUSB -#include "tusb.h" +#if CIRCUITPY_DISPLAYIO +#include "shared-bindings/displayio/__init__.h" #endif static supervisor_run_reason_t _run_reason; @@ -52,7 +52,7 @@ static supervisor_run_reason_t _run_reason; //| """Returns the USB enumeration status (read-only).""" static mp_obj_t supervisor_runtime_get_usb_connected(mp_obj_t self) { #if CIRCUITPY_USB_DEVICE - return mp_obj_new_bool(tud_ready()); + return mp_obj_new_bool(usb_connected()); #else return mp_const_false; #endif diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 4d6851351afd2..eb526615927bd 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -194,7 +194,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_output_buffer_obj, usb_cdc_serial static mp_obj_t usb_cdc_serial_get_timeout(mp_obj_t self_in) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_float_t timeout = common_hal_usb_cdc_serial_get_timeout(self); - return (timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->timeout); + return (timeout < 0.0f) ? mp_const_none : mp_obj_new_float(timeout); } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_timeout_obj, usb_cdc_serial_get_timeout); @@ -219,7 +219,7 @@ MP_PROPERTY_GETSET(usb_cdc_serial_timeout_obj, static mp_obj_t usb_cdc_serial_get_write_timeout(mp_obj_t self_in) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_float_t write_timeout = common_hal_usb_cdc_serial_get_write_timeout(self); - return (write_timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->write_timeout); + return (write_timeout < 0.0f) ? mp_const_none : mp_obj_new_float(write_timeout); } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_write_timeout_obj, usb_cdc_serial_get_write_timeout); diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 30249cef389c1..091f7514a4fb7 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -6,7 +6,11 @@ #pragma once +#if defined(__ZEPHYR__) && __ZEPHYR__ == 1 +#include "common-hal/usb_cdc/Serial.h" +#else #include "shared-module/usb_cdc/Serial.h" +#endif extern const mp_obj_type_t usb_cdc_serial_type; diff --git a/shared-bindings/usb_cdc/__init__.h b/shared-bindings/usb_cdc/__init__.h index 34099f7e8fd61..154f259f4101d 100644 --- a/shared-bindings/usb_cdc/__init__.h +++ b/shared-bindings/usb_cdc/__init__.h @@ -6,7 +6,11 @@ #pragma once +#if defined(__ZEPHYR__) && __ZEPHYR__ == 1 +#include "common-hal/usb_cdc/__init__.h" +#else #include "shared-module/usb_cdc/__init__.h" +#endif // Set the module dict entries. void usb_cdc_set_console(mp_obj_t serial_obj); diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index 94b429b6ab650..5728a95e08f4c 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -219,7 +219,7 @@ void serial_init(void) { } bool serial_connected(void) { - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected()) { return true; } @@ -235,11 +235,11 @@ bool serial_connected(void) { } #endif - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC if (usb_cdc_console_enabled() && tud_cdc_connected()) { return true; } - #elif CIRCUITPY_USB_DEVICE + #elif CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE if (tud_cdc_connected()) { return true; } @@ -273,7 +273,7 @@ bool serial_connected(void) { } char serial_read(void) { - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected() && tud_vendor_available() > 0) { char tiny_buffer; tud_vendor_read(&tiny_buffer, 1); @@ -327,7 +327,7 @@ char serial_read(void) { return -1; } #endif - #if CIRCUITPY_USB_DEVICE + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE return (char)tud_cdc_read_char(); #endif @@ -338,7 +338,7 @@ uint32_t serial_bytes_available(void) { // There may be multiple serial input channels, so sum the count from all. uint32_t count = 0; - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected()) { count += tud_vendor_available(); } @@ -360,7 +360,7 @@ uint32_t serial_bytes_available(void) { count += usb_keyboard_chars_available(); #endif - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC if (usb_cdc_console_enabled()) { count += tud_cdc_available(); } @@ -399,7 +399,7 @@ uint32_t serial_write_substring(const char *text, uint32_t length) { return length_sent; } - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected()) { length_sent = tud_vendor_write(text, length); } @@ -423,7 +423,7 @@ uint32_t serial_write_substring(const char *text, uint32_t length) { } #endif - #if CIRCUITPY_USB_DEVICE + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE // Delay the very first write if (tud_cdc_connected() && !_first_write_done) { mp_hal_delay_ms(50); diff --git a/supervisor/shared/usb.c b/supervisor/shared/usb.c new file mode 100644 index 0000000000000..ff0dd1f84672f --- /dev/null +++ b/supervisor/shared/usb.c @@ -0,0 +1,65 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/usb.h" + +#if CIRCUITPY_STORAGE +#include "shared-module/storage/__init__.h" +#endif + +#if CIRCUITPY_USB_DEVICE +#include "shared-bindings/supervisor/__init__.h" + +#if CIRCUITPY_USB_CDC +#include "shared-module/usb_cdc/__init__.h" +#endif + +#if CIRCUITPY_USB_HID +#include "shared-module/usb_hid/__init__.h" +#endif + +#if CIRCUITPY_USB_MIDI +#include "shared-module/usb_midi/__init__.h" +#endif + +#if CIRCUITPY_USB_VIDEO +#include "shared-module/usb_video/__init__.h" +#endif +#endif + +// Set up USB defaults before any USB changes are made in boot.py +void usb_set_defaults(void) { + #if CIRCUITPY_USB_DEVICE + #if CIRCUITPY_STORAGE && CIRCUITPY_USB_MSC + storage_usb_set_defaults(); + #endif + + #if CIRCUITPY_USB_CDC + usb_cdc_set_defaults(); + #endif + + #if CIRCUITPY_USB_HID + usb_hid_set_defaults(); + #endif + + #if CIRCUITPY_USB_MIDI + usb_midi_set_defaults(); + #endif + #endif +}; + +// Call this when ready to run code.py or a REPL, and a VM has been started. +void usb_setup_with_vm(void) { + #if CIRCUITPY_USB_DEVICE + #if CIRCUITPY_USB_HID + usb_hid_setup_devices(); + #endif + + #if CIRCUITPY_USB_MIDI + usb_midi_setup_ports(); + #endif + #endif +} diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index e67c15d022cc9..86a0cf8237eb1 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -87,6 +87,10 @@ bool usb_enabled(void) { return tusb_inited(); } +bool usb_connected(void) { + return tud_ready(); +} + MP_WEAK void post_usb_init(void) { } @@ -144,40 +148,6 @@ void usb_init(void) { #endif } -// Set up USB defaults before any USB changes are made in boot.py -void usb_set_defaults(void) { - #if CIRCUITPY_USB_DEVICE - #if CIRCUITPY_STORAGE && CIRCUITPY_USB_MSC - storage_usb_set_defaults(); - #endif - - #if CIRCUITPY_USB_CDC - usb_cdc_set_defaults(); - #endif - - #if CIRCUITPY_USB_HID - usb_hid_set_defaults(); - #endif - - #if CIRCUITPY_USB_MIDI - usb_midi_set_defaults(); - #endif - #endif -}; - -// Call this when ready to run code.py or a REPL, and a VM has been started. -void usb_setup_with_vm(void) { - #if CIRCUITPY_USB_DEVICE - #if CIRCUITPY_USB_HID - usb_hid_setup_devices(); - #endif - - #if CIRCUITPY_USB_MIDI - usb_midi_setup_ports(); - #endif - #endif -} - void usb_background(void) { if (usb_enabled()) { #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO diff --git a/supervisor/shared/workflow.c b/supervisor/shared/workflow.c index df2055580fc5d..c1999b4a7b68a 100644 --- a/supervisor/shared/workflow.c +++ b/supervisor/shared/workflow.c @@ -89,7 +89,7 @@ bool supervisor_workflow_active(void) { void supervisor_workflow_start(void) { // Start USB after giving boot.py a chance to tweak behavior. - #if CIRCUITPY_TINYUSB + #if CIRCUITPY_USB_DEVICE // Setup USB connection after heap is available. // It needs the heap to build descriptors. usb_init(); diff --git a/supervisor/usb.h b/supervisor/usb.h index 10f033a3616b8..d6e4236c9b48d 100644 --- a/supervisor/usb.h +++ b/supervisor/usb.h @@ -48,6 +48,7 @@ typedef struct { bool usb_enabled(void); void usb_add_interface_string(uint8_t interface_string_index, const char str[]); bool usb_build_descriptors(const usb_identification_t *identification); +bool usb_connected(void); void usb_disconnect(void); void usb_init(void); void usb_set_defaults(void); From eb0be7e240320210b3f41d7150f80a2c48042098 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 18 Dec 2025 10:07:01 -0800 Subject: [PATCH 256/517] Add new usb.c to old builds --- supervisor/supervisor.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 4848915630cff..9ebdd52068e73 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -116,6 +116,7 @@ ifeq ($(CIRCUITPY_TINYUSB),1) lib/tinyusb/src/common/tusb_fifo.c \ lib/tinyusb/src/tusb.c \ supervisor/usb.c \ + supervisor/shared/usb.c \ supervisor/shared/usb/usb.c \ ifeq ($(CIRCUITPY_USB_DEVICE),1) From 248dd38ef36b588752e5ac0b9484e3112bf5cf79 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 18 Dec 2025 11:13:33 -0800 Subject: [PATCH 257/517] Fix host-only tinyusb builds --- supervisor/shared/usb/usb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index 86a0cf8237eb1..fbe8be9788207 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -88,7 +88,11 @@ bool usb_enabled(void) { } bool usb_connected(void) { + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE return tud_ready(); + #else + return false; + #endif } MP_WEAK void post_usb_init(void) { From 51057e75d8f418e4696eeffd08fecdc48090341b Mon Sep 17 00:00:00 2001 From: noqman Date: Mon, 22 Dec 2025 04:32:10 +0800 Subject: [PATCH 258/517] Add frozen PAJ7620 submodule and adjust firmware flash allocation --- .gitmodules | 3 +++ frozen/CircuitPython_edupico2_paj7620 | 1 + .../boards/cytron_edu_pico_v2_2w/link.ld | 1 + .../cytron_edu_pico_v2_2w/mpconfigboard.mk | 17 +++++++++++------ 4 files changed, 16 insertions(+), 6 deletions(-) create mode 160000 frozen/CircuitPython_edupico2_paj7620 create mode 100644 ports/raspberrypi/boards/cytron_edu_pico_v2_2w/link.ld diff --git a/.gitmodules b/.gitmodules index 4cef2eeb9d500..a7b67b8d86051 100644 --- a/.gitmodules +++ b/.gitmodules @@ -418,3 +418,6 @@ [submodule "frozen/Adafruit_CircuitPython_OPT4048"] path = frozen/Adafruit_CircuitPython_OPT4048 url = https://github.com/adafruit/Adafruit_CircuitPython_OPT4048.git +[submodule "frozen/CircuitPython_edupico2_paj7620"] + path = frozen/CircuitPython_edupico2_paj7620 + url = https://github.com/CytronTechnologies/CircuitPython_edupico2_paj7620.git diff --git a/frozen/CircuitPython_edupico2_paj7620 b/frozen/CircuitPython_edupico2_paj7620 new file mode 160000 index 0000000000000..16a4d78472499 --- /dev/null +++ b/frozen/CircuitPython_edupico2_paj7620 @@ -0,0 +1 @@ +Subproject commit 16a4d784724992862cffd594aeed8c627ecc18b3 diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/link.ld b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/link.ld new file mode 100644 index 0000000000000..e814bead4c51e --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/link.ld @@ -0,0 +1 @@ +firmware_size = 1532k; diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk index 2bea1359b4a83..1fa8666cfad5e 100644 --- a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk @@ -9,10 +9,10 @@ CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" -CIRCUITPY__EVE = 1 - CIRCUITPY_SDCARDIO = 1 +CIRCUITPY__EVE = 1 + CIRCUITPY_CYW43 = 1 CIRCUITPY_SSL = 1 CIRCUITPY_HASHLIB = 1 @@ -21,6 +21,11 @@ CIRCUITPY_MDNS = 1 CIRCUITPY_SOCKETPOOL = 1 CIRCUITPY_WIFI = 1 +CIRCUITPY_MAX3421E = 0 +CIRCUITPY_ULAB = 0 +CIRCUITPY_ZLIB = 0 +CIRCUITPY_SSL_MBEDTLS = 0 + CFLAGS += \ -DCYW43_PIN_WL_DYNAMIC=0 \ -DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \ @@ -33,9 +38,6 @@ CFLAGS += \ -DCYW43_WL_GPIO_LED_PIN=0 -DCYW43_PIO_CLOCK_DIV_INT=3 -# Must be accompanied by a linker script change -CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' - # The default is -O3. Change to -O2 because the build was overflowing. OPTIMIZATION_FLAGS = -O2 @@ -46,9 +48,12 @@ FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Motor FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SimpleIO FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_framebuf +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_OPT4048 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_OPT4048 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ImageLoad FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_AHTx0 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/CircuitPython_edupico2_paj7620 + +CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' From 195938db6b57051b51381c85b5e7a22023bd8bd5 Mon Sep 17 00:00:00 2001 From: Noqman Muzafar Date: Mon, 22 Dec 2025 14:55:48 +0800 Subject: [PATCH 259/517] Re-enable CIRCUITPY_SSL_MBEDTLS and change the optimization flag to -Os --- .../boards/cytron_edu_pico_v2_2w/mpconfigboard.mk | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk index 1fa8666cfad5e..6bcb010159e25 100644 --- a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk @@ -24,7 +24,6 @@ CIRCUITPY_WIFI = 1 CIRCUITPY_MAX3421E = 0 CIRCUITPY_ULAB = 0 CIRCUITPY_ZLIB = 0 -CIRCUITPY_SSL_MBEDTLS = 0 CFLAGS += \ -DCYW43_PIN_WL_DYNAMIC=0 \ @@ -38,8 +37,8 @@ CFLAGS += \ -DCYW43_WL_GPIO_LED_PIN=0 -DCYW43_PIO_CLOCK_DIV_INT=3 -# The default is -O3. Change to -O2 because the build was overflowing. -OPTIMIZATION_FLAGS = -O2 +# The default is -O3. +OPTIMIZATION_FLAGS = -Os # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text From 83a933d1b2209968adc6d01e7d00dbac3de3e2d0 Mon Sep 17 00:00:00 2001 From: Noqman Muzafar Date: Tue, 23 Dec 2025 16:33:55 +0800 Subject: [PATCH 260/517] Disable sdcardio and add Adafruit_SD frozen --- .../boards/cytron_edu_pico_v2_2w/mpconfigboard.mk | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk index 6bcb010159e25..4e40cb792c73e 100644 --- a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk @@ -9,8 +9,6 @@ CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" -CIRCUITPY_SDCARDIO = 1 - CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 @@ -21,10 +19,6 @@ CIRCUITPY_MDNS = 1 CIRCUITPY_SOCKETPOOL = 1 CIRCUITPY_WIFI = 1 -CIRCUITPY_MAX3421E = 0 -CIRCUITPY_ULAB = 0 -CIRCUITPY_ZLIB = 0 - CFLAGS += \ -DCYW43_PIN_WL_DYNAMIC=0 \ -DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \ @@ -52,6 +46,7 @@ FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ImageLoad FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_AHTx0 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer FROZEN_MPY_DIRS += $(TOP)/frozen/CircuitPython_edupico2_paj7620 From 9160a494cf68db413e738e816d5d32de7b44fb53 Mon Sep 17 00:00:00 2001 From: Noqman Muzafar Date: Tue, 23 Dec 2025 22:31:06 +0800 Subject: [PATCH 261/517] Enable sdcardio and unfreeze Adafruit_SD --- .../raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk index 4e40cb792c73e..ac0bb6d6f2736 100644 --- a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk +++ b/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk @@ -9,6 +9,8 @@ CHIP_FAMILY = rp2 EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" +CIRCUITPY_SDCARDIO = 1 + CIRCUITPY__EVE = 1 CIRCUITPY_CYW43 = 1 @@ -43,7 +45,6 @@ FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SimpleIO FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_framebuf FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_OPT4048 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 -FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ImageLoad FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_AHTx0 FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD From ddeef5bab871f4461073c9882b17fd06e2e5eb87 Mon Sep 17 00:00:00 2001 From: Manjunath CV Date: Thu, 25 Dec 2025 00:43:37 +0530 Subject: [PATCH 262/517] Updated USB_VID and USB_PID from https://pid.codes/1209/DEC1/ --- .../boards/weact_studio_rp2350b_core/mpconfigboard.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk index ceef6b4846d94..d86f28280f3a0 100644 --- a/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk @@ -1,5 +1,5 @@ -USB_VID = 0x2E8A -USB_PID = 0x000F +USB_VID = 0x1209 +USB_PID = 0xDEC1 USB_PRODUCT = "RP2350B Core" USB_MANUFACTURER = "WeAct Studio" From 1e9c67312c544444cfe5fdec4750c65e2d68f92b Mon Sep 17 00:00:00 2001 From: David Dyck Date: Wed, 24 Dec 2025 13:28:35 -0800 Subject: [PATCH 263/517] Update monotonic function docstring for clarity Clarify the return value of the monotonic function to specify it returns fractional seconds. looking at cpython's time.monotonic() documentation, one sees in the first line "Return the value (in fractional seconds) of a monotonic clock" - I would have expected a similar introduction when reading that for circuitpython at https://docs.circuitpython.org/en/latest/shared-bindings/time/ I see there is a fall back that says "For more information, refer to the original CPython documentation" but would it have hurt to have mention that the units of the float returned by time.monotomic was in seconds? ( there are 7 mentions of millisecond(s), --- shared-bindings/time/__init__.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/time/__init__.c b/shared-bindings/time/__init__.c index 8a591da368858..86f73d95ca6b2 100644 --- a/shared-bindings/time/__init__.c +++ b/shared-bindings/time/__init__.c @@ -22,7 +22,7 @@ //| //| //| def monotonic() -> float: -//| """Returns an always increasing value of time with an unknown reference +//| """Returns an always increasing value of time (in fractional **seconds**) with an unknown reference //| point. Only use it to compare against other values from `time.monotonic()` //| during the same code run. //| From 75b3c3974315c063ab9bd1fcc57fe13c2599efaa Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 24 Dec 2025 16:34:10 -0500 Subject: [PATCH 264/517] Fix comment formatting in monotonic function --- shared-bindings/time/__init__.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared-bindings/time/__init__.c b/shared-bindings/time/__init__.c index 86f73d95ca6b2..e19d97251bcb8 100644 --- a/shared-bindings/time/__init__.c +++ b/shared-bindings/time/__init__.c @@ -22,12 +22,12 @@ //| //| //| def monotonic() -> float: -//| """Returns an always increasing value of time (in fractional **seconds**) with an unknown reference +//| """Returns an always increasing value of time, in fractional seconds, with an unknown reference //| point. Only use it to compare against other values from `time.monotonic()` //| during the same code run. //| //| On most boards, `time.monotonic()` converts a 64-bit millisecond tick counter -//| to a float. Floats on most boards are encoded in 30 bits internally, with +//| to seconds, as a float. Floats on most boards are encoded in 30 bits internally, with //| effectively 22 bits of precision. The float returned by `time.monotonic()` will //| accurately represent time to millisecond precision only up to 2**22 milliseconds //| (about 1.165 hours). From 7a3c9aeccfee6de3c46349c742ad2e342007058b Mon Sep 17 00:00:00 2001 From: Manjunath CV Date: Thu, 25 Dec 2025 17:30:10 +0530 Subject: [PATCH 265/517] Updated pin names --- .../boards/weact_studio_rp2350b_core/pins.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c index 989855a3843a3..39febd2a9f385 100644 --- a/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c @@ -55,13 +55,29 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_GP37), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_GP38), MP_ROM_PTR(&pin_GPIO38) }, { MP_ROM_QSTR(MP_QSTR_GP39), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_GP40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_GP41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_GP42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_GP43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_GP44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_GP45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_GP46), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_GP47), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO47) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From c7e605fc40bd300fb20a6461089439bbbe99612e Mon Sep 17 00:00:00 2001 From: Manjunath CV Date: Thu, 25 Dec 2025 17:34:20 +0530 Subject: [PATCH 266/517] Updated pin names --- .../boards/weact_studio_rp2350b_core/pins.c | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c index 39febd2a9f385..d6a82becfdd47 100644 --- a/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c @@ -34,12 +34,12 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, { MP_ROM_QSTR(MP_QSTR_GP23), MP_ROM_PTR(&pin_GPIO23) }, - { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO23) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO23) }, { MP_ROM_QSTR(MP_QSTR_GP24), MP_ROM_PTR(&pin_GPIO24) }, { MP_ROM_QSTR(MP_QSTR_GP25), MP_ROM_PTR(&pin_GPIO25) }, - { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO25) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO25) }, { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, @@ -57,27 +57,35 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_GP39), MP_ROM_PTR(&pin_GPIO39) }, { MP_ROM_QSTR(MP_QSTR_GP40), MP_ROM_PTR(&pin_GPIO40) }, - { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO40) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO40) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP40_A0), MP_ROM_PTR(&pin_GPIO40) }, { MP_ROM_QSTR(MP_QSTR_GP41), MP_ROM_PTR(&pin_GPIO41) }, - { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP41_A1), MP_ROM_PTR(&pin_GPIO41) }, { MP_ROM_QSTR(MP_QSTR_GP42), MP_ROM_PTR(&pin_GPIO42) }, - { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO42) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO42) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP42_A2), MP_ROM_PTR(&pin_GPIO42) }, { MP_ROM_QSTR(MP_QSTR_GP43), MP_ROM_PTR(&pin_GPIO43) }, - { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO43) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO43) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP43_A3), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_GP44), MP_ROM_PTR(&pin_GPIO44) }, - { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO44) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO44) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP44_A4), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_GP45), MP_ROM_PTR(&pin_GPIO45) }, - { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO45) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO45) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP45_A5), MP_ROM_PTR(&pin_GPIO45) }, { MP_ROM_QSTR(MP_QSTR_GP46), MP_ROM_PTR(&pin_GPIO46) }, - { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO46) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO46) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP46_A6), MP_ROM_PTR(&pin_GPIO46) }, { MP_ROM_QSTR(MP_QSTR_GP47), MP_ROM_PTR(&pin_GPIO47) }, - { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO47) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO47) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP47_A7), MP_ROM_PTR(&pin_GPIO47) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From ca2afccb1ca74a188964d4cc0b77c1c4e903e89a Mon Sep 17 00:00:00 2001 From: Noqman Muzafar Date: Mon, 29 Dec 2025 15:58:53 +0800 Subject: [PATCH 267/517] Rename board folder/id from cytron_edu_pico_v2_2w to cytron_edu_v2_pico_2w --- .../{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/board.c | 0 .../{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/link.ld | 0 .../mpconfigboard.h | 0 .../mpconfigboard.mk | 0 .../pico-sdk-configboard.h | 0 .../{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/pins.c | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename ports/raspberrypi/boards/{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/board.c (100%) rename ports/raspberrypi/boards/{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/link.ld (100%) rename ports/raspberrypi/boards/{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/mpconfigboard.h (100%) rename ports/raspberrypi/boards/{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/mpconfigboard.mk (100%) rename ports/raspberrypi/boards/{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/pico-sdk-configboard.h (100%) rename ports/raspberrypi/boards/{cytron_edu_pico_v2_2w => cytron_edu_v2_pico_2w}/pins.c (100%) diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/board.c b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/board.c similarity index 100% rename from ports/raspberrypi/boards/cytron_edu_pico_v2_2w/board.c rename to ports/raspberrypi/boards/cytron_edu_v2_pico_2w/board.c diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/link.ld b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/link.ld similarity index 100% rename from ports/raspberrypi/boards/cytron_edu_pico_v2_2w/link.ld rename to ports/raspberrypi/boards/cytron_edu_v2_pico_2w/link.ld diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.h b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.h similarity index 100% rename from ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.h rename to ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.h diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.mk similarity index 100% rename from ports/raspberrypi/boards/cytron_edu_pico_v2_2w/mpconfigboard.mk rename to ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.mk diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pico-sdk-configboard.h b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pico-sdk-configboard.h similarity index 100% rename from ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pico-sdk-configboard.h rename to ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pico-sdk-configboard.h diff --git a/ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pins.c b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pins.c similarity index 100% rename from ports/raspberrypi/boards/cytron_edu_pico_v2_2w/pins.c rename to ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pins.c From 5a4446ea09ad3f883d9db136b5d92f15476c7e3c Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 4 Jan 2026 22:24:50 -0500 Subject: [PATCH 268/517] espressif_bindings/Camera.c: fix type of framebuffer_count --- ports/espressif/bindings/espcamera/Camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/bindings/espcamera/Camera.c b/ports/espressif/bindings/espcamera/Camera.c index ed6b542a02ff3..712ff8827d779 100644 --- a/ports/espressif/bindings/espcamera/Camera.c +++ b/ports/espressif/bindings/espcamera/Camera.c @@ -261,7 +261,7 @@ static mp_obj_t espcamera_camera_reconfigure(mp_uint_t n_args, const mp_obj_t *p args[ARG_grab_mode].u_obj != MP_ROM_NONE ? validate_grab_mode(args[ARG_grab_mode].u_obj, MP_QSTR_grab_mode) : common_hal_espcamera_camera_get_grab_mode(self); - bool framebuffer_count = + mp_int_t framebuffer_count = args[ARG_framebuffer_count].u_obj != MP_ROM_NONE ? mp_obj_get_int(args[ARG_framebuffer_count].u_obj) : common_hal_espcamera_camera_get_framebuffer_count(self); From c5e4979655ed6a90e8510723d18949a01979a84d Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 5 Nov 2025 12:10:55 -0500 Subject: [PATCH 269/517] stop building intel and universal macos mpy-cross --- .github/workflows/build.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f837d793bddc..697a110605425 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -131,33 +131,18 @@ jobs: gcc --version python3 --version msgfmt --version - - name: Build mpy-cross - run: make -C mpy-cross -j4 - - uses: actions/upload-artifact@v4 - with: - name: mpy-cross-macos-x64 - path: mpy-cross/build/mpy-cross - name: Build mpy-cross (arm64) run: make -C mpy-cross -j4 -f Makefile.m1 V=2 - uses: actions/upload-artifact@v4 with: name: mpy-cross-macos-arm64 path: mpy-cross/build-arm64/mpy-cross-arm64 - - name: Make universal binary - run: lipo -create -output mpy-cross-macos-universal mpy-cross/build/mpy-cross mpy-cross/build-arm64/mpy-cross-arm64 - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: mpy-cross-macos-universal - path: mpy-cross-macos-universal - name: Upload to S3 if: >- (github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository_owner == 'adafruit') || (github.event_name == 'release' && (github.event.action == 'published' || github.event.action == 'rerequested')) run: | - [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross-macos-universal s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-universal --no-progress --region us-east-1 [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/build-arm64/mpy-cross-arm64 s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-arm64 --no-progress --region us-east-1 - [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/build/mpy-cross s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-x64 --no-progress --region us-east-1 env: AWS_PAGER: '' AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} From 0caa9edc6d1b594ddf819166dacc3ccad6d21918 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 4 Nov 2025 16:36:14 -0800 Subject: [PATCH 270/517] Use latest macos in CI --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 697a110605425..fbd3af440c6b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -107,7 +107,7 @@ jobs: cp-version: ${{ needs.scheduler.outputs.cp-version }} mpy-cross-mac: - runs-on: macos-13 + runs-on: macos-latest needs: scheduler if: needs.scheduler.outputs.ports != '{}' env: From 85a8f1ec464c84084af0fdff3998c52039e6de75 Mon Sep 17 00:00:00 2001 From: Joe Freeman Date: Tue, 6 Jan 2026 07:11:52 -0500 Subject: [PATCH 271/517] Add APA102 and DEFAULT_I2C_BUS definitions to the lilygo_tdongle_s3 --- ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h b/ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h index fe5525ef05633..0edea0bf63b40 100644 --- a/ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h +++ b/ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h @@ -11,5 +11,8 @@ #define MICROPY_HW_BOARD_NAME "LILYGO T-Dongle S3" #define MICROPY_HW_MCU_NAME "ESP32S3" -#define DEFAULT_UART_BUS_RX (&pin_GPIO44) -#define DEFAULT_UART_BUS_TX (&pin_GPIO43) +#define MICROPY_HW_APA102_MOSI (&pin_GPIO40) +#define MICROPY_HW_APA102_SCK (&pin_GPIO39) + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO44) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO43) From 7b77c43e0eddd834ad75961461eb2c0826d9cf0e Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 7 Jan 2026 09:00:42 -0600 Subject: [PATCH 272/517] update frozen libs --- frozen/Adafruit_CircuitPython_Display_Text | 2 +- frozen/Adafruit_CircuitPython_LIS3DH | 2 +- frozen/Adafruit_CircuitPython_LSM6DS | 2 +- frozen/Adafruit_CircuitPython_PortalBase | 2 +- frozen/Adafruit_CircuitPython_RFM69 | 2 +- frozen/Adafruit_CircuitPython_RFM9x | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index 0c4ea1b8c102c..372cbe27459c0 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit 0c4ea1b8c102cdd98c91914dd09f067ea6f7b1e1 +Subproject commit 372cbe27459c02db838fb65489cf8b4b5dba12f3 diff --git a/frozen/Adafruit_CircuitPython_LIS3DH b/frozen/Adafruit_CircuitPython_LIS3DH index a3c33eff7c03e..83333f8f6a550 160000 --- a/frozen/Adafruit_CircuitPython_LIS3DH +++ b/frozen/Adafruit_CircuitPython_LIS3DH @@ -1 +1 @@ -Subproject commit a3c33eff7c03e7c1f1e896a08f4878f5db6a6cbf +Subproject commit 83333f8f6a5501f9cbee215ebc791cd2fb67bd7d diff --git a/frozen/Adafruit_CircuitPython_LSM6DS b/frozen/Adafruit_CircuitPython_LSM6DS index 2bf5c7fcadbb0..36247d1d9d769 160000 --- a/frozen/Adafruit_CircuitPython_LSM6DS +++ b/frozen/Adafruit_CircuitPython_LSM6DS @@ -1 +1 @@ -Subproject commit 2bf5c7fcadbb0034c0d193c5c45409f435e7329c +Subproject commit 36247d1d9d769a23f40ca2c4371a5cd64e2a7204 diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase index 25fc43dd67ae9..059c805bd1f91 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit 25fc43dd67ae95a8e62173e90c3069502194873a +Subproject commit 059c805bd1f9157495d17739c8082f9a2042bdbf diff --git a/frozen/Adafruit_CircuitPython_RFM69 b/frozen/Adafruit_CircuitPython_RFM69 index 62cb5cf6fbeb9..763992bb08434 160000 --- a/frozen/Adafruit_CircuitPython_RFM69 +++ b/frozen/Adafruit_CircuitPython_RFM69 @@ -1 +1 @@ -Subproject commit 62cb5cf6fbeb9943bf7b2db4fc614f9b40830bf3 +Subproject commit 763992bb084343ee1a7cfce72585e028ced0d890 diff --git a/frozen/Adafruit_CircuitPython_RFM9x b/frozen/Adafruit_CircuitPython_RFM9x index 5b8a9ae0ace6b..ad05a38fc96ed 160000 --- a/frozen/Adafruit_CircuitPython_RFM9x +++ b/frozen/Adafruit_CircuitPython_RFM9x @@ -1 +1 @@ -Subproject commit 5b8a9ae0ace6bd5f3a0860c95c3c389a09e4b59b +Subproject commit ad05a38fc96edcae5d0ac9d107c268dd76e4a186 From 4fe2f03a865cbf5cd020596f4117f793ded9e329 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 12 Jan 2026 16:48:25 -0800 Subject: [PATCH 273/517] Add support for Yoto Mini and Yoto Player V3 There were a bunch of changes to enable the boards: * Add web workflow for sdioio cards. Adds the "native" access. * Introduce `i2cioexpander` for native I2C expander support. * Introduce DigitalInOut protocol so that IOPins can be used where DigitalInOut would be traditionally. * Add mp_errno_t for clarifying what APIs return errno. * DigitalInOut protocol uses it to pass through I2C issues. * Switch `bitbangio` and `fourwire` to DigitalInOutProtocol. * Support the Mini's GC9306 display with CS and DC over IOPin. * DigitalInOutProtocol can fall back to Python-level access if not natively supported. * Make board.I2C() speed configurable to speed up IOExpander access. * Update codespell to work with Python 3.14. * Make static web workflow responses cachable by the browser for 1 day. --- .pre-commit-config.yaml | 2 +- extmod/vfs_blockdev.c | 11 +- locale/circuitpython.pot | 36 +- ports/analog/common-hal/busio/I2C.c | 12 +- ports/atmel-samd/common-hal/busio/I2C.c | 18 +- ports/atmel-samd/common-hal/sdioio/SDCard.c | 69 +++- ports/broadcom/common-hal/busio/I2C.c | 16 +- ports/broadcom/common-hal/sdioio/SDCard.c | 76 +++- ports/cxd56/common-hal/busio/I2C.c | 10 +- ports/cxd56/common-hal/sdioio/SDCard.c | 60 ++- ports/espressif/boards/yoto_mini_2024/board.c | 203 +++++++++ ports/espressif/boards/yoto_mini_2024/board.h | 3 + .../boards/yoto_mini_2024/mpconfigboard.h | 26 ++ .../boards/yoto_mini_2024/mpconfigboard.mk | 14 + ports/espressif/boards/yoto_mini_2024/pins.c | 79 ++++ .../espressif/boards/yoto_mini_2024/sdkconfig | 0 ports/espressif/boards/yoto_player_v3/board.c | 148 +++++++ ports/espressif/boards/yoto_player_v3/board.h | 3 + .../boards/yoto_player_v3/mpconfigboard.h | 26 ++ .../boards/yoto_player_v3/mpconfigboard.mk | 14 + ports/espressif/boards/yoto_player_v3/pins.c | 104 +++++ .../espressif/boards/yoto_player_v3/sdkconfig | 0 ports/espressif/common-hal/busio/I2C.c | 14 +- ports/espressif/common-hal/sdioio/SDCard.c | 66 ++- ports/espressif/mpconfigport.mk | 3 - ports/mimxrt10xx/common-hal/busio/I2C.c | 14 +- ports/nordic/common-hal/busio/I2C.c | 22 +- ports/raspberrypi/common-hal/busio/I2C.c | 24 +- ports/raspberrypi/mpconfigport.mk | 1 + ports/renode/common-hal/busio/I2C.c | 10 +- ports/silabs/common-hal/busio/I2C.c | 12 +- ports/stm/common-hal/busio/I2C.c | 14 +- ports/stm/common-hal/sdioio/SDCard.c | 63 ++- ports/zephyr-cp/common-hal/busio/I2C.c | 36 +- py/circuitpy_defns.mk | 7 + py/circuitpy_mpconfig.h | 3 + py/circuitpy_mpconfig.mk | 3 + py/mperrno.h | 2 + shared-bindings/bitbangio/I2C.c | 13 +- shared-bindings/bitbangio/I2C.h | 4 +- shared-bindings/bitbangio/SPI.c | 18 +- shared-bindings/bitbangio/SPI.h | 3 +- shared-bindings/board/__init__.h | 6 +- shared-bindings/busio/I2C.c | 12 +- shared-bindings/busio/I2C.h | 11 +- shared-bindings/digitalio/DigitalInOut.c | 77 +++- shared-bindings/digitalio/DigitalInOut.h | 3 + .../digitalio/DigitalInOutProtocol.c | 386 ++++++++++++++++++ .../digitalio/DigitalInOutProtocol.h | 67 +++ shared-bindings/fourwire/FourWire.c | 12 +- shared-bindings/fourwire/FourWire.h | 4 +- shared-bindings/i2cioexpander/IOExpander.c | 247 +++++++++++ shared-bindings/i2cioexpander/IOExpander.h | 34 ++ shared-bindings/i2cioexpander/IOPin.c | 331 +++++++++++++++ shared-bindings/i2cioexpander/IOPin.h | 46 +++ shared-bindings/i2cioexpander/__init__.c | 49 +++ shared-bindings/i2cioexpander/__init__.h | 9 + shared-bindings/sdcardio/SDCard.h | 7 +- shared-bindings/sdioio/SDCard.h | 12 +- shared-bindings/socketpool/Socket.h | 1 + shared-module/bitbangio/I2C.c | 145 ++++--- shared-module/bitbangio/I2C.h | 6 +- shared-module/bitbangio/SPI.c | 161 +++++--- shared-module/bitbangio/SPI.h | 9 +- shared-module/board/__init__.c | 4 + shared-module/busdisplay/BusDisplay.c | 2 +- shared-module/fourwire/FourWire.c | 90 ++-- shared-module/fourwire/FourWire.h | 11 +- shared-module/i2cioexpander/IOExpander.c | 196 +++++++++ shared-module/i2cioexpander/IOExpander.h | 41 ++ shared-module/i2cioexpander/IOPin.c | 147 +++++++ shared-module/i2cioexpander/IOPin.h | 26 ++ shared-module/i2cioexpander/__init__.c | 7 + supervisor/port.h | 5 + supervisor/shared/port.c | 17 + supervisor/shared/status_leds.c | 6 +- supervisor/shared/web_workflow/web_workflow.c | 4 + 77 files changed, 3083 insertions(+), 370 deletions(-) create mode 100644 ports/espressif/boards/yoto_mini_2024/board.c create mode 100644 ports/espressif/boards/yoto_mini_2024/board.h create mode 100644 ports/espressif/boards/yoto_mini_2024/mpconfigboard.h create mode 100644 ports/espressif/boards/yoto_mini_2024/mpconfigboard.mk create mode 100644 ports/espressif/boards/yoto_mini_2024/pins.c create mode 100644 ports/espressif/boards/yoto_mini_2024/sdkconfig create mode 100644 ports/espressif/boards/yoto_player_v3/board.c create mode 100644 ports/espressif/boards/yoto_player_v3/board.h create mode 100644 ports/espressif/boards/yoto_player_v3/mpconfigboard.h create mode 100644 ports/espressif/boards/yoto_player_v3/mpconfigboard.mk create mode 100644 ports/espressif/boards/yoto_player_v3/pins.c create mode 100644 ports/espressif/boards/yoto_player_v3/sdkconfig create mode 100644 shared-bindings/digitalio/DigitalInOutProtocol.c create mode 100644 shared-bindings/digitalio/DigitalInOutProtocol.h create mode 100644 shared-bindings/i2cioexpander/IOExpander.c create mode 100644 shared-bindings/i2cioexpander/IOExpander.h create mode 100644 shared-bindings/i2cioexpander/IOPin.c create mode 100644 shared-bindings/i2cioexpander/IOPin.h create mode 100644 shared-bindings/i2cioexpander/__init__.c create mode 100644 shared-bindings/i2cioexpander/__init__.h create mode 100644 shared-module/i2cioexpander/IOExpander.c create mode 100644 shared-module/i2cioexpander/IOExpander.h create mode 100644 shared-module/i2cioexpander/IOPin.c create mode 100644 shared-module/i2cioexpander/IOPin.h create mode 100644 shared-module/i2cioexpander/__init__.c diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f7e7956498d68..4db04e1d0eddb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,7 +28,7 @@ repos: lib/mbedtls_errors/generate_errors.diff ) - repo: https://github.com/codespell-project/codespell - rev: v2.2.4 + rev: v2.4.1 hooks: - id: codespell args: [-w] diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index f1187b9bbf2f5..0cd3cc6f0c631 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -63,7 +63,16 @@ void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev) { #endif #if CIRCUITPY_SDIOIO if (mp_obj_get_type(bdev) == &sdioio_SDCard_type) { - // TODO: Enable native blockdev for SDIO too. + self->flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL; + self->readblocks[0] = mp_const_none; + self->readblocks[1] = bdev; + self->readblocks[2] = (mp_obj_t)sdioio_sdcard_readblocks; // native version + self->writeblocks[0] = mp_const_none; + self->writeblocks[1] = bdev; + self->writeblocks[2] = (mp_obj_t)sdioio_sdcard_writeblocks; // native version + self->u.ioctl[0] = mp_const_none; + self->u.ioctl[1] = bdev; + self->u.ioctl[2] = (mp_obj_t)sdioio_sdcard_ioctl; // native version } #endif if (self->u.ioctl[0] != MP_OBJ_NULL) { diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index bcc7d1dd1ca9d..9cf51a0c0251f 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -119,7 +119,8 @@ msgstr "" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "" @@ -132,7 +133,7 @@ msgid "%q indices must be integers, not %s" msgstr "" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c msgid "%q init failed" msgstr "" @@ -235,6 +236,14 @@ msgstr "" msgid "%q must be power of 2" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "" @@ -766,6 +775,10 @@ msgstr "" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -800,6 +813,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "" @@ -939,6 +953,7 @@ msgid "Done" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "" @@ -1277,8 +1292,8 @@ msgstr "" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" @@ -1880,6 +1895,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -2539,6 +2555,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -3814,6 +3834,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3827,6 +3851,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index 336c04fe039f9..b74c921bae3ac 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -187,7 +187,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } // Write data to the device selected by address -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { int ret; @@ -202,14 +202,14 @@ uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, }; ret = MXC_I2C_MasterTransaction(&wr_req); if (ret) { - return MP_EIO; + return -MP_EIO; } return 0; } // Read into buffer from the device selected by address -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { @@ -226,14 +226,14 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, ret = MXC_I2C_MasterTransaction(&rd_req); if (ret) { // Return I/O error - return MP_EIO; + return -MP_EIO; } return 0; } // Write the bytes from out_data to the device selected by address -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { @@ -249,7 +249,7 @@ uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, }; ret = MXC_I2C_MasterTransaction(&wr_rd_req); if (ret) { - return MP_EIO; + return -MP_EIO; } return 0; diff --git a/ports/atmel-samd/common-hal/busio/I2C.c b/ports/atmel-samd/common-hal/busio/I2C.c index da108709feaff..700c3e4aba24b 100644 --- a/ports/atmel-samd/common-hal/busio/I2C.c +++ b/ports/atmel-samd/common-hal/busio/I2C.c @@ -176,7 +176,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { uint16_t attempts = ATTEMPTS; @@ -197,17 +197,17 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, if (status == I2C_OK) { return 0; } else if (status == I2C_ERR_BAD_ADDRESS) { - return MP_ENODEV; + return -MP_ENODEV; } - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { uint16_t attempts = ATTEMPTS; @@ -228,14 +228,14 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, if (status == ERR_NONE) { return 0; } else if (status == I2C_ERR_BAD_ADDRESS) { - return MP_ENODEV; + return -MP_ENODEV; } - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/atmel-samd/common-hal/sdioio/SDCard.c b/ports/atmel-samd/common-hal/sdioio/SDCard.c index 8ff1a391bb265..af3610c89e9a1 100644 --- a/ports/atmel-samd/common-hal/sdioio/SDCard.c +++ b/ports/atmel-samd/common-hal/sdioio/SDCard.c @@ -9,6 +9,7 @@ #include "py/runtime.h" #include "common-hal/microcontroller/Pin.h" +#include "extmod/vfs.h" #include "shared-bindings/sdioio/SDCard.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" @@ -170,43 +171,85 @@ static void debug_print_state(sdioio_sdcard_obj_t *self, const char *what, sd_mm #endif } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { - check_for_deinit(self); - check_whole_block(bufinfo); +mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); self->state_programming = true; - sd_mmc_err_t r = sd_mmc_init_write_blocks(0, start_block, bufinfo->len / 512); + sd_mmc_err_t r = sd_mmc_init_write_blocks(0, start_block, num_blocks); if (r != SD_MMC_OK) { debug_print_state(self, "sd_mmc_init_write_blocks", r); - return -EIO; + return -MP_EIO; } - r = sd_mmc_start_write_blocks(bufinfo->buf, bufinfo->len / 512); + r = sd_mmc_start_write_blocks(buf, num_blocks); if (r != SD_MMC_OK) { debug_print_state(self, "sd_mmc_start_write_blocks", r); - return -EIO; + return -MP_EIO; } - // debug_print_state(self, "after writeblocks OK"); return 0; } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { check_for_deinit(self); check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); - sd_mmc_err_t r = sd_mmc_init_read_blocks(0, start_block, bufinfo->len / 512); + sd_mmc_err_t r = sd_mmc_init_read_blocks(0, start_block, num_blocks); if (r != SD_MMC_OK) { debug_print_state(self, "sd_mmc_init_read_blocks", r); - return -EIO; + return -MP_EIO; } - r = sd_mmc_start_read_blocks(bufinfo->buf, bufinfo->len / 512); + r = sd_mmc_start_read_blocks(buf, num_blocks); if (r != SD_MMC_OK) { debug_print_state(self, "sd_mmc_start_read_blocks", r); - return -EIO; + return -MP_EIO; } sd_mmc_wait_end_of_write_blocks(0); return 0; } +mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { + check_for_deinit(self); + check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } +} + bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { check_for_deinit(self); return true; diff --git a/ports/broadcom/common-hal/busio/I2C.c b/ports/broadcom/common-hal/busio/I2C.c index 6a77cec08af83..cdc986f05ae29 100644 --- a/ports/broadcom/common-hal/busio/I2C.c +++ b/ports/broadcom/common-hal/busio/I2C.c @@ -137,7 +137,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { // Discussion of I2C implementation is here: https://github.com/raspberrypi/linux/issues/254 -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { COMPLETE_MEMORY_READS; self->peripheral->S_b.DONE = true; @@ -182,7 +182,7 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, while (self->peripheral->S_b.ERR == 1) { RUN_BACKGROUND_TASKS; } - return MP_ENODEV; + return -MP_ENODEV; } if (loop_len < len) { @@ -192,19 +192,19 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, return 0; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { COMPLETE_MEMORY_READS; self->peripheral->A_b.ADDR = addr; if (self->finish_write) { self->finish_write = false; if (self->peripheral->S_b.ERR == 1) { - return MP_ENODEV; + return -MP_ENODEV; } self->peripheral->FIFO_b.DATA = self->last_write_data; } else { @@ -236,15 +236,15 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, while (self->peripheral->S_b.ERR == 1) { RUN_BACKGROUND_TASKS; } - return MP_ENODEV; + return -MP_ENODEV; } return 0; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/broadcom/common-hal/sdioio/SDCard.c b/ports/broadcom/common-hal/sdioio/SDCard.c index f87d40cd8dcab..5dc405bdc18e8 100644 --- a/ports/broadcom/common-hal/sdioio/SDCard.c +++ b/ports/broadcom/common-hal/sdioio/SDCard.c @@ -11,6 +11,7 @@ #include "py/runtime.h" #include "common-hal/microcontroller/Pin.h" +#include "extmod/vfs.h" #include "shared-bindings/sdioio/SDCard.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" @@ -304,44 +305,95 @@ static void check_whole_block(mp_buffer_info_t *bufinfo) { } } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +// Native function for VFS blockdev layer +mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!self->init) { - return -EIO; + return -MP_EIO; } - check_whole_block(bufinfo); self->state_programming = true; COMPLETE_MEMORY_READS; - sdmmc_err_t error = sdmmc_write_sectors(&self->card_info, bufinfo->buf, - start_block, bufinfo->len / 512); + sdmmc_err_t error = sdmmc_write_sectors(&self->card_info, buf, + start_block, num_blocks); COMPLETE_MEMORY_READS; if (error != SDMMC_OK) { mp_printf(&mp_plat_print, "write sectors result %d\n", error); - return -EIO; + return -MP_EIO; } return 0; } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (!self->init) { - return -EIO; + return -MP_EIO; } check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (!self->init) { + return -MP_EIO; + } COMPLETE_MEMORY_READS; - sdmmc_err_t error = sdmmc_read_sectors(&self->card_info, bufinfo->buf, - start_block, bufinfo->len / 512); + sdmmc_err_t error = sdmmc_read_sectors(&self->card_info, buf, + start_block, num_blocks); COMPLETE_MEMORY_READS; if (error != SDMMC_OK) { - mp_printf(&mp_plat_print, "read sectors result %d when reading block %d for %d\n", error, start_block, bufinfo->len / 512); - return -EIO; + mp_printf(&mp_plat_print, "read sectors result %d when reading block %d for %d\n", error, start_block, num_blocks); + return -MP_EIO; } return 0; } +mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { + if (!self->init) { + return -MP_EIO; + } + check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } +} + bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { if (!self->init) { return false; diff --git a/ports/cxd56/common-hal/busio/I2C.c b/ports/cxd56/common-hal/busio/I2C.c index fb33c242a0367..c8b32b6a023b0 100644 --- a/ports/cxd56/common-hal/busio/I2C.c +++ b/ports/cxd56/common-hal/busio/I2C.c @@ -91,7 +91,7 @@ bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { return I2C_TRANSFER(self->i2c_dev, &msg, 1) < 0 ? false : true; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len, bool stop) { +static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len, bool stop) { struct i2c_msg_s msg; msg.frequency = self->frequency; @@ -102,12 +102,12 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addre return -I2C_TRANSFER(self->i2c_dev, &msg, 1); } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len) { +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len) { struct i2c_msg_s msg; msg.frequency = self->frequency; @@ -118,9 +118,9 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8 return -I2C_TRANSFER(self->i2c_dev, &msg, 1); } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/cxd56/common-hal/sdioio/SDCard.c b/ports/cxd56/common-hal/sdioio/SDCard.c index aa1d137177066..95dde482dde0c 100644 --- a/ports/cxd56/common-hal/sdioio/SDCard.c +++ b/ports/cxd56/common-hal/sdioio/SDCard.c @@ -8,6 +8,7 @@ #include #include +#include "extmod/vfs.h" #include "py/mperrno.h" #include "py/runtime.h" @@ -95,22 +96,73 @@ static void check_whole_block(mp_buffer_info_t *bufinfo) { } } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +// Native function for VFS blockdev layer +mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + int result = self->inode->u.i_bops->read(self->inode, buf, start_block, num_blocks); + if (result < 0) { + return -MP_EIO; + } + return 0; +} + +mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (common_hal_sdioio_sdcard_deinited(self)) { raise_deinited_error(); } check_whole_block(bufinfo); - return self->inode->u.i_bops->read(self->inode, bufinfo->buf, start_block, bufinfo->len / 512); + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +// Native function for VFS blockdev layer +mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + int result = self->inode->u.i_bops->write(self->inode, buf, start_block, num_blocks); + if (result < 0) { + return -MP_EIO; + } + return 0; +} + +mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (common_hal_sdioio_sdcard_deinited(self)) { raise_deinited_error(); } check_whole_block(bufinfo); - return self->inode->u.i_bops->write(self->inode, bufinfo->buf, start_block, bufinfo->len / 512); + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } } void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self) { diff --git a/ports/espressif/boards/yoto_mini_2024/board.c b/ports/espressif/boards/yoto_mini_2024/board.c new file mode 100644 index 0000000000000..fb6a96a957d06 --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/board.c @@ -0,0 +1,203 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "board.h" + +#include "supervisor/board.h" + +#include "extmod/vfs_fat.h" + +#include "py/mpstate.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/i2cioexpander/IOExpander.h" +#include "shared-bindings/i2cioexpander/IOPin.h" +#include "shared-bindings/sdioio/SDCard.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" + +#include "supervisor/filesystem.h" + +static sdioio_sdcard_obj_t sdmmc; +static mp_vfs_mount_t _sdcard_vfs; +static fs_user_mount_t _sdcard_usermount; +static i2cioexpander_ioexpander_obj_t ioexpander; + +#define DELAY 0x80 + +// This is a GC9306. +uint8_t display_init_sequence[] = { + 0xfe, 0, + 0xef, 0, + + // // sw reset + // 0x01, 0 | DELAY, 150, + // normal display mode on + // 0x13, 0, + // display and color format settings + 0x36, 1, 0x48, // Memory access control. mini does 0x48, not 0, 2, 3, 4 or 6 + 0x3A, 1 | DELAY, 0x55, 10, // COLMOD. mini does 0x06 + 0xa4, 2, 0x44, 0x44, // power control 7 + 0xa5, 2, 0x42, 0x42, + 0xaa, 2, 0x88, 0x88, + 0xae, 1, 0x2b, + 0xe8, 2, 0x22, 0x0b, // frame rate + 0xe3, 2, 0x01, 0x10, + 0xff, 1, 0x61, + 0xac, 1, 0x00, + 0xad, 1, 0x33, + 0xaf, 1, 0x77, + 0xa6, 2, 0x1c, 0x1c, // power control 2 + 0xa7, 2, 0x1c, 0x1c, // power control 3 + 0xa8, 2, 0x10, 0x10, // power control 4 + 0xa9, 2, 0x0d, 0x0d, // power control 5 + 0xf0, 6, 0x02, 0x01, 0x00, 0x00, 0x00, 0x05, // Gamma settings + 0xf1, 6, 0x01, 0x02, 0x00, 0x06, 0x10, 0x0e, + 0xf2, 6, 0x03, 0x11, 0x28, 0x02, 0x00, 0x48, + 0xf3, 6, 0x0c, 0x11, 0x30, 0x00, 0x00, 0x46, + 0xf4, 6, 0x05, 0x1f, 0x1f, 0x36, 0x30, 0x0f, + 0xf5, 6, 0x04, 0x1d, 0x1a, 0x38, 0x3f, 0x0f, // Last gamma setting + 0x35, 1, 0x00, + 0x44, 2, 0x00, 0x0a, // set tear scan line + 0x21, 0, // display inversion on + // sleep out + 0x11, 0 | DELAY, 255, + + // display on + 0x29, 0 | DELAY, 255, +}; + +void board_init(void) { + // Wait for everything to start + mp_hal_delay_ms(300); + + // Initialize the board's peripherals here. + busio_i2c_obj_t *i2c = MP_OBJ_TO_PTR(common_hal_board_create_i2c(0)); + + // Initialize the IOExpander + ioexpander.base.type = &i2cioexpander_ioexpander_type; + common_hal_i2cioexpander_ioexpander_construct( + &ioexpander, + MP_OBJ_FROM_PTR(i2c), + 0x20, // I2C address + 16, // Number of pins + 2, // Output register + 0, // Input register + 6); // Direction register + + board_set(MP_QSTR_IOEXPANDER, MP_OBJ_FROM_PTR(&ioexpander)); + board_set(MP_QSTR_PLUG_STATUS, ioexpander.pins->items[8 + 5]); + board_set(MP_QSTR_CHARGE_STATUS, ioexpander.pins->items[8 + 7]); + board_set(MP_QSTR_POWER_BUTTON, ioexpander.pins->items[8 + 3]); + board_set(MP_QSTR_ENC1_BUTTON, ioexpander.pins->items[5]); + board_set(MP_QSTR_ENC2_BUTTON, ioexpander.pins->items[4]); + board_set(MP_QSTR_HEADPHONE_DETECT, ioexpander.pins->items[8 + 1]); + board_set(MP_QSTR_PACTRL, ioexpander.pins->items[6]); + board_set(MP_QSTR_DISPLAY_CS, ioexpander.pins->items[0]); + board_set(MP_QSTR_DISPLAY_DC, ioexpander.pins->items[1]); + board_set(MP_QSTR_DISPLAY_RESET, ioexpander.pins->items[2]); + + board_set(MP_QSTR_LEVEL_CONVERTER, ioexpander.pins->items[3]); + board_set(MP_QSTR_LEVEL_POWER_ENABLE, ioexpander.pins->items[8 + 4]); + board_set(MP_QSTR_LEVEL_VINHOLD, ioexpander.pins->items[8 + 6]); + + board_set(MP_QSTR_TILT, ioexpander.pins->items[8 + 2]); + + // Only on some variants + board_set(MP_QSTR_RTC_INT, ioexpander.pins->items[7]); + + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[2]), true, DRIVE_MODE_PUSH_PULL); + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[3]), true, DRIVE_MODE_PUSH_PULL); + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[6]), true, DRIVE_MODE_PUSH_PULL); + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[14]), true, DRIVE_MODE_PUSH_PULL); + + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + ioexpander.pins->items[1], // DC + ioexpander.pins->items[0], // CS + ioexpander.pins->items[2], // RST + 25000000, // baudrate + 0, // polarity + 0 // phase + ); + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 240, // width (after rotation) + 240, // height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO26, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); + + digitalinout_result_t sd_enable_res = common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[12]), false, DRIVE_MODE_PUSH_PULL); + if (sd_enable_res != DIGITALINOUT_OK) { + mp_printf(&mp_plat_print, "Failed to initialize IOExpander. Skipping SD card\n"); + return; + } + + sdmmc.base.type = &sdioio_SDCard_type; + const mcu_pin_obj_t *data_pins[4] = {MP_ROM_PTR(&pin_GPIO2), MP_ROM_PTR(&pin_GPIO4), MP_ROM_PTR(&pin_GPIO12), MP_ROM_PTR(&pin_GPIO13)}; + common_hal_sdioio_sdcard_construct(&sdmmc, MP_ROM_PTR(&pin_GPIO14), MP_ROM_PTR(&pin_GPIO15), 4, data_pins, 25 * 1000000); + + fs_user_mount_t *vfs = &_sdcard_usermount; + vfs->base.type = &mp_fat_vfs_type; + vfs->fatfs.drv = vfs; + + // Initialise underlying block device + vfs->blockdev.block_size = FF_MIN_SS; // default, will be populated by call to MP_BLOCKDEV_IOCTL_BLOCK_SIZE + mp_vfs_blockdev_init(&vfs->blockdev, &sdmmc); + + // mount the block device so the VFS methods can be used + FRESULT res = f_mount(&vfs->fatfs); + if (res != FR_OK) { + common_hal_sdioio_sdcard_deinit(&sdmmc); + return; + } + common_hal_sdioio_sdcard_never_reset(&sdmmc); + + filesystem_set_concurrent_write_protection(vfs, true); + filesystem_set_writable_by_usb(vfs, false); + + mp_vfs_mount_t *sdcard_vfs = &_sdcard_vfs; + sdcard_vfs->str = "/sd"; + sdcard_vfs->len = 3; + sdcard_vfs->obj = MP_OBJ_FROM_PTR(&_sdcard_usermount); + sdcard_vfs->next = MP_STATE_VM(vfs_mount_table); + MP_STATE_VM(vfs_mount_table) = sdcard_vfs; +} diff --git a/ports/espressif/boards/yoto_mini_2024/board.h b/ports/espressif/boards/yoto_mini_2024/board.h new file mode 100644 index 0000000000000..1e40b62b047db --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/board.h @@ -0,0 +1,3 @@ +#include "py/obj.h" + +extern void board_set(qstr key, mp_obj_t value); diff --git a/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h new file mode 100644 index 0000000000000..b5ab8a7e6c2af --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h @@ -0,0 +1,26 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Yoto Mini 2024" +#define MICROPY_HW_MCU_NAME "ESP32" + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO25, .sda = &pin_GPIO21}} +#define CIRCUITPY_BOARD_I2C_SPEED (400000) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO23, .mosi = &pin_GPIO22, .miso = NULL}} + +#define CIRCUITPY_BOARD_UART (1) +#define CIRCUITPY_BOARD_UART_PIN {{.tx = &pin_GPIO17, .rx = &pin_GPIO16}} + +// UART pins attached to the USB-serial converter chip +#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1) +#define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO3) diff --git a/ports/espressif/boards/yoto_mini_2024/mpconfigboard.mk b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.mk new file mode 100644 index 0000000000000..356e7015a04fa --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.mk @@ -0,0 +1,14 @@ +CIRCUITPY_CREATOR_ID = 0x40100000 +CIRCUITPY_CREATION_ID = 0x00320002 + +IDF_TARGET = esp32 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 40m + +CIRCUITPY_I2CIOEXPANDER = 1 diff --git a/ports/espressif/boards/yoto_mini_2024/pins.c b/ports/espressif/boards/yoto_mini_2024/pins.c new file mode 100644 index 0000000000000..d4ff3b4863fb5 --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/pins.c @@ -0,0 +1,79 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT +// + +#include "board.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static mp_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_MUTABLE_BOARD_DICT_STANDARD_ITEMS + + // External pins are in silkscreen order, from top to bottom, left side, then right side + { MP_ROM_QSTR(MP_QSTR_ENC1A), MP_OBJ_FROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_ENC1B), MP_OBJ_FROM_PTR(&pin_GPIO35) }, + + { MP_ROM_QSTR(MP_QSTR_ENC2A), MP_OBJ_FROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_ENC2B), MP_OBJ_FROM_PTR(&pin_GPIO27) }, + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_OBJ_FROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_OBJ_FROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY_SCK), MP_OBJ_FROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_MOSI), MP_OBJ_FROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_NFC_IN), MP_OBJ_FROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_OBJ_FROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_NFC_OUT), MP_OBJ_FROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_OBJ_FROM_PTR(&pin_GPIO33) }, + + { MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_OBJ_FROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_OBJ_FROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_I2S_BCLK), MP_OBJ_FROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_OBJ_FROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCLK), MP_OBJ_FROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DOUT), MP_OBJ_FROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER_INT), MP_OBJ_FROM_PTR(&pin_GPIO34) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_OBJ_FROM_PTR(&displays[0].display)}, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_OBJ_FROM_PTR(&board_i2c_obj) }, + + // Filled in by board_init() + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_PLUG_STATUS), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_CHARGE_STATUS), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_POWER_BUTTON), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_ENC1_BUTTON), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_ENC2_BUTTON), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_HEADPHONE_DETECT), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_PACTRL), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_DC), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_RESET), mp_const_none }, + + { MP_ROM_QSTR(MP_QSTR_LEVEL_CONVERTER), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_LEVEL_POWER_ENABLE), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_LEVEL_VINHOLD), mp_const_none }, + + { MP_ROM_QSTR(MP_QSTR_TILT), mp_const_none }, + + // Only on some variants + { MP_ROM_QSTR(MP_QSTR_RTC_INT), mp_const_none }, + +}; +MP_DEFINE_MUTABLE_DICT(board_module_globals, board_module_globals_table); + +void board_set(qstr q, mp_obj_t value) { + mp_obj_t key = MP_OBJ_NEW_QSTR(q); + for (size_t i = 0; i < MP_ARRAY_SIZE(board_module_globals_table); i++) { + if (board_module_globals_table[i].key == key) { + board_module_globals_table[i].value = value; + return; + } + } +} diff --git a/ports/espressif/boards/yoto_mini_2024/sdkconfig b/ports/espressif/boards/yoto_mini_2024/sdkconfig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/boards/yoto_player_v3/board.c b/ports/espressif/boards/yoto_player_v3/board.c new file mode 100644 index 0000000000000..fd6ee40b9f32e --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/board.c @@ -0,0 +1,148 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "board.h" + +#include "supervisor/board.h" + +#include "extmod/vfs_fat.h" + +#include "py/mpstate.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/i2cioexpander/IOExpander.h" +#include "shared-bindings/i2cioexpander/IOPin.h" +#include "shared-bindings/sdioio/SDCard.h" + +#include "supervisor/filesystem.h" + +static sdioio_sdcard_obj_t sdmmc; +static mp_vfs_mount_t _sdcard_vfs; +static fs_user_mount_t _sdcard_usermount; +static i2cioexpander_ioexpander_obj_t ioexpander0; // First chip (p0/p1) +static i2cioexpander_ioexpander_obj_t ioexpander1; // Second chip (p2/p3) + +void board_init(void) { + // Wait for everything to start + mp_hal_delay_ms(300); + + // Initialize the board's peripherals here. + busio_i2c_obj_t *i2c = MP_OBJ_TO_PTR(common_hal_board_create_i2c(0)); + + // Initialize the IOExpanders + // V3/V3-E uses two PI4IOE5V6416 chips (16 pins each) + // First chip (0x20): p0 (pins 0-7), p1 (pins 8-15) + ioexpander0.base.type = &i2cioexpander_ioexpander_type; + common_hal_i2cioexpander_ioexpander_construct( + &ioexpander0, + MP_OBJ_FROM_PTR(i2c), + 0x20, // I2C address for first chip + 16, // Number of pins + 2, // Output register + 0, // Input register + 6); // Direction register + + // Second chip (0x21): p2 (pins 0-7), p3 (pins 8-15) + ioexpander1.base.type = &i2cioexpander_ioexpander_type; + common_hal_i2cioexpander_ioexpander_construct( + &ioexpander1, + MP_OBJ_FROM_PTR(i2c), + 0x21, // I2C address for second chip + 16, // Number of pins + 2, // Output register + 0, // Input register + 6); // Direction register + + board_set(MP_QSTR_IOEXPANDER0, MP_OBJ_FROM_PTR(&ioexpander0)); + board_set(MP_QSTR_IOEXPANDER1, MP_OBJ_FROM_PTR(&ioexpander1)); + + // Battery and charging (from first chip - p0/p1) + board_set(MP_QSTR_BATTERY_ALERT, ioexpander0.pins->items[6]); // IOX.0.6 + board_set(MP_QSTR_QI_STATUS, ioexpander0.pins->items[7]); // IOX.0.7 + board_set(MP_QSTR_QI_I2C_INT, ioexpander0.pins->items[0]); // IOX.0.0 + board_set(MP_QSTR_USB_STATUS, ioexpander0.pins->items[8 + 0]); // IOX.1.0 + board_set(MP_QSTR_CHARGE_STATUS, ioexpander0.pins->items[8 + 4]); // IOX.1.4 + + // Buttons (from first chip - p0/p1) + board_set(MP_QSTR_POWER_BUTTON, ioexpander0.pins->items[8 + 3]); // IOX.1.3 + board_set(MP_QSTR_ENC1_BUTTON, ioexpander0.pins->items[5]); // IOX.0.5 + board_set(MP_QSTR_ENC2_BUTTON, ioexpander0.pins->items[4]); // IOX.0.4 + + // Audio (from first chip - p0/p1 and second chip - p2/p3) + board_set(MP_QSTR_HEADPHONE_DETECT, ioexpander0.pins->items[8 + 1]); // IOX.1.1 + board_set(MP_QSTR_PACTRL, ioexpander1.pins->items[4]); // IOX.2.4 + + // Display - V3/V3-E uses ht16d35x with 4 CS lines (from second chip - p2) + board_set(MP_QSTR_DISPLAY_CS0, ioexpander1.pins->items[0]); // IOX.2.0 + board_set(MP_QSTR_DISPLAY_CS1, ioexpander1.pins->items[1]); // IOX.2.1 + board_set(MP_QSTR_DISPLAY_CS2, ioexpander1.pins->items[2]); // IOX.2.2 + board_set(MP_QSTR_DISPLAY_CS3, ioexpander1.pins->items[3]); // IOX.2.3 + + // Power control (from second chip - p2/p3) + board_set(MP_QSTR_LEVEL_CONVERTER, ioexpander1.pins->items[8 + 0]); // IOX.3.0 + board_set(MP_QSTR_LEVEL_POWER_ENABLE, ioexpander1.pins->items[5]); // IOX.2.5 + board_set(MP_QSTR_LEVEL_VINHOLD, ioexpander1.pins->items[8 + 1]); // IOX.3.1 + board_set(MP_QSTR_LEVEL_VOUTEN, ioexpander1.pins->items[8 + 3]); // IOX.3.3 + + // Sensors (from first chip - p0/p1) + board_set(MP_QSTR_TILT, ioexpander0.pins->items[8 + 2]); // IOX.1.2 + board_set(MP_QSTR_RTC_INT, ioexpander0.pins->items[1]); // IOX.0.1 + + // Qi charging control (V3-E, from second chip - p2/p3) + board_set(MP_QSTR_QI_CHARGE_ENABLE, ioexpander1.pins->items[6]); // IOX.2.6 + board_set(MP_QSTR_USB_CHARGE_ENABLE, ioexpander1.pins->items[7]); // IOX.2.7 + board_set(MP_QSTR_QI_ENABLE_5W, ioexpander1.pins->items[8 + 5]); // IOX.3.5 + + // Output pin 3 high. Not clear why. + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander0.pins->items[3]), true, DRIVE_MODE_PUSH_PULL); + + // Initialize output pins + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[0]), true, DRIVE_MODE_PUSH_PULL); // DISPLAY_CS0 (IOX.2.0) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[1]), true, DRIVE_MODE_PUSH_PULL); // DISPLAY_CS1 (IOX.2.1) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[2]), true, DRIVE_MODE_PUSH_PULL); // DISPLAY_CS2 (IOX.2.2) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[3]), true, DRIVE_MODE_PUSH_PULL); // DISPLAY_CS3 (IOX.2.3) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[4]), true, DRIVE_MODE_PUSH_PULL); // PACTRL (IOX.2.4) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[5]), false, DRIVE_MODE_PUSH_PULL); // LEVEL_POWER_ENABLE (IOX.2.5) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[6]), true, DRIVE_MODE_PUSH_PULL); // QI_CHARGE_ENABLE + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[7]), false, DRIVE_MODE_PUSH_PULL); // USB_CHARGE_ENABLE + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[8 + 0]), true, DRIVE_MODE_PUSH_PULL); // LEVEL_CONVERTER (IOX.3.0) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[8 + 1]), true, DRIVE_MODE_PUSH_PULL); // VINHOLD (IOX.3.1) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[8 + 3]), true, DRIVE_MODE_PUSH_PULL); // VOUTEN (IOX.3.3) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[8 + 5]), false, DRIVE_MODE_PUSH_PULL); // QI_ENABLE_5W (IOX.3.5) + + // Initialize SD card + // V3/V3-E uses single-line SD (sd1 mode), not 4-line (sd4 mode) like Mini + sdmmc.base.type = &sdioio_SDCard_type; + const mcu_pin_obj_t *data_pins[1] = {MP_ROM_PTR(&pin_GPIO2)}; + common_hal_sdioio_sdcard_construct(&sdmmc, MP_ROM_PTR(&pin_GPIO14), MP_ROM_PTR(&pin_GPIO15), 1, data_pins, 25 * 1000000); + + fs_user_mount_t *vfs = &_sdcard_usermount; + vfs->base.type = &mp_fat_vfs_type; + vfs->fatfs.drv = vfs; + + // Initialise underlying block device + vfs->blockdev.block_size = FF_MIN_SS; // default, will be populated by call to MP_BLOCKDEV_IOCTL_BLOCK_SIZE + mp_vfs_blockdev_init(&vfs->blockdev, &sdmmc); + + // mount the block device so the VFS methods can be used + FRESULT res = f_mount(&vfs->fatfs); + if (res != FR_OK) { + common_hal_sdioio_sdcard_deinit(&sdmmc); + return; + } + common_hal_sdioio_sdcard_never_reset(&sdmmc); + + filesystem_set_concurrent_write_protection(vfs, true); + filesystem_set_writable_by_usb(vfs, false); + + mp_vfs_mount_t *sdcard_vfs = &_sdcard_vfs; + sdcard_vfs->str = "/sd"; + sdcard_vfs->len = 3; + sdcard_vfs->obj = MP_OBJ_FROM_PTR(&_sdcard_usermount); + sdcard_vfs->next = MP_STATE_VM(vfs_mount_table); + MP_STATE_VM(vfs_mount_table) = sdcard_vfs; +} diff --git a/ports/espressif/boards/yoto_player_v3/board.h b/ports/espressif/boards/yoto_player_v3/board.h new file mode 100644 index 0000000000000..1e40b62b047db --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/board.h @@ -0,0 +1,3 @@ +#include "py/obj.h" + +extern void board_set(qstr key, mp_obj_t value); diff --git a/ports/espressif/boards/yoto_player_v3/mpconfigboard.h b/ports/espressif/boards/yoto_player_v3/mpconfigboard.h new file mode 100644 index 0000000000000..ba53649275cd2 --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/mpconfigboard.h @@ -0,0 +1,26 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Yoto Player V3" +#define MICROPY_HW_MCU_NAME "ESP32" + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO25, .sda = &pin_GPIO21}} +#define CIRCUITPY_BOARD_I2C_SPEED (400000) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO23, .mosi = &pin_GPIO22, .miso = &pin_GPIO26}} + +#define CIRCUITPY_BOARD_UART (1) +#define CIRCUITPY_BOARD_UART_PIN {{.tx = &pin_GPIO17, .rx = &pin_GPIO16}} + +// UART pins attached to the USB-serial converter chip +#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1) +#define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO3) diff --git a/ports/espressif/boards/yoto_player_v3/mpconfigboard.mk b/ports/espressif/boards/yoto_player_v3/mpconfigboard.mk new file mode 100644 index 0000000000000..a1b940316c40a --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/mpconfigboard.mk @@ -0,0 +1,14 @@ +CIRCUITPY_CREATOR_ID = 0x40100000 +CIRCUITPY_CREATION_ID = 0x00320001 + +IDF_TARGET = esp32 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 40m + +CIRCUITPY_I2CIOEXPANDER = 1 diff --git a/ports/espressif/boards/yoto_player_v3/pins.c b/ports/espressif/boards/yoto_player_v3/pins.c new file mode 100644 index 0000000000000..0862a020fdfce --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/pins.c @@ -0,0 +1,104 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT +// + +#include "board.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static mp_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_MUTABLE_BOARD_DICT_STANDARD_ITEMS + + // External pins are in silkscreen order, from top to bottom, left side, then right side + // Encoder 1: V3-E uses GPIO26/13, V3 uses GPIO35/13 + { MP_ROM_QSTR(MP_QSTR_ENC1A), MP_OBJ_FROM_PTR(&pin_GPIO26) }, // V3-E: GPIO26, V3: GPIO35 + { MP_ROM_QSTR(MP_QSTR_ENC1A_V3), MP_OBJ_FROM_PTR(&pin_GPIO35) }, // V3-E: GPIO26, V3: GPIO35 + { MP_ROM_QSTR(MP_QSTR_ENC1B), MP_OBJ_FROM_PTR(&pin_GPIO13) }, + + // Encoder 2: Both V3 and V3-E use GPIO27/4 + { MP_ROM_QSTR(MP_QSTR_ENC2A), MP_OBJ_FROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_ENC2B), MP_OBJ_FROM_PTR(&pin_GPIO4) }, + + // Light sensor (V3/V3-E) + { MP_ROM_QSTR(MP_QSTR_LIGHT_SENSOR), MP_OBJ_FROM_PTR(&pin_GPIO36) }, + + // Temperature sensors (V3/V3-E) + { MP_ROM_QSTR(MP_QSTR_TEMP_SENSOR), MP_OBJ_FROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_QI_RX_TEMP_SENSOR), MP_OBJ_FROM_PTR(&pin_GPIO35) }, // V3-E only + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_OBJ_FROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_OBJ_FROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY_SCK), MP_OBJ_FROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_OBJ_FROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_MOSI), MP_OBJ_FROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_OBJ_FROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_MISO), MP_OBJ_FROM_PTR(&pin_GPIO26) }, // V3/V3-E have MISO + { MP_ROM_QSTR(MP_QSTR_MISO), MP_OBJ_FROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_NFC_IN), MP_OBJ_FROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_OBJ_FROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_NFC_OUT), MP_OBJ_FROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_OBJ_FROM_PTR(&pin_GPIO33) }, + + { MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_OBJ_FROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_OBJ_FROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_I2S_BCLK), MP_OBJ_FROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_OBJ_FROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCLK), MP_OBJ_FROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DOUT), MP_OBJ_FROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER_INT), MP_OBJ_FROM_PTR(&pin_GPIO34) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_OBJ_FROM_PTR(&board_i2c_obj) }, + + // Filled in by board_init() + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER0), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER1), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_BATTERY_ALERT), mp_const_none }, // IOX.0.6 + { MP_ROM_QSTR(MP_QSTR_QI_STATUS), mp_const_none }, // IOX.0.7 + { MP_ROM_QSTR(MP_QSTR_USB_STATUS), mp_const_none }, // IOX.1.0 + { MP_ROM_QSTR(MP_QSTR_CHARGE_STATUS), mp_const_none }, // IOX.1.4 + { MP_ROM_QSTR(MP_QSTR_POWER_BUTTON), mp_const_none }, // IOX.1.3 + { MP_ROM_QSTR(MP_QSTR_ENC1_BUTTON), mp_const_none }, // IOX.0.5 + { MP_ROM_QSTR(MP_QSTR_ENC2_BUTTON), mp_const_none }, // IOX.0.4 + { MP_ROM_QSTR(MP_QSTR_HEADPHONE_DETECT), mp_const_none }, // IOX.1.1 + { MP_ROM_QSTR(MP_QSTR_PACTRL), mp_const_none }, // IOX.2.4 + // V3/V3-E use ht16d35x display with 4 CS lines + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS0), mp_const_none }, // IOX.2.0 + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS1), mp_const_none }, // IOX.2.1 + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS2), mp_const_none }, // IOX.2.2 + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS3), mp_const_none }, // IOX.2.3 + + { MP_ROM_QSTR(MP_QSTR_LEVEL_CONVERTER), mp_const_none }, // IOX.3.0 + { MP_ROM_QSTR(MP_QSTR_LEVEL_POWER_ENABLE), mp_const_none }, // IOX.2.5 + { MP_ROM_QSTR(MP_QSTR_LEVEL_VINHOLD), mp_const_none }, // IOX.3.1 + { MP_ROM_QSTR(MP_QSTR_LEVEL_VOUTEN), mp_const_none }, // IOX.3.3 + + { MP_ROM_QSTR(MP_QSTR_TILT), mp_const_none }, // IOX.1.2 + { MP_ROM_QSTR(MP_QSTR_RTC_INT), mp_const_none }, // IOX.0.1 + + // Qi charging pins (V3-E) + { MP_ROM_QSTR(MP_QSTR_QI_CHARGE_ENABLE), mp_const_none }, // IOX.2.6 + { MP_ROM_QSTR(MP_QSTR_QI_ENABLE_5W), mp_const_none }, // IOX.3.5 + { MP_ROM_QSTR(MP_QSTR_QI_I2C_INT), mp_const_none }, // IOX.0.0 + + // USB-C charging pins (V3/V3-E) + { MP_ROM_QSTR(MP_QSTR_USB_CHARGE_ENABLE), mp_const_none }, // IOX.2.7 + +}; +MP_DEFINE_MUTABLE_DICT(board_module_globals, board_module_globals_table); + +void board_set(qstr q, mp_obj_t value) { + mp_obj_t key = MP_OBJ_NEW_QSTR(q); + for (size_t i = 0; i < MP_ARRAY_SIZE(board_module_globals_table); i++) { + if (board_module_globals_table[i].key == key) { + board_module_globals_table[i].value = value; + return; + } + } +} diff --git a/ports/espressif/boards/yoto_player_v3/sdkconfig b/ports/espressif/boards/yoto_player_v3/sdkconfig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/common-hal/busio/I2C.c b/ports/espressif/common-hal/busio/I2C.c index 9ef3877ea92ed..723fa7d740000 100644 --- a/ports/espressif/common-hal/busio/I2C.c +++ b/ports/espressif/common-hal/busio/I2C.c @@ -169,16 +169,16 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t convert_esp_err(esp_err_t result) { +static mp_errno_t convert_esp_err(esp_err_t result) { switch (result) { case ESP_OK: return 0; case ESP_FAIL: - return MP_ENODEV; + return -MP_ENODEV; case ESP_ERR_TIMEOUT: - return MP_ETIMEDOUT; + return -MP_ETIMEDOUT; default: - return MP_EIO; + return -MP_EIO; } } @@ -189,7 +189,7 @@ static size_t _transaction_duration_ms(size_t frequency, size_t len) { return (len + 1) / bytes_per_ms + 1000; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = addr, @@ -202,7 +202,7 @@ uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const u return convert_esp_err(result); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = addr, @@ -215,7 +215,7 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t return convert_esp_err(result); } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, diff --git a/ports/espressif/common-hal/sdioio/SDCard.c b/ports/espressif/common-hal/sdioio/SDCard.c index 87ba32f802095..f774561816258 100644 --- a/ports/espressif/common-hal/sdioio/SDCard.c +++ b/ports/espressif/common-hal/sdioio/SDCard.c @@ -10,6 +10,7 @@ #include "driver/sdmmc_host.h" #include "ports/espressif/esp-idf/components/sdmmc/include/sdmmc_cmd.h" +#include "extmod/vfs.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" #include "shared-bindings/sdioio/SDCard.h" @@ -168,32 +169,73 @@ static void check_whole_block(mp_buffer_info_t *bufinfo, int sector_size) { } } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { - common_hal_sdioio_sdcard_check_for_deinit(self); - check_whole_block(bufinfo, self->card.csd.sector_size); - esp_err_t err; - ESP_LOGI(TAG, "in common_hal_sdioio_sdcard_writeblocks"); - // err = sdmmc_io_write_blocks(&self->card, 1, start_block, bufinfo->buf, bufinfo->len); - err = sdmmc_write_sectors(&self->card, bufinfo->buf, start_block, bufinfo->len / self->card.csd.sector_size); +mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + ESP_LOGI(TAG, "in sdioio_sdcard_writeblocks"); + esp_err_t err = sdmmc_write_sectors(&self->card, buf, start_block, num_blocks); if (err != ESP_OK) { ESP_LOGW(TAG, "Failed to write blocks with err 0x%X", err); + return -MP_EIO; } return 0; } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { common_hal_sdioio_sdcard_check_for_deinit(self); check_whole_block(bufinfo, self->card.csd.sector_size); - esp_err_t err; - ESP_LOGI(TAG, "in common_hal_sdioio_sdcard_readblocks"); - // err = sdmmc_io_read_blocks(&self->card, 1, start_block, bufinfo->buf, bufinfo->len); - err = sdmmc_read_sectors(&self->card, bufinfo->buf, start_block, bufinfo->len / self->card.csd.sector_size); + + uint32_t num_blocks = bufinfo->len / self->card.csd.sector_size; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + ESP_LOGI(TAG, "in sdioio_sdcard_readblocks"); + esp_err_t err = sdmmc_read_sectors(&self->card, buf, start_block, num_blocks); if (err != ESP_OK) { ESP_LOGW(TAG, "Failed to read blocks with err 0x%X", err); + return -MP_EIO; } return 0; } +mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { + common_hal_sdioio_sdcard_check_for_deinit(self); + check_whole_block(bufinfo, self->card.csd.sector_size); + + uint32_t num_blocks = bufinfo->len / self->card.csd.sector_size; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } +} + bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { return true; } diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index a967b8a4f15f5..67f8ca2987bd8 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -103,9 +103,6 @@ ifeq ($(IDF_TARGET),esp32) CIRCUITPY_ALARM_TOUCH = 1 CIRCUITPY_RGBMATRIX = 0 -# SDMMC not supported yet -CIRCUITPY_SDIOIO = 0 - # Has no USB CIRCUITPY_USB_DEVICE = 0 diff --git a/ports/mimxrt10xx/common-hal/busio/I2C.c b/ports/mimxrt10xx/common-hal/busio/I2C.c index 132b3083212a1..38777f2c9038c 100644 --- a/ports/mimxrt10xx/common-hal/busio/I2C.c +++ b/ports/mimxrt10xx/common-hal/busio/I2C.c @@ -201,7 +201,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { lpi2c_master_transfer_t xfer = { 0 }; @@ -215,15 +215,15 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, return 0; } - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { lpi2c_master_transfer_t xfer = { 0 }; @@ -237,12 +237,12 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, return 0; } - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/nordic/common-hal/busio/I2C.c b/ports/nordic/common-hal/busio/I2C.c index 3558fad165ba6..904d35cae3d0b 100644 --- a/ports/nordic/common-hal/busio/I2C.c +++ b/ports/nordic/common-hal/busio/I2C.c @@ -47,18 +47,18 @@ void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { never_reset_pin_number(self->sda_pin_number); } -static uint8_t twi_error_to_mp(const nrfx_err_t err) { +static mp_errno_t twi_error_to_mp(const nrfx_err_t err) { switch (err) { case NRFX_ERROR_DRV_TWI_ERR_ANACK: - return MP_ENODEV; + return -MP_ENODEV; case NRFX_ERROR_BUSY: - return MP_EBUSY; + return -MP_EBUSY; case NRFX_ERROR_INVALID_ADDR: case NRFX_ERROR_DRV_TWI_ERR_DNACK: case NRFX_ERROR_DRV_TWI_ERR_OVERRUN: - return MP_EIO; + return -MP_EIO; case NRFX_ERROR_TIMEOUT: - return MP_ETIMEDOUT; + return -MP_ETIMEDOUT; default: break; } @@ -258,9 +258,9 @@ static nrfx_err_t _twim_xfer_with_timeout(busio_i2c_obj_t *self, nrfx_twim_xfer_ } } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool stopBit) { +static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool stopBit) { if (len == 0) { - return common_hal_busio_i2c_probe(self, addr) ? 0 : MP_ENODEV; + return common_hal_busio_i2c_probe(self, addr) ? 0 : -MP_ENODEV; } nrfx_err_t err = NRFX_SUCCESS; @@ -286,11 +286,11 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, return twi_error_to_mp(err); } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { if (len == 0) { return 0; } @@ -317,9 +317,9 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t return twi_error_to_mp(err); } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/raspberrypi/common-hal/busio/I2C.c b/ports/raspberrypi/common-hal/busio/I2C.c index 0f7e023f0e9c5..41df0eac72429 100644 --- a/ports/raspberrypi/common-hal/busio/I2C.c +++ b/ports/raspberrypi/common-hal/busio/I2C.c @@ -86,7 +86,7 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, // // Do not use the default supplied clock stretching timeout here. // It is too short for some devices. Use the busio timeout instead. - shared_module_bitbangio_i2c_construct(&self->bitbangio_i2c, scl, sda, + shared_module_bitbangio_i2c_construct(&self->bitbangio_i2c, MP_OBJ_FROM_PTR(scl), MP_OBJ_FROM_PTR(sda), frequency, BUS_TIMEOUT_US); self->baudrate = i2c_init(self->peripheral, frequency); @@ -144,7 +144,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { if (len == 0) { // The RP2040 I2C peripheral will not perform 0 byte writes. @@ -174,20 +174,20 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, } switch (result) { case PICO_ERROR_GENERIC: - return MP_ENODEV; + return -MP_ENODEV; case PICO_ERROR_TIMEOUT: - return MP_ETIMEDOUT; + return -MP_ETIMEDOUT; default: - return MP_EIO; + return -MP_EIO; } } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { size_t result = i2c_read_timeout_us(self->peripheral, addr, data, len, false, BUS_TIMEOUT_US); if (result == len) { @@ -195,17 +195,17 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, } switch (result) { case PICO_ERROR_GENERIC: - return MP_ENODEV; + return -MP_ENODEV; case PICO_ERROR_TIMEOUT: - return MP_ETIMEDOUT; + return -MP_ETIMEDOUT; default: - return MP_EIO; + return -MP_EIO; } } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index bbce13d13811f..8401c5d75453a 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -30,6 +30,7 @@ CIRCUITPY_FREQUENCYIO = 0 # Use PWM internally CIRCUITPY_I2CTARGET = 1 +CIRCUITPY_I2CIOEXPANDER = 1 CIRCUITPY_NVM = 1 # Use PIO internally CIRCUITPY_PULSEIO ?= 1 diff --git a/ports/renode/common-hal/busio/I2C.c b/ports/renode/common-hal/busio/I2C.c index c94f63cb880e3..0b01b6e79369d 100644 --- a/ports/renode/common-hal/busio/I2C.c +++ b/ports/renode/common-hal/busio/I2C.c @@ -44,19 +44,19 @@ bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return 0; } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - return MP_EIO; + return -MP_EIO; } void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { diff --git a/ports/silabs/common-hal/busio/I2C.c b/ports/silabs/common-hal/busio/I2C.c index ae18f561c502f..85f3e63a6dbe6 100644 --- a/ports/silabs/common-hal/busio/I2C.c +++ b/ports/silabs/common-hal/busio/I2C.c @@ -145,7 +145,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } // Write data to the device selected by address -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { I2C_TransferSeq_TypeDef seq; @@ -159,13 +159,13 @@ uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, ret = I2CSPM_Transfer(self->i2cspm, &seq); if (ret != i2cTransferDone) { - return MP_EIO; + return -MP_EIO; } return 0; } // Read into buffer from the device selected by address -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { @@ -180,13 +180,13 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, ret = I2CSPM_Transfer(self->i2cspm, &seq); if (ret != i2cTransferDone) { - return MP_EIO; + return -MP_EIO; } return 0; } // Write the bytes from out_data to the device selected by address, -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { @@ -204,7 +204,7 @@ uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, ret = I2CSPM_Transfer(self->i2cspm, &seq); if (ret != i2cTransferDone) { - return MP_EIO; + return -MP_EIO; } return 0; } diff --git a/ports/stm/common-hal/busio/I2C.c b/ports/stm/common-hal/busio/I2C.c index e6957989cc72c..5277bffe1fcd2 100644 --- a/ports/stm/common-hal/busio/I2C.c +++ b/ports/stm/common-hal/busio/I2C.c @@ -212,7 +212,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { HAL_StatusTypeDef result; if (!transmit_stop_bit) { @@ -234,19 +234,19 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, result = HAL_I2C_Master_Transmit(&(self->handle), (uint16_t)(addr << 1), (uint8_t *)data, (uint16_t)len, 500); } - return result == HAL_OK ? 0 : MP_EIO; + return result == HAL_OK ? 0 : -MP_EIO; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { if (!self->frame_in_prog) { return HAL_I2C_Master_Receive(&(self->handle), (uint16_t)(addr << 1), data, (uint16_t)len, 500) - == HAL_OK ? 0 : MP_EIO; + == HAL_OK ? 0 : -MP_EIO; } else { HAL_StatusTypeDef result = HAL_I2C_Master_Seq_Receive_IT(&(self->handle), (uint16_t)(addr << 1), (uint8_t *)data, @@ -259,9 +259,9 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, } } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/stm/common-hal/sdioio/SDCard.c b/ports/stm/common-hal/sdioio/SDCard.c index 92e552c2e8984..d8cfdb1131885 100644 --- a/ports/stm/common-hal/sdioio/SDCard.c +++ b/ports/stm/common-hal/sdioio/SDCard.c @@ -6,6 +6,8 @@ #include #include "shared-bindings/sdioio/SDCard.h" + +#include "extmod/vfs.h" #include "py/mperrno.h" #include "py/runtime.h" @@ -239,9 +241,10 @@ static void check_for_deinit(sdioio_sdcard_obj_t *self) { } } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { - check_for_deinit(self); - check_whole_block(bufinfo); +// Native function for VFS blockdev layer +mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); self->state_programming = true; common_hal_mcu_disable_interrupts(); @@ -251,17 +254,27 @@ int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t sta #else uint32_t time_out = 1000; #endif - HAL_StatusTypeDef r = HAL_SD_WriteBlocks(&self->handle, bufinfo->buf, start_block, bufinfo->len / 512, time_out); + HAL_StatusTypeDef r = HAL_SD_WriteBlocks(&self->handle, buf, start_block, num_blocks, time_out); common_hal_mcu_enable_interrupts(); if (r != HAL_OK) { - return -EIO; + return -MP_EIO; } return 0; } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { check_for_deinit(self); check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); common_hal_mcu_disable_interrupts(); #ifdef STM32H750xx @@ -270,14 +283,48 @@ int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t star #else uint32_t time_out = 1000; #endif - HAL_StatusTypeDef r = HAL_SD_ReadBlocks(&self->handle, bufinfo->buf, start_block, bufinfo->len / 512, time_out); + HAL_StatusTypeDef r = HAL_SD_ReadBlocks(&self->handle, buf, start_block, num_blocks, time_out); common_hal_mcu_enable_interrupts(); if (r != HAL_OK) { - return -EIO; + return -MP_EIO; } return 0; } +mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { + check_for_deinit(self); + check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } +} + bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { check_for_deinit(self); return true; diff --git a/ports/zephyr-cp/common-hal/busio/I2C.c b/ports/zephyr-cp/common-hal/busio/I2C.c index 49def727a53ca..d26fc2828cdfd 100644 --- a/ports/zephyr-cp/common-hal/busio/I2C.c +++ b/ports/zephyr-cp/common-hal/busio/I2C.c @@ -73,34 +73,34 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { k_mutex_unlock(&self->mutex); } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { if (common_hal_busio_i2c_deinited(self)) { - return MP_EIO; + return -MP_EIO; } int ret = i2c_write(self->i2c_device, data, len, addr); if (ret != 0) { // Map Zephyr error codes to errno if (ret == -ENOTSUP) { - return MP_EOPNOTSUPP; + return -MP_EOPNOTSUPP; } else if (ret == -EIO || ret == -ENXIO) { - return MP_EIO; + return -MP_EIO; } else if (ret == -EBUSY) { - return MP_EBUSY; + return -MP_EBUSY; } - return MP_EIO; + return -MP_EIO; } return 0; } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { if (common_hal_busio_i2c_deinited(self)) { - return MP_EIO; + return -MP_EIO; } if (len == 0) { @@ -111,23 +111,23 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, if (ret != 0) { // Map Zephyr error codes to errno if (ret == -ENOTSUP) { - return MP_EOPNOTSUPP; + return -MP_EOPNOTSUPP; } else if (ret == -EIO || ret == -ENXIO) { - return MP_EIO; + return -MP_EIO; } else if (ret == -EBUSY) { - return MP_EBUSY; + return -MP_EBUSY; } - return MP_EIO; + return -MP_EIO; } return 0; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { if (common_hal_busio_i2c_deinited(self)) { - return MP_EIO; + return -MP_EIO; } // Use i2c_write_read for combined transaction with repeated start @@ -135,13 +135,13 @@ uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, if (ret != 0) { // Map Zephyr error codes to errno if (ret == -ENOTSUP) { - return MP_EOPNOTSUPP; + return -MP_EOPNOTSUPP; } else if (ret == -EIO || ret == -ENXIO) { - return MP_EIO; + return -MP_EIO; } else if (ret == -EBUSY) { - return MP_EBUSY; + return -MP_EBUSY; } - return MP_EIO; + return -MP_EIO; } return 0; diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 56c836cb3a490..5760dbd9eb188 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -255,6 +255,9 @@ endif ifeq ($(CIRCUITPY_I2CTARGET),1) SRC_PATTERNS += i2ctarget/% endif +ifeq ($(CIRCUITPY_I2CIOEXPANDER),1) +SRC_PATTERNS += i2cioexpander/% +endif ifeq ($(CIRCUITPY_IMAGECAPTURE),1) SRC_PATTERNS += imagecapture/% endif @@ -612,6 +615,7 @@ $(filter $(SRC_PATTERNS), \ canio/Match.c \ codeop/__init__.c \ countio/Edge.c \ + digitalio/DigitalInOutProtocol.c \ digitalio/Direction.c \ digitalio/DriveMode.c \ digitalio/Pull.c \ @@ -725,6 +729,9 @@ SRC_SHARED_MODULE_ALL = \ dotclockframebuffer/__init__.c \ epaperdisplay/__init__.c \ epaperdisplay/EPaperDisplay.c \ + i2cioexpander/IOExpander.c \ + i2cioexpander/IOPin.c \ + i2cioexpander/__init__.c \ floppyio/__init__.c \ fontio/BuiltinFont.c \ fontio/__init__.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 386d1ee3d1e12..fb4f261ab6f49 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -321,6 +321,9 @@ typedef long mp_off_t; #if defined(DEFAULT_I2C_BUS_SCL) && defined(DEFAULT_I2C_BUS_SDA) #define CIRCUITPY_BOARD_I2C (1) #define CIRCUITPY_BOARD_I2C_PIN {{.scl = DEFAULT_I2C_BUS_SCL, .sda = DEFAULT_I2C_BUS_SDA}} +#ifndef CIRCUITPY_BOARD_I2C_SPEED +#define CIRCUITPY_BOARD_I2C_SPEED (100000) +#endif #else #define CIRCUITPY_BOARD_I2C (0) #endif diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 9bbe5691dfb3b..e3a3e8fe8008e 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -346,6 +346,9 @@ CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS_ONLY=$(CIRCUITPY_HASHLIB_MBEDTLS_ONLY) CIRCUITPY_I2CTARGET ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_I2CTARGET=$(CIRCUITPY_I2CTARGET) +CIRCUITPY_I2CIOEXPANDER ?= 0 +CFLAGS += -DCIRCUITPY_I2CIOEXPANDER=$(CIRCUITPY_I2CIOEXPANDER) + CIRCUITPY_IMAGECAPTURE ?= 0 CFLAGS += -DCIRCUITPY_IMAGECAPTURE=$(CIRCUITPY_IMAGECAPTURE) diff --git a/py/mperrno.h b/py/mperrno.h index 9e4ecd9419c66..f1fee77b22e2c 100644 --- a/py/mperrno.h +++ b/py/mperrno.h @@ -143,6 +143,8 @@ #endif +typedef int mp_errno_t; + #if MICROPY_PY_ERRNO #include "py/obj.h" diff --git a/shared-bindings/bitbangio/I2C.c b/shared-bindings/bitbangio/I2C.c index d1a200d39b52f..ddd8824eb58ed 100644 --- a/shared-bindings/bitbangio/I2C.c +++ b/shared-bindings/bitbangio/I2C.c @@ -22,8 +22,8 @@ //| //| def __init__( //| self, -//| scl: microcontroller.Pin, -//| sda: microcontroller.Pin, +//| scl: Union[microcontroller.Pin, digitalio.DigitalInOutProtocol], +//| sda: Union[microcontroller.Pin, digitalio.DigitalInOutProtocol], //| *, //| frequency: int = 400000, //| timeout: int = 255, @@ -40,8 +40,8 @@ //| bit unpacking. Instead, use an existing driver or make one with //| :ref:`Register ` data descriptors. //| -//| :param ~microcontroller.Pin scl: The clock pin -//| :param ~microcontroller.Pin sda: The data pin +//| :param ~microcontroller.Pin scl: The clock pin or DigitalInOut object +//| :param ~microcontroller.Pin sda: The data pin or DigitalInOut object //| :param int frequency: The clock frequency of the bus //| :param int timeout: The maximum clock stretching timeout in microseconds""" //| ... @@ -57,11 +57,8 @@ static mp_obj_t bitbangio_i2c_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - const mcu_pin_obj_t *scl = validate_obj_is_free_pin(args[ARG_scl].u_obj, MP_QSTR_scl); - const mcu_pin_obj_t *sda = validate_obj_is_free_pin(args[ARG_sda].u_obj, MP_QSTR_sda); - bitbangio_i2c_obj_t *self = mp_obj_malloc_with_finaliser(bitbangio_i2c_obj_t, &bitbangio_i2c_type); - shared_module_bitbangio_i2c_construct(self, scl, sda, args[ARG_frequency].u_int, args[ARG_timeout].u_int); + shared_module_bitbangio_i2c_construct(self, args[ARG_scl].u_obj, args[ARG_sda].u_obj, args[ARG_frequency].u_int, args[ARG_timeout].u_int); return (mp_obj_t)self; } diff --git a/shared-bindings/bitbangio/I2C.h b/shared-bindings/bitbangio/I2C.h index 022be3692a6cf..cc9cd21b6012a 100644 --- a/shared-bindings/bitbangio/I2C.h +++ b/shared-bindings/bitbangio/I2C.h @@ -16,8 +16,8 @@ extern const mp_obj_type_t bitbangio_i2c_type; // Initializes the hardware peripheral. extern void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self, - const mcu_pin_obj_t *scl, - const mcu_pin_obj_t *sda, + mp_obj_t scl, + mp_obj_t sda, uint32_t frequency, uint32_t us_timeout); diff --git a/shared-bindings/bitbangio/SPI.c b/shared-bindings/bitbangio/SPI.c index 8938ae4898d56..f2e635b47bb86 100644 --- a/shared-bindings/bitbangio/SPI.c +++ b/shared-bindings/bitbangio/SPI.c @@ -33,9 +33,9 @@ //| //| def __init__( //| self, -//| clock: microcontroller.Pin, -//| MOSI: Optional[microcontroller.Pin] = None, -//| MISO: Optional[microcontroller.Pin] = None, +//| clock: Union[microcontroller.Pin, digitalio.DigitalInOutProtocol], +//| MOSI: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, +//| MISO: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, //| ) -> None: //| """Construct an SPI object on the given pins. //| @@ -48,9 +48,9 @@ //| :ref:`Register ` data descriptors. //| //| -//| :param ~microcontroller.Pin clock: the pin to use for the clock. -//| :param ~microcontroller.Pin MOSI: the Main Out Selected In pin. -//| :param ~microcontroller.Pin MISO: the Main In Selected Out pin.""" +//| :param ~microcontroller.Pin clock: the pin to use for the clock or DigitalInOut object +//| :param ~microcontroller.Pin MOSI: the Main Out Selected In pin or DigitalInOut object +//| :param ~microcontroller.Pin MISO: the Main In Selected Out pin or DigitalInOut object""" //| ... //| @@ -65,12 +65,8 @@ static mp_obj_t bitbangio_spi_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); - const mcu_pin_obj_t *mosi = validate_obj_is_free_pin_or_none(args[ARG_MOSI].u_obj, MP_QSTR_mosi); - const mcu_pin_obj_t *miso = validate_obj_is_free_pin_or_none(args[ARG_MISO].u_obj, MP_QSTR_miso); - bitbangio_spi_obj_t *self = mp_obj_malloc(bitbangio_spi_obj_t, &bitbangio_spi_type); - shared_module_bitbangio_spi_construct(self, clock, mosi, miso); + shared_module_bitbangio_spi_construct(self, args[ARG_clock].u_obj, args[ARG_MOSI].u_obj, args[ARG_MISO].u_obj); return (mp_obj_t)self; } diff --git a/shared-bindings/bitbangio/SPI.h b/shared-bindings/bitbangio/SPI.h index dbe821683b0a2..19ec05152ad37 100644 --- a/shared-bindings/bitbangio/SPI.h +++ b/shared-bindings/bitbangio/SPI.h @@ -16,8 +16,7 @@ extern const mp_obj_type_t bitbangio_spi_type; // Construct an underlying SPI object. extern void shared_module_bitbangio_spi_construct(bitbangio_spi_obj_t *self, - const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *miso); + mp_obj_t clock, mp_obj_t mosi, mp_obj_t miso); extern void shared_module_bitbangio_spi_deinit(bitbangio_spi_obj_t *self); extern bool shared_module_bitbangio_spi_deinited(bitbangio_spi_obj_t *self); diff --git a/shared-bindings/board/__init__.h b/shared-bindings/board/__init__.h index fbc39317a30ce..4466f3924bf3c 100644 --- a/shared-bindings/board/__init__.h +++ b/shared-bindings/board/__init__.h @@ -11,7 +11,7 @@ #include "shared-bindings/microcontroller/Pin.h" // for the pin definitions -extern const mp_obj_dict_t board_module_globals; +extern mp_obj_dict_t board_module_globals; static const MP_DEFINE_STR_OBJ(board_module_id_obj, CIRCUITPY_BOARD_ID); mp_obj_t common_hal_board_get_i2c(const mp_int_t instance); @@ -38,3 +38,7 @@ MP_DECLARE_CONST_FUN_OBJ_0(board_uart_obj); #define CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS \ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_board) }, \ { MP_ROM_QSTR(MP_QSTR_board_id), MP_ROM_PTR(&board_module_id_obj) }, + +#define CIRCUITPYTHON_MUTABLE_BOARD_DICT_STANDARD_ITEMS \ + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_board) }, \ + { MP_ROM_QSTR(MP_QSTR_board_id), MP_OBJ_FROM_PTR(&board_module_id_obj) }, diff --git a/shared-bindings/busio/I2C.c b/shared-bindings/busio/I2C.c index 165b6a5ba936b..34c02349deb3d 100644 --- a/shared-bindings/busio/I2C.c +++ b/shared-bindings/busio/I2C.c @@ -228,10 +228,10 @@ static mp_obj_t busio_i2c_readfrom_into(size_t n_args, const mp_obj_t *pos_args, start *= stride_in_bytes; length *= stride_in_bytes; - uint8_t status = + mp_errno_t status = common_hal_busio_i2c_read(self, args[ARG_address].u_int, ((uint8_t *)bufinfo.buf) + start, length); if (status != 0) { - mp_raise_OSError(status); + mp_raise_OSError(-status); } return mp_const_none; @@ -290,11 +290,11 @@ static mp_obj_t busio_i2c_writeto(size_t n_args, const mp_obj_t *pos_args, mp_ma length *= stride_in_bytes; // do the transfer - uint8_t status = + mp_errno_t status = common_hal_busio_i2c_write(self, args[ARG_address].u_int, ((uint8_t *)bufinfo.buf) + start, length); if (status != 0) { - mp_raise_OSError(status); + mp_raise_OSError(-status); } return mp_const_none; @@ -377,10 +377,10 @@ static mp_obj_t busio_i2c_writeto_then_readfrom(size_t n_args, const mp_obj_t *p in_start *= in_stride_in_bytes; in_length *= in_stride_in_bytes; - uint8_t status = common_hal_busio_i2c_write_read(self, args[ARG_address].u_int, + mp_errno_t status = common_hal_busio_i2c_write_read(self, args[ARG_address].u_int, ((uint8_t *)out_bufinfo.buf) + out_start, out_length, ((uint8_t *)in_bufinfo.buf) + in_start, in_length); if (status != 0) { - mp_raise_OSError(status); + mp_raise_OSError(-status); } return mp_const_none; diff --git a/shared-bindings/busio/I2C.h b/shared-bindings/busio/I2C.h index 55f2d0f010850..e7293ce861752 100644 --- a/shared-bindings/busio/I2C.h +++ b/shared-bindings/busio/I2C.h @@ -7,6 +7,7 @@ #pragma once #include "py/obj.h" +#include "py/mperrno.h" #include "common-hal/microcontroller/Pin.h" #include "common-hal/busio/I2C.h" @@ -35,17 +36,17 @@ extern void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self); // Probe the bus to see if a device acknowledges the given address. extern bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr); -// Write to the device and return 0 on success or an appropriate error code from mperrno.h -extern uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, +// Write to the device and return 0 on success or a negative error code from mperrno.h +extern mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len); // Reads memory of the i2c device picking up where it left off and return 0 on -// success or an appropriate error code from mperrno.h -extern uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, +// success or a negative error code from mperrno.h +extern mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len); // Do a write and then a read in the same I2C transaction. -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t address, +mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len); // This is used by the supervisor to claim I2C devices indefinitely. diff --git a/shared-bindings/digitalio/DigitalInOut.c b/shared-bindings/digitalio/DigitalInOut.c index a29fdcc371ca9..7d8471650495d 100644 --- a/shared-bindings/digitalio/DigitalInOut.c +++ b/shared-bindings/digitalio/DigitalInOut.c @@ -17,6 +17,7 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" #include "shared-bindings/digitalio/Direction.h" #include "shared-bindings/digitalio/DriveMode.h" #include "shared-bindings/digitalio/Pull.h" @@ -341,12 +342,86 @@ static const mp_rom_map_elem_t digitalio_digitalinout_locals_dict_table[] = { static MP_DEFINE_CONST_DICT(digitalio_digitalinout_locals_dict, digitalio_digitalinout_locals_dict_table); +// Protocol implementation - thin wrappers to match protocol signature +static void digitalinout_proto_deinit(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_digitalio_digitalinout_deinit(self); +} + +static bool digitalinout_proto_deinited(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_deinited(self); +} + +static digitalinout_result_t digitalinout_proto_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_switch_to_input(self, pull); +} + +static digitalinout_result_t digitalinout_proto_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_switch_to_output(self, value, drive_mode); +} + +static digitalio_direction_t digitalinout_proto_get_direction(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_get_direction(self); +} + +static mp_errno_t digitalinout_proto_set_value(mp_obj_t self_in, bool value) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_digitalio_digitalinout_set_value(self, value); + return 0; +} + +static mp_errno_t digitalinout_proto_get_value(mp_obj_t self_in, bool *value) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + *value = common_hal_digitalio_digitalinout_get_value(self); + return 0; +} + +static digitalinout_result_t digitalinout_proto_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_set_drive_mode(self, drive_mode); +} + +static digitalio_drive_mode_t digitalinout_proto_get_drive_mode(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_get_drive_mode(self); +} + +static digitalinout_result_t digitalinout_proto_set_pull(mp_obj_t self_in, digitalio_pull_t pull) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_set_pull(self, pull); +} + +static digitalio_pull_t digitalinout_proto_get_pull(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_get_pull(self); +} + +static const digitalinout_p_t digitalinout_digitalinout_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_digitalinout) + .deinit = digitalinout_proto_deinit, + .deinited = digitalinout_proto_deinited, + .switch_to_input = digitalinout_proto_switch_to_input, + .switch_to_output = digitalinout_proto_switch_to_output, + .get_direction = digitalinout_proto_get_direction, + .get_value = digitalinout_proto_get_value, + .set_value = digitalinout_proto_set_value, + .get_drive_mode = digitalinout_proto_get_drive_mode, + .set_drive_mode = digitalinout_proto_set_drive_mode, + .get_pull = digitalinout_proto_get_pull, + .set_pull = digitalinout_proto_set_pull, +}; + MP_DEFINE_CONST_OBJ_TYPE( digitalio_digitalinout_type, MP_QSTR_DigitalInOut, MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, make_new, digitalio_digitalinout_make_new, - locals_dict, &digitalio_digitalinout_locals_dict + locals_dict, &digitalio_digitalinout_locals_dict, + protocol, &digitalinout_digitalinout_proto ); // Helper for validating digitalio.DigitalInOut arguments diff --git a/shared-bindings/digitalio/DigitalInOut.h b/shared-bindings/digitalio/DigitalInOut.h index f030e27a60b98..7465af3e288bd 100644 --- a/shared-bindings/digitalio/DigitalInOut.h +++ b/shared-bindings/digitalio/DigitalInOut.h @@ -28,6 +28,9 @@ typedef enum { #endif } digitalinout_result_t; +// Include protocol after types are defined +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" + typedef enum { DIGITALINOUT_REG_READ, DIGITALINOUT_REG_WRITE, diff --git a/shared-bindings/digitalio/DigitalInOutProtocol.c b/shared-bindings/digitalio/DigitalInOutProtocol.c new file mode 100644 index 0000000000000..e0671a1b88133 --- /dev/null +++ b/shared-bindings/digitalio/DigitalInOutProtocol.c @@ -0,0 +1,386 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" + +#include "py/obj.h" +#include "py/objtype.h" +#include "py/proto.h" +#include "py/runtime.h" +#include "py/nlr.h" +#include "py/gc.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" +#include "shared-bindings/util.h" +#include "supervisor/port_heap.h" + +//| from typing import Protocol, Optional +//| +//| class DigitalInOutProtocol(Protocol): +//| """Protocol for digital input/output pin control. +//| +//| Any object that implements this protocol can be used as a digital pin, +//| providing compatibility with code expecting a `digitalio.DigitalInOut`. +//| """ +//| +//| def deinit(self) -> None: +//| """Deinitialize the pin and release hardware resources.""" +//| ... +//| +//| def deinited(self) -> bool: +//| """Check whether the pin has been deinitialized. +//| +//| :return: True if deinitialized, False otherwise +//| """ +//| ... +//| +//| def switch_to_input(self, pull: Optional[digitalio.Pull] = None) -> None: +//| """Configure the pin as a digital input. +//| +//| :param pull: Pull resistor configuration (UP, DOWN, or None) +//| """ +//| ... +//| +//| def switch_to_output( +//| self, +//| value: bool = False, +//| drive_mode: digitalio.DriveMode = digitalio.DriveMode.PUSH_PULL +//| ) -> None: +//| """Configure the pin as a digital output. +//| +//| :param value: Initial output value (default False) +//| :param drive_mode: Output drive mode (PUSH_PULL or OPEN_DRAIN) +//| """ +//| ... +//| +//| @property +//| def direction(self) -> digitalio.Direction: +//| """The pin direction (INPUT or OUTPUT).""" +//| ... +//| +//| @direction.setter +//| def direction(self, value: digitalio.Direction) -> None: +//| ... +//| +//| @property +//| def value(self) -> bool: +//| """The digital logic level of the pin.""" +//| ... +//| +//| @value.setter +//| def value(self, val: bool) -> None: +//| ... +//| +//| @property +//| def pull(self) -> Optional[digitalio.Pull]: +//| """The pull resistor configuration for inputs (UP, DOWN, or None).""" +//| ... +//| +//| @pull.setter +//| def pull(self, pul: Optional[digitalio.Pull]) -> None: +//| ... +//| +//| @property +//| def drive_mode(self) -> digitalio.DriveMode: +//| """The drive mode for outputs (PUSH_PULL or OPEN_DRAIN).""" +//| ... +//| +//| @drive_mode.setter +//| def drive_mode(self, mode: digitalio.DriveMode) -> None: +//| ... +//| +// C Implementation Notes: +// ----------------------- +// For C implementations, define a digitalinout_p_t protocol structure and assign it +// to your type's protocol field in MP_DEFINE_CONST_OBJ_TYPE. +// +// Example: +// static const digitalinout_p_t my_type_proto = { +// MP_PROTO_IMPLEMENT(MP_QSTR_protocol_digitalinout) +// .construct = my_construct_func, +// .deinit = my_deinit_func, +// .deinited = my_deinited_func, +// .switch_to_input = my_switch_to_input_func, +// .switch_to_output = my_switch_to_output_func, +// .get_direction = my_get_direction_func, +// .get_value = my_get_value_func, +// .set_value = my_set_value_func, +// .get_drive_mode = my_get_drive_mode_func, +// .set_drive_mode = my_set_drive_mode_func, +// .get_pull = my_get_pull_func, +// .set_pull = my_set_pull_func, +// }; +// +// MP_DEFINE_CONST_OBJ_TYPE( +// my_type, +// MP_QSTR_MyType, +// MP_TYPE_FLAG_NONE, +// make_new, my_make_new, +// protocol, &my_type_proto +// ); +// +// See shared-bindings/digitalio/DigitalInOut.c for a complete example. +// + + +static void check_object_has_method(mp_obj_t obj, qstr method_name) { + mp_obj_t dest[2]; + mp_load_method_protected(obj, method_name, dest, true); + if (dest[0] == MP_OBJ_NULL) { + mp_raise_TypeError_varg(MP_ERROR_TEXT("%q object missing '%q' method"), MP_OBJ_TO_PTR(obj), method_name); + } +} + +static void check_object_has_attr(mp_obj_t obj, qstr attr_name) { + mp_obj_t dest[2]; + mp_load_method_protected(obj, attr_name, dest, true); + if (dest[0] == MP_OBJ_NULL) { + mp_raise_TypeError_varg(MP_ERROR_TEXT("%q object missing '%q' attribute"), MP_OBJ_TO_PTR(obj), attr_name); + } +} + +mp_obj_t digitalinout_protocol_from_pin( + mp_obj_t pin_or_dio, + qstr arg_name, + bool allow_none, + bool use_port_allocation, + bool *out_owns_pin) { + + *out_owns_pin = false; + + // Handle None case + if (allow_none && pin_or_dio == mp_const_none) { + return mp_const_none; + } + + // Check if it's a Pin + if (mp_obj_is_type(pin_or_dio, &mcu_pin_type)) { + // Validate the pin is free + const mcu_pin_obj_t *pin; + if (allow_none) { + pin = validate_obj_is_free_pin_or_none(pin_or_dio, arg_name); + if (pin == NULL) { + return mp_const_none; + } + } else { + pin = validate_obj_is_free_pin(pin_or_dio, arg_name); + } + + // Allocate and construct a DigitalInOut object + // Use port_malloc if GC is not available or if forced + digitalio_digitalinout_obj_t *dio; + if (use_port_allocation) { + dio = port_malloc(sizeof(digitalio_digitalinout_obj_t), false); + } else { + dio = m_malloc(sizeof(digitalio_digitalinout_obj_t)); + } + dio->base.type = &digitalio_digitalinout_type; + mp_obj_t dio_obj = MP_OBJ_FROM_PTR(dio); + *out_owns_pin = true; + + digitalinout_result_t result = common_hal_digitalio_digitalinout_construct(dio_obj, pin); + if (result != DIGITALINOUT_OK) { + // Free the allocation on error + if (use_port_allocation) { + port_free(dio); + } + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q init failed"), arg_name); + } + return dio_obj; + } + + // Check if it natively implements the DigitalInOutProtocol + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, pin_or_dio); + if (proto != NULL) { + // Native protocol support - use it directly + return pin_or_dio; + } + + // Verify the object has the required methods/attributes + check_object_has_method(pin_or_dio, MP_QSTR_deinit); + check_object_has_method(pin_or_dio, MP_QSTR_switch_to_input); + check_object_has_method(pin_or_dio, MP_QSTR_switch_to_output); + check_object_has_attr(pin_or_dio, MP_QSTR_deinited); + check_object_has_attr(pin_or_dio, MP_QSTR_direction); + check_object_has_attr(pin_or_dio, MP_QSTR_value); + check_object_has_attr(pin_or_dio, MP_QSTR_drive_mode); + check_object_has_attr(pin_or_dio, MP_QSTR_pull); + + // Object has all required attributes - use it as DigitalInOutProtocol + return pin_or_dio; +} + +void digitalinout_protocol_deinit(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->deinit) { + proto->deinit(self); + return; + } + + // Fallback to Python method call + mp_obj_t dest[2]; + mp_load_method_maybe(self, MP_QSTR_deinit, dest); + if (dest[0] != MP_OBJ_NULL) { + mp_call_method_n_kw(0, 0, dest); + return; + } +} + +bool digitalinout_protocol_deinited(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->deinited) { + return proto->deinited(self); + } + + // Try as attribute + mp_obj_t attr = mp_load_attr(self, MP_QSTR_deinited); + return mp_obj_is_true(attr); +} + +digitalinout_result_t digitalinout_protocol_switch_to_input(mp_obj_t self, digitalio_pull_t pull) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->switch_to_input) { + return proto->switch_to_input(self, pull); + } + + // Fallback to Python method call + mp_obj_t dest[3]; + mp_load_method_maybe(self, MP_QSTR_switch_to_input, dest); + if (dest[0] != MP_OBJ_NULL) { + mp_obj_t pull_obj = mp_const_none; + if (pull == PULL_UP) { + pull_obj = MP_OBJ_FROM_PTR(&digitalio_pull_up_obj); + } else if (pull == PULL_DOWN) { + pull_obj = MP_OBJ_FROM_PTR(&digitalio_pull_down_obj); + } + dest[2] = pull_obj; + mp_call_method_n_kw(1, 0, dest); + return DIGITALINOUT_OK; + } + + return DIGITALINOUT_PIN_BUSY; +} + +digitalinout_result_t digitalinout_protocol_switch_to_output(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->switch_to_output) { + return proto->switch_to_output(self, value, drive_mode); + } + + // Fallback to Python method call + mp_obj_t dest[4]; + mp_load_method_maybe(self, MP_QSTR_switch_to_output, dest); + if (dest[0] != MP_OBJ_NULL) { + dest[2] = mp_obj_new_bool(value); + dest[3] = (drive_mode == DRIVE_MODE_PUSH_PULL) ? + MP_OBJ_FROM_PTR(&digitalio_drive_mode_push_pull_obj) : + MP_OBJ_FROM_PTR(&digitalio_drive_mode_open_drain_obj); + mp_call_method_n_kw(2, 0, dest); + return DIGITALINOUT_OK; + } + + mp_raise_TypeError(MP_ERROR_TEXT("object does not support DigitalInOut protocol")); +} + +digitalio_direction_t digitalinout_protocol_get_direction(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->get_direction) { + return proto->get_direction(self); + } + + // Fallback to Python attribute access + mp_obj_t direction = mp_load_attr(self, MP_QSTR_direction); + if (direction == MP_ROM_PTR(&digitalio_direction_input_obj)) { + return DIRECTION_INPUT; + } + return DIRECTION_OUTPUT; +} + +mp_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->set_value) { + return proto->set_value(self, value); + } + + // Fallback to Python attribute assignment + mp_store_attr(self, MP_QSTR_value, mp_obj_new_bool(value)); + return 0; +} + +mp_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->get_value) { + return proto->get_value(self, value); + } + + // Fallback to Python attribute access + *value = mp_obj_is_true(mp_load_attr(self, MP_QSTR_value)); + return 0; +} + +digitalinout_result_t digitalinout_protocol_set_drive_mode(mp_obj_t self, digitalio_drive_mode_t drive_mode) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->set_drive_mode) { + return proto->set_drive_mode(self, drive_mode); + } + + // Fallback to Python attribute assignment + mp_obj_t drive_mode_obj = (drive_mode == DRIVE_MODE_PUSH_PULL) ? + MP_OBJ_FROM_PTR(&digitalio_drive_mode_push_pull_obj) : + MP_OBJ_FROM_PTR(&digitalio_drive_mode_open_drain_obj); + mp_store_attr(self, MP_QSTR_drive_mode, drive_mode_obj); + return DIGITALINOUT_OK; +} + +digitalio_drive_mode_t digitalinout_protocol_get_drive_mode(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->get_drive_mode) { + return proto->get_drive_mode(self); + } + + // Fallback to Python attribute access + mp_obj_t drive_mode = mp_load_attr(self, MP_QSTR_drive_mode); + if (drive_mode == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + return DRIVE_MODE_OPEN_DRAIN; + } + return DRIVE_MODE_PUSH_PULL; +} + +digitalinout_result_t digitalinout_protocol_set_pull(mp_obj_t self, digitalio_pull_t pull) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->set_pull) { + return proto->set_pull(self, pull); + } + + // Fallback to Python attribute assignment + mp_obj_t pull_obj = mp_const_none; + if (pull == PULL_UP) { + pull_obj = MP_OBJ_FROM_PTR(&digitalio_pull_up_obj); + } else if (pull == PULL_DOWN) { + pull_obj = MP_OBJ_FROM_PTR(&digitalio_pull_down_obj); + } + mp_store_attr(self, MP_QSTR_pull, pull_obj); + return DIGITALINOUT_OK; +} + +digitalio_pull_t digitalinout_protocol_get_pull(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->get_pull) { + return proto->get_pull(self); + } + + // Fallback to Python attribute access + mp_obj_t pull = mp_load_attr(self, MP_QSTR_pull); + if (pull == MP_OBJ_FROM_PTR(&digitalio_pull_up_obj)) { + return PULL_UP; + } else if (pull == MP_OBJ_FROM_PTR(&digitalio_pull_down_obj)) { + return PULL_DOWN; + } + return PULL_NONE; +} diff --git a/shared-bindings/digitalio/DigitalInOutProtocol.h b/shared-bindings/digitalio/DigitalInOutProtocol.h new file mode 100644 index 0000000000000..5ec8bcb185543 --- /dev/null +++ b/shared-bindings/digitalio/DigitalInOutProtocol.h @@ -0,0 +1,67 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" +#include "py/proto.h" +#include "py/mperrno.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" + +// Protocol structure for DigitalInOut implementations +// Note: mcu_pin_obj_t and digitalinout_result_t are defined by files that include this header +typedef struct _digitalinout_p_t { + MP_PROTOCOL_HEAD // MP_QSTR_protocol_digitalinout + void (*deinit)(mp_obj_t self); + bool (*deinited)(mp_obj_t self); + digitalinout_result_t (*switch_to_input)(mp_obj_t self, digitalio_pull_t pull); + digitalinout_result_t (*switch_to_output)(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode); + digitalio_direction_t (*get_direction)(mp_obj_t self); + mp_errno_t (*set_value)(mp_obj_t self, bool value); // Return 0 if ok + mp_errno_t (*get_value)(mp_obj_t self, bool *value); // Return 0 if ok + digitalinout_result_t (*set_drive_mode)(mp_obj_t self, digitalio_drive_mode_t drive_mode); + digitalio_drive_mode_t (*get_drive_mode)(mp_obj_t self); + digitalinout_result_t (*set_pull)(mp_obj_t self, digitalio_pull_t pull); + digitalio_pull_t (*get_pull)(mp_obj_t self); +} digitalinout_p_t; + +// Protocol helper functions +// These functions work with any object that implements the DigitalInOut protocol, +// either through native C protocol or Python attributes/methods. + +// Converts a Pin or DigitalInOutProtocol to a DigitalInOutProtocol object. +// If pin_or_dio is a Pin, allocates and initializes a DigitalInOut object. +// If pin_or_dio is already a DigitalInOutProtocol, returns it directly. +// If allow_none is true and pin_or_dio is None, returns None. +// If force_port_allocation is true, uses port_malloc instead of GC allocation. +// Sets *out_owns_pin to true if a new DigitalInOut was allocated (caller must deinit and free). +// Returns the DigitalInOutProtocol object to use. +// Raises an exception on error. +// Note: To free allocated objects, deinit first, then use gc_ptr_on_heap() to determine +// if port_free() should be called (if not on heap) or let GC handle it (if on heap). +mp_obj_t digitalinout_protocol_from_pin( + mp_obj_t pin_or_dio, + qstr arg_name, + bool allow_none, + bool force_port_allocation, + bool *out_owns_pin); + +void digitalinout_protocol_deinit(mp_obj_t self); +bool digitalinout_protocol_deinited(mp_obj_t self); +digitalinout_result_t digitalinout_protocol_switch_to_input(mp_obj_t self, digitalio_pull_t pull); +digitalinout_result_t digitalinout_protocol_switch_to_output(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode); +digitalio_direction_t digitalinout_protocol_get_direction(mp_obj_t self); +mp_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value); +mp_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value); +digitalinout_result_t digitalinout_protocol_set_drive_mode(mp_obj_t self, digitalio_drive_mode_t drive_mode); +digitalio_drive_mode_t digitalinout_protocol_get_drive_mode(mp_obj_t self); +digitalinout_result_t digitalinout_protocol_set_pull(mp_obj_t self, digitalio_pull_t pull); +digitalio_pull_t digitalinout_protocol_get_pull(mp_obj_t self); diff --git a/shared-bindings/fourwire/FourWire.c b/shared-bindings/fourwire/FourWire.c index 083cf21a00367..608645aaebf0b 100644 --- a/shared-bindings/fourwire/FourWire.c +++ b/shared-bindings/fourwire/FourWire.c @@ -30,9 +30,9 @@ //| self, //| spi_bus: busio.SPI, //| *, -//| command: Optional[microcontroller.Pin], -//| chip_select: Optional[microcontroller.Pin], -//| reset: Optional[microcontroller.Pin] = None, +//| command: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, +//| chip_select: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, +//| reset: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, //| baudrate: int = 24000000, //| polarity: int = 0, //| phase: int = 0, @@ -73,10 +73,6 @@ static mp_obj_t fourwire_fourwire_make_new(const mp_obj_type_t *type, size_t n_a mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - const mcu_pin_obj_t *command = validate_obj_is_free_pin_or_none(args[ARG_command].u_obj, MP_QSTR_command); - const mcu_pin_obj_t *chip_select = validate_obj_is_free_pin_or_none(args[ARG_chip_select].u_obj, MP_QSTR_chip_select); - const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj, MP_QSTR_reset); - mp_obj_t spi = mp_arg_validate_type(args[ARG_spi_bus].u_obj, &busio_spi_type, MP_QSTR_spi_bus); fourwire_fourwire_obj_t *self = &allocate_display_bus_or_raise()->fourwire_bus; @@ -86,7 +82,7 @@ static mp_obj_t fourwire_fourwire_make_new(const mp_obj_type_t *type, size_t n_a uint8_t phase = (uint8_t)mp_arg_validate_int_range(args[ARG_phase].u_int, 0, 1, MP_QSTR_phase); common_hal_fourwire_fourwire_construct(self, - MP_OBJ_TO_PTR(spi), command, chip_select, reset, args[ARG_baudrate].u_int, polarity, phase); + MP_OBJ_TO_PTR(spi), args[ARG_command].u_obj, args[ARG_chip_select].u_obj, args[ARG_reset].u_obj, args[ARG_baudrate].u_int, polarity, phase); return self; } diff --git a/shared-bindings/fourwire/FourWire.h b/shared-bindings/fourwire/FourWire.h index 515a466b4b98c..3e77c25434d64 100644 --- a/shared-bindings/fourwire/FourWire.h +++ b/shared-bindings/fourwire/FourWire.h @@ -16,8 +16,8 @@ extern const mp_obj_type_t fourwire_fourwire_type; void common_hal_fourwire_fourwire_construct(fourwire_fourwire_obj_t *self, - busio_spi_obj_t *spi, const mcu_pin_obj_t *command, - const mcu_pin_obj_t *chip_select, const mcu_pin_obj_t *reset, uint32_t baudrate, + busio_spi_obj_t *spi, mp_obj_t command, + mp_obj_t chip_select, mp_obj_t reset, uint32_t baudrate, uint8_t polarity, uint8_t phase); void common_hal_fourwire_fourwire_deinit(fourwire_fourwire_obj_t *self); diff --git a/shared-bindings/i2cioexpander/IOExpander.c b/shared-bindings/i2cioexpander/IOExpander.c new file mode 100644 index 0000000000000..09c404d289c74 --- /dev/null +++ b/shared-bindings/i2cioexpander/IOExpander.c @@ -0,0 +1,247 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/i2cioexpander/IOExpander.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/util.h" +#include "shared/runtime/context_manager_helpers.h" + +//| class IOExpander: +//| """Control a generic I2C-based GPIO expander +//| +//| IOExpander provides a simple interface to I2C-based GPIO expanders that +//| use basic register reads and writes for control. The expander provides +//| individual pins through the `pins` attribute that implement the +//| DigitalInOutProtocol. +//| """ +//| +//| def __init__( +//| self, +//| i2c: busio.I2C, +//| address: int, +//| num_pins: int, +//| set_value_reg: Optional[int] = None, +//| get_value_reg: Optional[int] = None, +//| set_direction_reg: Optional[int] = None, +//| ) -> None: +//| """Initialize an I2C GPIO expander +//| +//| :param busio.I2C i2c: The I2C bus the expander is connected to +//| :param int address: The I2C device address +//| :param int num_pins: The number of GPIO pins (8 or 16) +//| :param int set_value_reg: Register address to write pin values (optional) +//| :param int get_value_reg: Register address to read pin values (optional) +//| :param int set_direction_reg: Register address to set pin directions (optional) +//| """ +//| ... + +static mp_obj_t i2cioexpander_ioexpander_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_i2c, ARG_address, ARG_num_pins, ARG_set_value_reg, ARG_get_value_reg, ARG_set_direction_reg }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_num_pins, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_set_value_reg, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_get_value_reg, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_set_direction_reg, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Validate I2C object + mp_obj_t i2c = mp_arg_validate_type(args[ARG_i2c].u_obj, &busio_i2c_type, MP_QSTR_i2c); + + // Validate address + int address = args[ARG_address].u_int; + if (address < 0 || address > 0x7F) { + mp_raise_ValueError(MP_ERROR_TEXT("address out of range")); + } + + // Validate num_pins + int num_pins = args[ARG_num_pins].u_int; + if (num_pins != 8 && num_pins != 16) { + mp_raise_ValueError(MP_ERROR_TEXT("num_pins must be 8 or 16")); + } + + i2cioexpander_ioexpander_obj_t *self = mp_obj_malloc(i2cioexpander_ioexpander_obj_t, &i2cioexpander_ioexpander_type); + + // Convert and validate register parameters + uint16_t set_value_reg = NO_REGISTER; + if (args[ARG_set_value_reg].u_obj != mp_const_none) { + mp_int_t reg = mp_obj_get_int(args[ARG_set_value_reg].u_obj); + mp_arg_validate_int_range(reg, 0, 255, MP_QSTR_set_value_reg); + set_value_reg = reg; + } + + uint16_t get_value_reg = NO_REGISTER; + if (args[ARG_get_value_reg].u_obj != mp_const_none) { + mp_int_t reg = mp_obj_get_int(args[ARG_get_value_reg].u_obj); + mp_arg_validate_int_range(reg, 0, 255, MP_QSTR_get_value_reg); + get_value_reg = reg; + } + + uint16_t set_direction_reg = NO_REGISTER; + if (args[ARG_set_direction_reg].u_obj != mp_const_none) { + mp_int_t reg = mp_obj_get_int(args[ARG_set_direction_reg].u_obj); + mp_arg_validate_int_range(reg, 0, 255, MP_QSTR_set_direction_reg); + set_direction_reg = reg; + } + + common_hal_i2cioexpander_ioexpander_construct( + self, + i2c, + address, + num_pins, + set_value_reg, + get_value_reg, + set_direction_reg); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialize the expander. No further operations are possible.""" +//| ... +static mp_obj_t i2cioexpander_ioexpander_deinit(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_i2cioexpander_ioexpander_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_deinit_obj, i2cioexpander_ioexpander_deinit); + +//| def __enter__(self) -> IOExpander: +//| """No-op used by Context Managers.""" +//| ... +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +static mp_obj_t i2cioexpander_ioexpander___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_i2cioexpander_ioexpander_deinit(MP_OBJ_TO_PTR(args[0])); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(i2cioexpander_ioexpander___exit___obj, 4, 4, i2cioexpander_ioexpander___exit__); + +//| @property +//| def input_value(self) -> int: +//| """Read the live value of all pins at once. Returns an integer where each +//| bit represents a pin's current state.""" +//| ... +static mp_obj_t i2cioexpander_ioexpander_obj_get_input_value(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t value; + if (!common_hal_i2cioexpander_ioexpander_get_input_value(self, &value)) { + mp_raise_OSError(MP_EIO); + } + return MP_OBJ_NEW_SMALL_INT(value); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_input_value_obj, i2cioexpander_ioexpander_obj_get_input_value); + +MP_PROPERTY_GETTER(i2cioexpander_ioexpander_input_value_obj, + (mp_obj_t)&i2cioexpander_ioexpander_get_input_value_obj); + +//| @property +//| def output_value(self) -> int: +//| """Get or set the cached output value. Reading returns the last value written, +//| not the live pin state. Writing updates the output pins.""" +//| ... +//| @output_value.setter +//| def output_value(self, val: int) -> None: ... +static mp_obj_t i2cioexpander_ioexpander_obj_get_output_value(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t value; + common_hal_i2cioexpander_ioexpander_get_output_value(self, &value); + return mp_obj_new_int(value); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_output_value_obj, i2cioexpander_ioexpander_obj_get_output_value); + +static mp_obj_t i2cioexpander_ioexpander_obj_set_output_value(mp_obj_t self_in, mp_obj_t value) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_errno_t result = common_hal_i2cioexpander_ioexpander_set_output_value(self, mp_obj_get_int(value)); + if (result != 0) { + mp_raise_OSError(result); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_ioexpander_set_output_value_obj, i2cioexpander_ioexpander_obj_set_output_value); + +MP_PROPERTY_GETSET(i2cioexpander_ioexpander_output_value_obj, + (mp_obj_t)&i2cioexpander_ioexpander_get_output_value_obj, + (mp_obj_t)&i2cioexpander_ioexpander_set_output_value_obj); + +//| @property +//| def output_mask(self) -> int: +//| """Get or set which pins are configured as outputs. Each bit in the mask +//| represents a pin: 1 for output, 0 for input.""" +//| ... +//| @output_mask.setter +//| def output_mask(self, val: int) -> None: ... +static mp_obj_t i2cioexpander_ioexpander_obj_get_output_mask(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t output_mask; + common_hal_i2cioexpander_ioexpander_get_output_mask(self, &output_mask); + return mp_obj_new_int(output_mask); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_output_mask_obj, i2cioexpander_ioexpander_obj_get_output_mask); + +static mp_obj_t i2cioexpander_ioexpander_obj_set_output_mask(mp_obj_t self_in, mp_obj_t value) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_errno_t result = common_hal_i2cioexpander_ioexpander_set_output_mask(self, mp_obj_get_int(value)); + if (result != 0) { + mp_raise_OSError(result); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_ioexpander_set_output_mask_obj, i2cioexpander_ioexpander_obj_set_output_mask); + +MP_PROPERTY_GETSET(i2cioexpander_ioexpander_output_mask_obj, + (mp_obj_t)&i2cioexpander_ioexpander_get_output_mask_obj, + (mp_obj_t)&i2cioexpander_ioexpander_set_output_mask_obj); + +//| @property +//| def pins(self) -> Tuple[DigitalInOutProtocol, ...]: +//| """A tuple of pin objects that implement the DigitalInOutProtocol. +//| Each pin can be used like a digitalio.DigitalInOut object.""" +//| ... +static mp_obj_t i2cioexpander_ioexpander_obj_get_pins(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_ioexpander_get_pins(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_pins_obj, i2cioexpander_ioexpander_obj_get_pins); + +MP_PROPERTY_GETTER(i2cioexpander_ioexpander_pins_obj, + (mp_obj_t)&i2cioexpander_ioexpander_get_pins_obj); + +static const mp_rom_map_elem_t i2cioexpander_ioexpander_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&i2cioexpander_ioexpander_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&i2cioexpander_ioexpander___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_input_value), MP_ROM_PTR(&i2cioexpander_ioexpander_input_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_output_value), MP_ROM_PTR(&i2cioexpander_ioexpander_output_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_output_mask), MP_ROM_PTR(&i2cioexpander_ioexpander_output_mask_obj) }, + { MP_ROM_QSTR(MP_QSTR_pins), MP_ROM_PTR(&i2cioexpander_ioexpander_pins_obj) }, +}; +static MP_DEFINE_CONST_DICT(i2cioexpander_ioexpander_locals_dict, i2cioexpander_ioexpander_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + i2cioexpander_ioexpander_type, + MP_QSTR_IOExpander, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, i2cioexpander_ioexpander_make_new, + locals_dict, &i2cioexpander_ioexpander_locals_dict + ); diff --git a/shared-bindings/i2cioexpander/IOExpander.h b/shared-bindings/i2cioexpander/IOExpander.h new file mode 100644 index 0000000000000..64e49b603648d --- /dev/null +++ b/shared-bindings/i2cioexpander/IOExpander.h @@ -0,0 +1,34 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-module/i2cioexpander/IOExpander.h" + +extern const mp_obj_type_t i2cioexpander_ioexpander_type; + +void common_hal_i2cioexpander_ioexpander_construct( + i2cioexpander_ioexpander_obj_t *self, + mp_obj_t i2c, + uint8_t address, + uint8_t num_pins, + uint16_t set_value_reg, + uint16_t get_value_reg, + uint16_t set_direction_reg); + +void common_hal_i2cioexpander_ioexpander_deinit(i2cioexpander_ioexpander_obj_t *self); +bool common_hal_i2cioexpander_ioexpander_deinited(i2cioexpander_ioexpander_obj_t *self); + +mp_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioexpander_obj_t *self, size_t *value); +// No error return because this returns a cached value. +void common_hal_i2cioexpander_ioexpander_get_output_value(i2cioexpander_ioexpander_obj_t *self, size_t *value); +mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_ioexpander_obj_t *self, size_t value); + +void common_hal_i2cioexpander_ioexpander_get_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t *mask); +mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t mask); + +mp_obj_t common_hal_i2cioexpander_ioexpander_get_pins(i2cioexpander_ioexpander_obj_t *self); diff --git a/shared-bindings/i2cioexpander/IOPin.c b/shared-bindings/i2cioexpander/IOPin.c new file mode 100644 index 0000000000000..3e43e18831fde --- /dev/null +++ b/shared-bindings/i2cioexpander/IOPin.c @@ -0,0 +1,331 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/i2cioexpander/IOPin.h" +#include "shared-module/i2cioexpander/IOPin.h" + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" +#include "shared-bindings/util.h" + +static void check_result(digitalinout_result_t result) { + switch (result) { + case DIGITALINOUT_OK: + return; + case DIGITALINOUT_PIN_BUSY: + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_Pin); + #if CIRCUITPY_DIGITALIO_HAVE_INPUT_ONLY + case DIGITALINOUT_INPUT_ONLY: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_direction); + #endif + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL + case DIGITALINOUT_INVALID_PULL: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pull); + #endif + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE + case DIGITALINOUT_INVALID_DRIVE_MODE: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_drive_mode); + #endif + } +} + +static inline void check_for_deinit(i2cioexpander_iopin_obj_t *self) { + if (common_hal_i2cioexpander_iopin_deinited(self)) { + raise_deinited_error(); + } +} + +//| def deinit(self) -> None: +//| """Turn off the pin and release it for other use.""" +//| ... +static mp_obj_t i2cioexpander_iopin_deinit(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_i2cioexpander_iopin_deinit(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_deinit_obj, i2cioexpander_iopin_deinit); + +//| def switch_to_output( +//| self, value: bool = False, drive_mode: digitalio.DriveMode = digitalio.DriveMode.PUSH_PULL +//| ) -> None: +//| """Set the drive mode and value and then switch to writing out digital values. +//| +//| :param bool value: default value to set upon switching +//| :param digitalio.DriveMode drive_mode: drive mode for the output""" +//| ... +static mp_obj_t i2cioexpander_iopin_switch_to_output(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_value, ARG_drive_mode }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_value, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_drive_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&digitalio_drive_mode_push_pull_obj)} }, + }; + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + digitalio_drive_mode_t drive_mode = DRIVE_MODE_PUSH_PULL; + if (args[ARG_drive_mode].u_rom_obj == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + drive_mode = DRIVE_MODE_OPEN_DRAIN; + } + check_result(common_hal_i2cioexpander_iopin_switch_to_output(self, args[ARG_value].u_bool, drive_mode)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(i2cioexpander_iopin_switch_to_output_obj, 1, i2cioexpander_iopin_switch_to_output); + +//| def switch_to_input(self, pull: Optional[digitalio.Pull] = None) -> None: +//| """Set the pull and then switch to read in digital values. +//| +//| :param digitalio.Pull pull: pull configuration for the input""" +//| ... +static mp_obj_t i2cioexpander_iopin_switch_to_input(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_pull }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + }; + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + check_result(common_hal_i2cioexpander_iopin_switch_to_input(self, validate_pull(args[ARG_pull].u_rom_obj, MP_QSTR_pull))); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(i2cioexpander_iopin_switch_to_input_obj, 1, i2cioexpander_iopin_switch_to_input); + +//| direction: digitalio.Direction +//| """The direction of the pin.""" +static mp_obj_t i2cioexpander_iopin_obj_get_direction(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + digitalio_direction_t direction = common_hal_i2cioexpander_iopin_get_direction(self); + if (direction == DIRECTION_INPUT) { + return MP_OBJ_FROM_PTR(&digitalio_direction_input_obj); + } + return MP_OBJ_FROM_PTR(&digitalio_direction_output_obj); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_get_direction_obj, i2cioexpander_iopin_obj_get_direction); + +static mp_obj_t i2cioexpander_iopin_obj_set_direction(mp_obj_t self_in, mp_obj_t value) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (value == MP_ROM_PTR(&digitalio_direction_input_obj)) { + check_result(common_hal_i2cioexpander_iopin_switch_to_input(self, PULL_NONE)); + } else if (value == MP_ROM_PTR(&digitalio_direction_output_obj)) { + check_result(common_hal_i2cioexpander_iopin_switch_to_output(self, false, DRIVE_MODE_PUSH_PULL)); + } else { + mp_arg_error_invalid(MP_QSTR_direction); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_iopin_set_direction_obj, i2cioexpander_iopin_obj_set_direction); + +MP_PROPERTY_GETSET(i2cioexpander_iopin_direction_obj, + (mp_obj_t)&i2cioexpander_iopin_get_direction_obj, + (mp_obj_t)&i2cioexpander_iopin_set_direction_obj); + +//| value: bool +//| """The digital logic level of the pin.""" +static mp_obj_t i2cioexpander_iopin_obj_get_value(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + bool value; + mp_errno_t res = common_hal_i2cioexpander_iopin_get_value(self, &value); + if (res != 0) { + mp_raise_OSError(-res); + } + return mp_obj_new_bool(value); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_get_value_obj, i2cioexpander_iopin_obj_get_value); + +static mp_obj_t i2cioexpander_iopin_obj_set_value(mp_obj_t self_in, mp_obj_t value) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Cannot set value when direction is input.")); + return mp_const_none; + } + mp_errno_t res = common_hal_i2cioexpander_iopin_set_value(self, mp_obj_is_true(value)); + if (res != 0) { + mp_raise_OSError(-res); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_iopin_set_value_obj, i2cioexpander_iopin_obj_set_value); + +MP_PROPERTY_GETSET(i2cioexpander_iopin_value_obj, + (mp_obj_t)&i2cioexpander_iopin_get_value_obj, + (mp_obj_t)&i2cioexpander_iopin_set_value_obj); + +//| drive_mode: digitalio.DriveMode +//| """The pin drive mode.""" +static mp_obj_t i2cioexpander_iopin_obj_get_drive_mode(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Drive mode not used when direction is input.")); + return mp_const_none; + } + digitalio_drive_mode_t drive_mode = common_hal_i2cioexpander_iopin_get_drive_mode(self); + if (drive_mode == DRIVE_MODE_PUSH_PULL) { + return MP_OBJ_FROM_PTR(&digitalio_drive_mode_push_pull_obj); + } + return MP_OBJ_FROM_PTR(&digitalio_drive_mode_open_drain_obj); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_get_drive_mode_obj, i2cioexpander_iopin_obj_get_drive_mode); + +static mp_obj_t i2cioexpander_iopin_obj_set_drive_mode(mp_obj_t self_in, mp_obj_t drive_mode) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Drive mode not used when direction is input.")); + return mp_const_none; + } + digitalio_drive_mode_t c_drive_mode = DRIVE_MODE_PUSH_PULL; + if (drive_mode == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + c_drive_mode = DRIVE_MODE_OPEN_DRAIN; + } + check_result(common_hal_i2cioexpander_iopin_set_drive_mode(self, c_drive_mode)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_iopin_set_drive_mode_obj, i2cioexpander_iopin_obj_set_drive_mode); + +MP_PROPERTY_GETSET(i2cioexpander_iopin_drive_mode_obj, + (mp_obj_t)&i2cioexpander_iopin_get_drive_mode_obj, + (mp_obj_t)&i2cioexpander_iopin_set_drive_mode_obj); + +//| pull: Optional[digitalio.Pull] +//| """The pin pull direction.""" +static mp_obj_t i2cioexpander_iopin_obj_get_pull(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_OUTPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Pull not used when direction is output.")); + return mp_const_none; + } + digitalio_pull_t pull = common_hal_i2cioexpander_iopin_get_pull(self); + if (pull == PULL_UP) { + return MP_OBJ_FROM_PTR(&digitalio_pull_up_obj); + } else if (pull == PULL_DOWN) { + return MP_OBJ_FROM_PTR(&digitalio_pull_down_obj); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_get_pull_obj, i2cioexpander_iopin_obj_get_pull); + +static mp_obj_t i2cioexpander_iopin_obj_set_pull(mp_obj_t self_in, mp_obj_t pull_obj) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_OUTPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Pull not used when direction is output.")); + return mp_const_none; + } + check_result(common_hal_i2cioexpander_iopin_set_pull(self, validate_pull(pull_obj, MP_QSTR_pull))); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_iopin_set_pull_obj, i2cioexpander_iopin_obj_set_pull); + +MP_PROPERTY_GETSET(i2cioexpander_iopin_pull_obj, + (mp_obj_t)&i2cioexpander_iopin_get_pull_obj, + (mp_obj_t)&i2cioexpander_iopin_set_pull_obj); + +// Protocol implementation for DigitalInOutProtocol +static void iopin_protocol_deinit(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_i2cioexpander_iopin_deinit(self); +} + +static bool iopin_protocol_deinited(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_deinited(self); +} + +static digitalinout_result_t iopin_protocol_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_switch_to_input(self, pull); +} + +static digitalinout_result_t iopin_protocol_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_switch_to_output(self, value, drive_mode); +} + +static digitalio_direction_t iopin_protocol_get_direction(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_get_direction(self); +} + +static mp_errno_t iopin_protocol_get_value(mp_obj_t self_in, bool *value) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_get_value(self, value); +} + +static mp_errno_t iopin_protocol_set_value(mp_obj_t self_in, bool value) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_set_value(self, value); +} + +static digitalio_drive_mode_t iopin_protocol_get_drive_mode(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_get_drive_mode(self); +} + +static digitalinout_result_t iopin_protocol_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_set_drive_mode(self, drive_mode); +} + +static digitalio_pull_t iopin_protocol_get_pull(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_get_pull(self); +} + +static digitalinout_result_t iopin_protocol_set_pull(mp_obj_t self_in, digitalio_pull_t pull) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_set_pull(self, pull); +} + +static const digitalinout_p_t iopin_digitalinout_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_digitalinout) + .deinit = iopin_protocol_deinit, + .deinited = iopin_protocol_deinited, + .switch_to_input = iopin_protocol_switch_to_input, + .switch_to_output = iopin_protocol_switch_to_output, + .get_direction = iopin_protocol_get_direction, + .get_value = iopin_protocol_get_value, + .set_value = iopin_protocol_set_value, + .get_drive_mode = iopin_protocol_get_drive_mode, + .set_drive_mode = iopin_protocol_set_drive_mode, + .get_pull = iopin_protocol_get_pull, + .set_pull = iopin_protocol_set_pull, +}; + +static const mp_rom_map_elem_t i2cioexpander_iopin_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&i2cioexpander_iopin_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_switch_to_input), MP_ROM_PTR(&i2cioexpander_iopin_switch_to_input_obj) }, + { MP_ROM_QSTR(MP_QSTR_switch_to_output), MP_ROM_PTR(&i2cioexpander_iopin_switch_to_output_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_direction), MP_ROM_PTR(&i2cioexpander_iopin_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&i2cioexpander_iopin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_drive_mode), MP_ROM_PTR(&i2cioexpander_iopin_drive_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_pull), MP_ROM_PTR(&i2cioexpander_iopin_pull_obj) }, +}; +static MP_DEFINE_CONST_DICT(i2cioexpander_iopin_locals_dict, i2cioexpander_iopin_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + i2cioexpander_iopin_type, + MP_QSTR_IOPin, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + protocol, &iopin_digitalinout_p, + locals_dict, &i2cioexpander_iopin_locals_dict + ); diff --git a/shared-bindings/i2cioexpander/IOPin.h b/shared-bindings/i2cioexpander/IOPin.h new file mode 100644 index 0000000000000..6a7d96130fa62 --- /dev/null +++ b/shared-bindings/i2cioexpander/IOPin.h @@ -0,0 +1,46 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-module/i2cioexpander/IOPin.h" + +extern const mp_obj_type_t i2cioexpander_iopin_type; + +mp_errno_t i2cioexpander_iopin_construct( + i2cioexpander_iopin_obj_t *self, + i2cioexpander_ioexpander_obj_t *expander, + uint8_t pin_number); + +void common_hal_i2cioexpander_iopin_deinit(i2cioexpander_iopin_obj_t *self); +bool common_hal_i2cioexpander_iopin_deinited(i2cioexpander_iopin_obj_t *self); + +digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_input( + i2cioexpander_iopin_obj_t *self, + digitalio_pull_t pull); + +digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_output( + i2cioexpander_iopin_obj_t *self, + bool value, + digitalio_drive_mode_t drive_mode); + +digitalio_direction_t common_hal_i2cioexpander_iopin_get_direction(i2cioexpander_iopin_obj_t *self); + +mp_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *self, bool value); +mp_errno_t common_hal_i2cioexpander_iopin_get_value(i2cioexpander_iopin_obj_t *self, bool *value); + +digitalinout_result_t common_hal_i2cioexpander_iopin_set_drive_mode( + i2cioexpander_iopin_obj_t *self, + digitalio_drive_mode_t drive_mode); + +digitalio_drive_mode_t common_hal_i2cioexpander_iopin_get_drive_mode(i2cioexpander_iopin_obj_t *self); + +digitalinout_result_t common_hal_i2cioexpander_iopin_set_pull( + i2cioexpander_iopin_obj_t *self, + digitalio_pull_t pull); + +digitalio_pull_t common_hal_i2cioexpander_iopin_get_pull(i2cioexpander_iopin_obj_t *self); diff --git a/shared-bindings/i2cioexpander/__init__.c b/shared-bindings/i2cioexpander/__init__.c new file mode 100644 index 0000000000000..106c45c3768bf --- /dev/null +++ b/shared-bindings/i2cioexpander/__init__.c @@ -0,0 +1,49 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/i2cioexpander/__init__.h" +#include "shared-bindings/i2cioexpander/IOExpander.h" + +//| """Support for I2C-based GPIO expanders +//| +//| The `i2cioexpander` module contains classes to support I2C-based GPIO expanders +//| that can be controlled via simple register reads and writes. +//| +//| All classes change hardware state and should be deinitialized when they +//| are no longer needed if the program continues after use. To do so, either +//| call :py:meth:`!deinit` or use a context manager. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| +//| Example:: +//| +//| import board +//| import busio +//| import i2cioexpander +//| +//| i2c = busio.I2C(board.SCL, board.SDA) +//| expander = i2cioexpander.IOExpander(i2c, 0x20, 8, 0x01, 0x00, 0x03) +//| pin0 = expander.pins[0] +//| pin0.switch_to_output(value=True) +//| """ + +static const mp_rom_map_elem_t i2cioexpander_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_i2cioexpander) }, + { MP_ROM_QSTR(MP_QSTR_IOExpander), MP_ROM_PTR(&i2cioexpander_ioexpander_type) }, +}; + +static MP_DEFINE_CONST_DICT(i2cioexpander_module_globals, i2cioexpander_module_globals_table); + +const mp_obj_module_t i2cioexpander_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&i2cioexpander_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_i2cioexpander, i2cioexpander_module); diff --git a/shared-bindings/i2cioexpander/__init__.h b/shared-bindings/i2cioexpander/__init__.h new file mode 100644 index 0000000000000..e0d0668686859 --- /dev/null +++ b/shared-bindings/i2cioexpander/__init__.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Nothing now. diff --git a/shared-bindings/sdcardio/SDCard.h b/shared-bindings/sdcardio/SDCard.h index 95998f5af5e90..ae364357707fe 100644 --- a/shared-bindings/sdcardio/SDCard.h +++ b/shared-bindings/sdcardio/SDCard.h @@ -7,6 +7,7 @@ #pragma once +#include "py/mperrno.h" #include "shared-module/sdcardio/SDCard.h" extern const mp_obj_type_t sdcardio_SDCard_type; @@ -17,9 +18,9 @@ bool common_hal_sdcardio_sdcard_deinited(sdcardio_sdcard_obj_t *self); void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self); void common_hal_sdcardio_sdcard_mark_deinit(sdcardio_sdcard_obj_t *self); int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self); -int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); -int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self); -int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); +mp_errno_t common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); +mp_errno_t common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self); +mp_errno_t common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); // Used by native vfs blockdev. mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); diff --git a/shared-bindings/sdioio/SDCard.h b/shared-bindings/sdioio/SDCard.h index dfeaf8fca8962..446ff3dfd7618 100644 --- a/shared-bindings/sdioio/SDCard.h +++ b/shared-bindings/sdioio/SDCard.h @@ -7,6 +7,7 @@ #pragma once #include "py/obj.h" +#include "py/mperrno.h" #include "common-hal/microcontroller/Pin.h" #include "common-hal/sdioio/SDCard.h" @@ -35,9 +36,14 @@ uint8_t common_hal_sdioio_sdcard_get_width(sdioio_sdcard_obj_t *self); // Return number of device blocks uint32_t common_hal_sdioio_sdcard_get_count(sdioio_sdcard_obj_t *self); -// Read or write blocks -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); +// Read or write blocks - returns 0 on success or negative error code from mperrno.h +mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); +mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); + +// Used by native vfs blockdev. +mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); +mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value); // This is used by the supervisor to claim SDIO devices indefinitely. extern void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self); diff --git a/shared-bindings/socketpool/Socket.h b/shared-bindings/socketpool/Socket.h index a883ebd505463..295f86e737169 100644 --- a/shared-bindings/socketpool/Socket.h +++ b/shared-bindings/socketpool/Socket.h @@ -6,6 +6,7 @@ #pragma once +#include "py/mperrno.h" #include "common-hal/socketpool/Socket.h" extern const mp_obj_type_t socketpool_socket_type; diff --git a/shared-module/bitbangio/I2C.c b/shared-module/bitbangio/I2C.c index 0089bdc1e1e1e..8e27298af7c0a 100644 --- a/shared-module/bitbangio/I2C.c +++ b/shared-module/bitbangio/I2C.c @@ -8,10 +8,15 @@ #include "py/mperrno.h" #include "py/obj.h" #include "py/runtime.h" +#include "py/gc.h" #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" static void delay(bitbangio_i2c_obj_t *self) { // We need to use an accurate delay to get acceptable I2C @@ -19,61 +24,81 @@ static void delay(bitbangio_i2c_obj_t *self) { common_hal_mcu_delay_us(self->us_delay); } -static void scl_low(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_set_value(&self->scl, false); +static bool scl_low(bitbangio_i2c_obj_t *self) { + return digitalinout_protocol_set_value(self->scl, false) == 0; } -static void scl_release(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_set_value(&self->scl, true); +static bool scl_release(bitbangio_i2c_obj_t *self) { + if (digitalinout_protocol_set_value(self->scl, true) != 0) { + return false; + } uint32_t count = self->us_timeout; delay(self); // For clock stretching, wait for the SCL pin to be released, with timeout. - common_hal_digitalio_digitalinout_switch_to_input(&self->scl, PULL_UP); - for (; !common_hal_digitalio_digitalinout_get_value(&self->scl) && count; --count) { + digitalinout_protocol_switch_to_input(self->scl, PULL_UP); + bool value; + for (; count; --count) { + if (digitalinout_protocol_get_value(self->scl, &value) != 0) { + return false; + } + if (value) { + break; + } common_hal_mcu_delay_us(1); } - common_hal_digitalio_digitalinout_switch_to_output(&self->scl, true, DRIVE_MODE_OPEN_DRAIN); + digitalinout_protocol_switch_to_output(self->scl, true, DRIVE_MODE_OPEN_DRAIN); // raise exception on timeout if (count == 0) { mp_raise_msg_varg(&mp_type_TimeoutError, MP_ERROR_TEXT("%q too long"), MP_QSTR_timeout); } + return true; } -static void sda_low(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_set_value(&self->sda, false); +static bool sda_low(bitbangio_i2c_obj_t *self) { + return digitalinout_protocol_set_value(self->sda, false) == 0; } -static void sda_release(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_set_value(&self->sda, true); +static bool sda_release(bitbangio_i2c_obj_t *self) { + return digitalinout_protocol_set_value(self->sda, true) == 0; } -static bool sda_read(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_switch_to_input(&self->sda, PULL_UP); - bool value = common_hal_digitalio_digitalinout_get_value(&self->sda); - common_hal_digitalio_digitalinout_switch_to_output(&self->sda, true, DRIVE_MODE_OPEN_DRAIN); - return value; +static bool sda_read(bitbangio_i2c_obj_t *self, bool *value) { + digitalinout_protocol_switch_to_input(self->sda, PULL_UP); + if (digitalinout_protocol_get_value(self->sda, value) != 0) { + return false; + } + digitalinout_protocol_switch_to_output(self->sda, true, DRIVE_MODE_OPEN_DRAIN); + return true; } -static void start(bitbangio_i2c_obj_t *self) { - sda_release(self); +static bool start(bitbangio_i2c_obj_t *self) { + if (!sda_release(self)) { + return false; + } delay(self); scl_release(self); sda_low(self); delay(self); + return true; } -static void stop(bitbangio_i2c_obj_t *self) { +static bool stop(bitbangio_i2c_obj_t *self) { delay(self); - sda_low(self); + if (!sda_low(self)) { + return false; + } delay(self); scl_release(self); sda_release(self); delay(self); + return true; } static int write_byte(bitbangio_i2c_obj_t *self, uint8_t val) { delay(self); - scl_low(self); + if (!scl_low(self)) { + return -1; + } for (int i = 7; i >= 0; i--) { if ((val >> i) & 1) { @@ -90,7 +115,10 @@ static int write_byte(bitbangio_i2c_obj_t *self, uint8_t val) { delay(self); scl_release(self); - int ret = sda_read(self); + bool ret; + if (!sda_read(self, &ret)) { + return -1; + } delay(self); scl_low(self); @@ -99,13 +127,17 @@ static int write_byte(bitbangio_i2c_obj_t *self, uint8_t val) { static bool read_byte(bitbangio_i2c_obj_t *self, uint8_t *val, bool ack) { delay(self); - scl_low(self); + if (!scl_low(self)) { + return false; + } delay(self); uint8_t data = 0; for (int i = 7; i >= 0; i--) { scl_release(self); - data = (data << 1) | sda_read(self); + bool bit; + sda_read(self, &bit); + data = (data << 1) | bit; scl_low(self); delay(self); } @@ -124,8 +156,8 @@ static bool read_byte(bitbangio_i2c_obj_t *self, uint8_t *val, bool ack) { } void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self, - const mcu_pin_obj_t *scl, - const mcu_pin_obj_t *sda, + mp_obj_t scl, + mp_obj_t sda, uint32_t frequency, uint32_t us_timeout) { @@ -134,32 +166,39 @@ void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self, if (self->us_delay == 0) { self->us_delay = 1; } - digitalinout_result_t result = common_hal_digitalio_digitalinout_construct(&self->scl, scl); - if (result != DIGITALINOUT_OK) { - return; - } - result = common_hal_digitalio_digitalinout_construct(&self->sda, sda); - if (result != DIGITALINOUT_OK) { - common_hal_digitalio_digitalinout_deinit(&self->scl); - return; - } - common_hal_digitalio_digitalinout_switch_to_output(&self->scl, true, DRIVE_MODE_OPEN_DRAIN); - common_hal_digitalio_digitalinout_switch_to_output(&self->sda, true, DRIVE_MODE_OPEN_DRAIN); - stop(self); + // Convert scl from Pin to DigitalInOutProtocol + self->scl = digitalinout_protocol_from_pin(scl, MP_QSTR_scl, false, false, &self->own_scl); + + // Convert sda from Pin to DigitalInOutProtocol + self->sda = digitalinout_protocol_from_pin(sda, MP_QSTR_sda, false, false, &self->own_sda); + + digitalinout_protocol_switch_to_output(self->scl, true, DRIVE_MODE_OPEN_DRAIN); + digitalinout_protocol_switch_to_output(self->sda, true, DRIVE_MODE_OPEN_DRAIN); + + if (!stop(self)) { + mp_raise_OSError(MP_EIO); + } } bool shared_module_bitbangio_i2c_deinited(bitbangio_i2c_obj_t *self) { // If one is deinited, both will be. - return common_hal_digitalio_digitalinout_deinited(&self->scl); + return digitalinout_protocol_deinited(self->scl); } void shared_module_bitbangio_i2c_deinit(bitbangio_i2c_obj_t *self) { if (shared_module_bitbangio_i2c_deinited(self)) { return; } - common_hal_digitalio_digitalinout_deinit(&self->scl); - common_hal_digitalio_digitalinout_deinit(&self->sda); + // Only deinit and free the pins if we own them + if (self->own_scl) { + digitalinout_protocol_deinit(self->scl); + circuitpy_free_obj(self->scl); + } + if (self->own_sda) { + digitalinout_protocol_deinit(self->sda); + circuitpy_free_obj(self->sda); + } } bool shared_module_bitbangio_i2c_try_lock(bitbangio_i2c_obj_t *self) { @@ -182,18 +221,28 @@ void shared_module_bitbangio_i2c_unlock(bitbangio_i2c_obj_t *self) { } bool shared_module_bitbangio_i2c_probe(bitbangio_i2c_obj_t *self, uint8_t addr) { - start(self); - bool ok = write_byte(self, addr << 1); + if (!start(self)) { + mp_raise_OSError(MP_EIO); + } + int result = write_byte(self, addr << 1); stop(self); - return ok; + if (result < 0) { + mp_raise_OSError(MP_EIO); + } + return result; } uint8_t shared_module_bitbangio_i2c_write(bitbangio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { // start the I2C transaction - start(self); + if (!start(self)) { + return MP_EIO; + } uint8_t status = 0; - if (!write_byte(self, addr << 1)) { + int result = write_byte(self, addr << 1); + if (result < 0) { + status = MP_EIO; + } else if (!result) { status = MP_ENODEV; } @@ -215,7 +264,9 @@ uint8_t shared_module_bitbangio_i2c_write(bitbangio_i2c_obj_t *self, uint16_t ad uint8_t shared_module_bitbangio_i2c_read(bitbangio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { // start the I2C transaction - start(self); + if (!start(self)) { + return MP_EIO; + } uint8_t status = 0; if (!write_byte(self, (addr << 1) | 1)) { status = MP_ENODEV; diff --git a/shared-module/bitbangio/I2C.h b/shared-module/bitbangio/I2C.h index 3908a3dd3740e..0cec9186e9b9d 100644 --- a/shared-module/bitbangio/I2C.h +++ b/shared-module/bitbangio/I2C.h @@ -12,9 +12,11 @@ typedef struct { mp_obj_base_t base; - digitalio_digitalinout_obj_t scl; - digitalio_digitalinout_obj_t sda; + mp_obj_t scl; + mp_obj_t sda; uint32_t us_delay; uint32_t us_timeout; volatile bool locked; + bool own_scl; + bool own_sda; } bitbangio_i2c_obj_t; diff --git a/shared-module/bitbangio/SPI.c b/shared-module/bitbangio/SPI.c index 56e978985edcc..b891d22c9aaaa 100644 --- a/shared-module/bitbangio/SPI.c +++ b/shared-module/bitbangio/SPI.c @@ -7,64 +7,63 @@ #include "py/mpconfig.h" #include "py/obj.h" #include "py/runtime.h" +#include "py/gc.h" #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/bitbangio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" #include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" #define MAX_BAUDRATE (common_hal_mcu_get_clock_frequency() / 48) void shared_module_bitbangio_spi_construct(bitbangio_spi_obj_t *self, - const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *miso) { - digitalinout_result_t result = common_hal_digitalio_digitalinout_construct(&self->clock, clock); - if (result != DIGITALINOUT_OK) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_clock); - } - common_hal_digitalio_digitalinout_switch_to_output(&self->clock, self->polarity == 1, DRIVE_MODE_PUSH_PULL); + mp_obj_t clock, mp_obj_t mosi, mp_obj_t miso) { - if (mosi != NULL) { - result = common_hal_digitalio_digitalinout_construct(&self->mosi, mosi); - if (result != DIGITALINOUT_OK) { - common_hal_digitalio_digitalinout_deinit(&self->clock); - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_mosi); - } - self->has_mosi = true; - common_hal_digitalio_digitalinout_switch_to_output(&self->mosi, false, DRIVE_MODE_PUSH_PULL); - } + // Convert clock from Pin to DigitalInOutProtocol + self->clock = digitalinout_protocol_from_pin(clock, MP_QSTR_clock, false, false, &self->own_clock); + digitalinout_protocol_switch_to_output(self->clock, self->polarity == 1, DRIVE_MODE_PUSH_PULL); - if (miso != NULL) { - // Starts out as input by default, no need to change. - result = common_hal_digitalio_digitalinout_construct(&self->miso, miso); - if (result != DIGITALINOUT_OK) { - common_hal_digitalio_digitalinout_deinit(&self->clock); - if (mosi != NULL) { - common_hal_digitalio_digitalinout_deinit(&self->mosi); - } - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_miso); - } - self->has_miso = true; + // Convert mosi from Pin to DigitalInOutProtocol (optional) + self->mosi = digitalinout_protocol_from_pin(mosi, MP_QSTR_mosi, true, false, &self->own_mosi); + self->has_mosi = (self->mosi != mp_const_none); + if (self->has_mosi) { + digitalinout_protocol_switch_to_output(self->mosi, false, DRIVE_MODE_PUSH_PULL); } + + // Convert miso from Pin to DigitalInOutProtocol (optional) + self->miso = digitalinout_protocol_from_pin(miso, MP_QSTR_miso, true, false, &self->own_miso); + self->has_miso = (self->miso != mp_const_none); + // MISO starts out as input by default, no need to change + self->delay_half = 5; self->polarity = 0; self->phase = 0; } bool shared_module_bitbangio_spi_deinited(bitbangio_spi_obj_t *self) { - return common_hal_digitalio_digitalinout_deinited(&self->clock); + return digitalinout_protocol_deinited(self->clock); } void shared_module_bitbangio_spi_deinit(bitbangio_spi_obj_t *self) { if (shared_module_bitbangio_spi_deinited(self)) { return; } - common_hal_digitalio_digitalinout_deinit(&self->clock); - if (self->has_mosi) { - common_hal_digitalio_digitalinout_deinit(&self->mosi); + // Only deinit and free the pins if we own them + if (self->own_clock) { + digitalinout_protocol_deinit(self->clock); + circuitpy_free_obj(self->clock); + } + if (self->has_mosi && self->own_mosi) { + digitalinout_protocol_deinit(self->mosi); + circuitpy_free_obj(self->mosi); } - if (self->has_miso) { - common_hal_digitalio_digitalinout_deinit(&self->miso); + if (self->has_miso && self->own_miso) { + digitalinout_protocol_deinit(self->miso); + circuitpy_free_obj(self->miso); } } @@ -80,7 +79,7 @@ void shared_module_bitbangio_spi_configure(bitbangio_spi_obj_t *self, // If the polarity has changed, make sure we re-initialize the idle state // of the clock as well. self->polarity = polarity; - common_hal_digitalio_digitalinout_switch_to_output(&self->clock, polarity == 1, DRIVE_MODE_PUSH_PULL); + digitalinout_protocol_switch_to_output(self->clock, polarity == 1, DRIVE_MODE_PUSH_PULL); } self->phase = phase; } @@ -121,9 +120,15 @@ bool shared_module_bitbangio_spi_write(bitbangio_spi_obj_t *self, const uint8_t for (size_t i = 0; i < len; ++i) { uint8_t data_out = data[i]; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + if (i == 0 && j == 0) { + if (digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1) != 0) { + return false; + } + } else { + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); + } + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); } if (dest != NULL) { dest[i] = data_in; @@ -136,16 +141,22 @@ bool shared_module_bitbangio_spi_write(bitbangio_spi_obj_t *self, const uint8_t for (size_t i = 0; i < len; ++i) { uint8_t data_out = data[i]; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); + if (i == 0 && j == 0) { + if (!digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1)) { + return false; + } + } else { + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); + } if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); common_hal_mcu_delay_us(delay_half); } } @@ -176,14 +187,18 @@ bool shared_module_bitbangio_spi_read(bitbangio_spi_obj_t *self, uint8_t *data, if (delay_half <= MICROPY_PY_MACHINE_SPI_MIN_DELAY) { // Clock out zeroes while we read. if (self->has_mosi) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, false); + if (digitalinout_protocol_set_value(self->mosi, false) != 0) { + return false; + } } for (size_t i = 0; i < len; ++i) { uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); - data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); + bool bit; + digitalinout_protocol_get_value(self->miso, &bit); + data_in = (data_in << 1) | bit; + digitalinout_protocol_set_value(self->clock, self->polarity); } data[i] = data_in; } @@ -191,28 +206,32 @@ bool shared_module_bitbangio_spi_read(bitbangio_spi_obj_t *self, uint8_t *data, } #endif if (self->has_mosi) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, false); + if (!digitalinout_protocol_set_value(self->mosi, false)) { + return false; + } } for (size_t i = 0; i < len; ++i) { uint8_t data_out = write_data; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { if (self->has_mosi) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); } if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); common_hal_mcu_delay_us(delay_half); } - data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso); + bool bit; + digitalinout_protocol_get_value(self->miso, &bit); + data_in = (data_in << 1) | bit; if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); common_hal_mcu_delay_us(delay_half); } } @@ -248,10 +267,18 @@ bool shared_module_bitbangio_spi_transfer(bitbangio_spi_obj_t *self, const uint8 uint8_t data_out = dout[i]; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); - data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + if (i == 0 && j == 0) { + if (digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1) != 0) { + return false; + } + } else { + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); + } + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); + bool bit; + digitalinout_protocol_get_value(self->miso, &bit); + data_in = (data_in << 1) | bit; + digitalinout_protocol_set_value(self->clock, self->polarity); } din[i] = data_in; @@ -267,20 +294,28 @@ bool shared_module_bitbangio_spi_transfer(bitbangio_spi_obj_t *self, const uint8 uint8_t data_out = dout[i]; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); + if (i == 0 && j == 0) { + if (!digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1)) { + return false; + } + } else { + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); + } if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); common_hal_mcu_delay_us(delay_half); } - data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso); + bool bit; + digitalinout_protocol_get_value(self->miso, &bit); + data_in = (data_in << 1) | bit; if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); common_hal_mcu_delay_us(delay_half); } } diff --git a/shared-module/bitbangio/SPI.h b/shared-module/bitbangio/SPI.h index c19170af7c46f..5f3c109b7f685 100644 --- a/shared-module/bitbangio/SPI.h +++ b/shared-module/bitbangio/SPI.h @@ -12,13 +12,16 @@ typedef struct { mp_obj_base_t base; - digitalio_digitalinout_obj_t clock; - digitalio_digitalinout_obj_t mosi; - digitalio_digitalinout_obj_t miso; + mp_obj_t clock; + mp_obj_t mosi; + mp_obj_t miso; uint32_t delay_half; bool has_miso : 1; bool has_mosi : 1; uint8_t polarity : 1; uint8_t phase : 1; volatile bool locked : 1; + bool own_clock; + bool own_mosi; + bool own_miso; } bitbangio_spi_obj_t; diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index a9c0e9a6c80cf..6bfc0bbf0f1d9 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -156,6 +156,10 @@ void reset_board_buses(void) { } } #endif + #if CIRCUITPY_I2CIOEXPANDER + // Assume the native I2C bus is used for IO Expander pins. + display_using_i2c = true; + #endif if (i2c_obj_created[instance]) { // make sure I2C lock is not held over a soft reset common_hal_busio_i2c_unlock(&i2c_obj[instance]); diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index ac57f3bf3e019..a4652324c1e4f 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -80,7 +80,7 @@ void common_hal_busdisplay_busdisplay_construct(busdisplay_busdisplay_obj_t *sel memcpy(full_command + 1, data, data_size); self->bus.send(self->bus.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, full_command, data_size + 1); } else { - self->bus.send(self->bus.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, cmd, 1); + self->bus.send(self->bus.bus, DISPLAY_COMMAND, data_size > 0 ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED, cmd, 1); self->bus.send(self->bus.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data, data_size); } displayio_display_bus_end_transaction(&self->bus); diff --git a/shared-module/fourwire/FourWire.c b/shared-module/fourwire/FourWire.c index 0a168fa1563fc..0266401f9ce3a 100644 --- a/shared-module/fourwire/FourWire.c +++ b/shared-module/fourwire/FourWire.c @@ -11,13 +11,15 @@ #include "py/gc.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/time/__init__.h" +#include "supervisor/port.h" void common_hal_fourwire_fourwire_construct(fourwire_fourwire_obj_t *self, - busio_spi_obj_t *spi, const mcu_pin_obj_t *command, - const mcu_pin_obj_t *chip_select, const mcu_pin_obj_t *reset, uint32_t baudrate, + busio_spi_obj_t *spi, mp_obj_t command, + mp_obj_t chip_select, mp_obj_t reset, uint32_t baudrate, uint8_t polarity, uint8_t phase) { self->bus = spi; @@ -27,31 +29,24 @@ void common_hal_fourwire_fourwire_construct(fourwire_fourwire_obj_t *self, self->polarity = polarity; self->phase = phase; - - self->command.base.type = &mp_type_NoneType; - if (command != NULL) { - self->command.base.type = &digitalio_digitalinout_type; - common_hal_digitalio_digitalinout_construct(&self->command, command); - common_hal_digitalio_digitalinout_switch_to_output(&self->command, true, DRIVE_MODE_PUSH_PULL); + self->command = digitalinout_protocol_from_pin(command, MP_QSTR_command, true, false, &self->own_command); + if (self->command != mp_const_none) { + digitalinout_protocol_switch_to_output(self->command, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(command); } - self->reset.base.type = &mp_type_NoneType; - if (reset != NULL) { - self->reset.base.type = &digitalio_digitalinout_type; - common_hal_digitalio_digitalinout_construct(&self->reset, reset); - common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL); + + self->reset = digitalinout_protocol_from_pin(reset, MP_QSTR_reset, true, false, &self->own_reset); + if (self->reset != mp_const_none) { + digitalinout_protocol_switch_to_output(self->reset, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(reset); common_hal_fourwire_fourwire_reset(self); } - self->chip_select.base.type = &mp_type_NoneType; - if (chip_select != NULL) { - self->chip_select.base.type = &digitalio_digitalinout_type; - common_hal_digitalio_digitalinout_construct(&self->chip_select, chip_select); - common_hal_digitalio_digitalinout_switch_to_output(&self->chip_select, true, DRIVE_MODE_PUSH_PULL); + self->chip_select = digitalinout_protocol_from_pin(chip_select, MP_QSTR_chip_select, true, false, &self->own_chip_select); + if (self->chip_select != mp_const_none) { + digitalinout_protocol_switch_to_output(self->chip_select, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(chip_select); } - } void common_hal_fourwire_fourwire_deinit(fourwire_fourwire_obj_t *self) { @@ -59,19 +54,33 @@ void common_hal_fourwire_fourwire_deinit(fourwire_fourwire_obj_t *self) { common_hal_busio_spi_deinit(self->bus); } - common_hal_reset_pin(self->command.pin); - common_hal_reset_pin(self->chip_select.pin); - common_hal_reset_pin(self->reset.pin); + // Only deinit and free the pins if we own them + if (self->command != mp_const_none && self->own_command) { + digitalinout_protocol_deinit(self->command); + circuitpy_free_obj(self->command); + } + if (self->chip_select != mp_const_none && self->own_chip_select) { + digitalinout_protocol_deinit(self->chip_select); + circuitpy_free_obj(self->chip_select); + } + if (self->reset != mp_const_none && self->own_reset) { + digitalinout_protocol_deinit(self->reset); + circuitpy_free_obj(self->reset); + } } bool common_hal_fourwire_fourwire_reset(mp_obj_t obj) { fourwire_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj); - if (self->reset.base.type == &mp_type_NoneType) { + if (self->reset == mp_const_none) { + return false; + } + if (digitalinout_protocol_set_value(self->reset, false) != 0) { return false; } - common_hal_digitalio_digitalinout_set_value(&self->reset, false); common_hal_mcu_delay_us(1000); - common_hal_digitalio_digitalinout_set_value(&self->reset, true); + if (digitalinout_protocol_set_value(self->reset, true) != 0) { + return false; + } common_hal_mcu_delay_us(1000); return true; } @@ -92,16 +101,23 @@ bool common_hal_fourwire_fourwire_begin_transaction(mp_obj_t obj) { } common_hal_busio_spi_configure(self->bus, self->frequency, self->polarity, self->phase, 8); - if (self->chip_select.base.type != &mp_type_NoneType) { - common_hal_digitalio_digitalinout_set_value(&self->chip_select, false); + if (self->chip_select != mp_const_none) { + // IO Expander CS can fail due to an I2C lock. + if (digitalinout_protocol_set_value(self->chip_select, false) != 0) { + common_hal_busio_spi_unlock(self->bus); + return false; + } } return true; } void common_hal_fourwire_fourwire_send(mp_obj_t obj, display_byte_type_t data_type, display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length) { + if (data_length == 0) { + return; + } fourwire_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj); - if (self->command.base.type == &mp_type_NoneType) { + if (self->command == mp_const_none) { // When the data/command pin is not specified, we simulate a 9-bit SPI mode, by // adding a data/command bit to every byte, and then splitting the resulting data back // into 8-bit chunks for transmission. If the length of the data being transmitted @@ -133,24 +149,24 @@ void common_hal_fourwire_fourwire_send(mp_obj_t obj, display_byte_type_t data_ty if (bits > 0) { buffer = buffer << (8 - bits); common_hal_busio_spi_write(self->bus, &buffer, 1); - if (self->chip_select.base.type != &mp_type_NoneType) { + if (self->chip_select != mp_const_none) { // toggle CS to discard superfluous bits - common_hal_digitalio_digitalinout_set_value(&self->chip_select, true); + digitalinout_protocol_set_value(self->chip_select, true); common_hal_mcu_delay_us(1); - common_hal_digitalio_digitalinout_set_value(&self->chip_select, false); + digitalinout_protocol_set_value(self->chip_select, false); } } } else { - common_hal_digitalio_digitalinout_set_value(&self->command, data_type == DISPLAY_DATA); + digitalinout_protocol_set_value(self->command, data_type == DISPLAY_DATA); if (chip_select == CHIP_SELECT_TOGGLE_EVERY_BYTE) { // Toggle chip select after each command byte in case the display driver // IC latches commands based on it. for (size_t i = 0; i < data_length; i++) { common_hal_busio_spi_write(self->bus, &data[i], 1); - if (self->chip_select.base.type != &mp_type_NoneType) { - common_hal_digitalio_digitalinout_set_value(&self->chip_select, true); + if (self->chip_select != mp_const_none) { + digitalinout_protocol_set_value(self->chip_select, true); common_hal_mcu_delay_us(1); - common_hal_digitalio_digitalinout_set_value(&self->chip_select, false); + digitalinout_protocol_set_value(self->chip_select, false); } } } else { @@ -161,8 +177,8 @@ void common_hal_fourwire_fourwire_send(mp_obj_t obj, display_byte_type_t data_ty void common_hal_fourwire_fourwire_end_transaction(mp_obj_t obj) { fourwire_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj); - if (self->chip_select.base.type != &mp_type_NoneType) { - common_hal_digitalio_digitalinout_set_value(&self->chip_select, true); + if (self->chip_select != mp_const_none) { + digitalinout_protocol_set_value(self->chip_select, true); } common_hal_busio_spi_unlock(self->bus); } diff --git a/shared-module/fourwire/FourWire.h b/shared-module/fourwire/FourWire.h index 629a426b3b035..c239a3dfde374 100644 --- a/shared-module/fourwire/FourWire.h +++ b/shared-module/fourwire/FourWire.h @@ -6,6 +6,8 @@ #pragma once +#include + #include "common-hal/busio/SPI.h" #include "common-hal/digitalio/DigitalInOut.h" #include "shared-module/displayio/Group.h" @@ -14,10 +16,13 @@ typedef struct { mp_obj_base_t base; busio_spi_obj_t *bus; busio_spi_obj_t inline_bus; - digitalio_digitalinout_obj_t command; - digitalio_digitalinout_obj_t chip_select; - digitalio_digitalinout_obj_t reset; + mp_obj_t command; + mp_obj_t chip_select; + mp_obj_t reset; uint32_t frequency; uint8_t polarity; uint8_t phase; + bool own_command; + bool own_chip_select; + bool own_reset; } fourwire_fourwire_obj_t; diff --git a/shared-module/i2cioexpander/IOExpander.c b/shared-module/i2cioexpander/IOExpander.c new file mode 100644 index 0000000000000..5146abc66e616 --- /dev/null +++ b/shared-module/i2cioexpander/IOExpander.c @@ -0,0 +1,196 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/i2cioexpander/IOExpander.h" +#include "shared-bindings/i2cioexpander/IOPin.h" + +#include + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/busio/I2C.h" +#include "supervisor/port.h" + +void common_hal_i2cioexpander_ioexpander_construct( + i2cioexpander_ioexpander_obj_t *self, + mp_obj_t i2c, + uint8_t address, + uint8_t num_pins, + uint16_t set_value_reg, + uint16_t get_value_reg, + uint16_t set_direction_reg) { + + // Store the I2C bus + self->i2c = (busio_i2c_obj_t *)i2c; + self->address = address; + self->num_pins = num_pins; + self->output_value = 0; + self->output_mask = 0; + + // Parse optional register addresses + self->has_set_value = (set_value_reg != NO_REGISTER); + if (self->has_set_value) { + self->set_value_reg = set_value_reg; + } + + self->has_get_value = (get_value_reg != NO_REGISTER); + if (self->has_get_value) { + self->get_value_reg = get_value_reg; + } + + self->has_set_direction = (set_direction_reg != NO_REGISTER); + if (self->has_set_direction) { + self->set_direction_reg = set_direction_reg; + } + + bool allocate_in_port_heap = !gc_alloc_possible() || !gc_ptr_on_heap(self); + + // Allocate tuple with space for pin objects in items[] + size_t tuple_size = offsetof(mp_obj_tuple_t, items) + sizeof(mp_obj_t) * num_pins; + mp_obj_tuple_t *pins_tuple = allocate_in_port_heap ? port_malloc(tuple_size, false) : m_malloc(tuple_size); + pins_tuple->base.type = &mp_type_tuple; + pins_tuple->len = num_pins; + + // Create IOPin objects for each pin + size_t pin_size = sizeof(i2cioexpander_iopin_obj_t); + for (uint8_t i = 0; i < num_pins; i++) { + i2cioexpander_iopin_obj_t *pin = allocate_in_port_heap ? port_malloc(pin_size, false) : m_malloc(pin_size); + pin->base.type = &i2cioexpander_iopin_type; + i2cioexpander_iopin_construct(pin, self, i); + pins_tuple->items[i] = MP_OBJ_FROM_PTR(pin); + } + + self->pins = pins_tuple; +} + +void common_hal_i2cioexpander_ioexpander_deinit(i2cioexpander_ioexpander_obj_t *self) { + if (gc_alloc_possible() && !gc_ptr_on_heap(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Cannot deinitialize board IOExpander")); + } + for (uint8_t i = 0; i < self->num_pins; i++) { + circuitpy_free_obj(self->pins->items[i]); + } + circuitpy_free_obj(self->pins); + self->i2c = NULL; +} + +bool common_hal_i2cioexpander_ioexpander_deinited(i2cioexpander_ioexpander_obj_t *self) { + return self->i2c == NULL; +} + +mp_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioexpander_obj_t *self, size_t *value) { + uint8_t buffer[2]; + uint8_t num_bytes = (self->num_pins > 8) ? 2 : 1; + + while (!common_hal_busio_i2c_try_lock(self->i2c)) { + RUN_BACKGROUND_TASKS; + } + + mp_errno_t result; + if (self->has_get_value) { + // Send register address then read + result = common_hal_busio_i2c_write_read( + self->i2c, self->address, &self->get_value_reg, 1, buffer, num_bytes); + } else { + // Read directly without register address + result = common_hal_busio_i2c_read(self->i2c, self->address, buffer, num_bytes); + } + common_hal_busio_i2c_unlock(self->i2c); + + if (result != 0) { + return result; + } + + if (num_bytes == 2) { + *value = buffer[0] | (buffer[1] << 8); + } else { + *value = buffer[0]; + } + return 0; +} + +void common_hal_i2cioexpander_ioexpander_get_output_value(i2cioexpander_ioexpander_obj_t *self, size_t *value) { + *value = self->output_value; +} + +mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_ioexpander_obj_t *self, size_t value) { + uint8_t buffer[5]; + uint8_t num_bytes = 0; + + // Add register address if provided + if (self->has_set_value) { + buffer[num_bytes++] = self->set_value_reg; + } + + size_t current_value = self->output_value; + if (current_value == value) { + return 0; + } + size_t diff = current_value ^ value; + + // Add value byte(s) but only if a high bit is changed + buffer[num_bytes++] = value & 0xFF; + if (self->num_pins > 8 && (diff >> 8) != 0) { + buffer[num_bytes++] = (value >> 8) & 0xFF; + } + if (self->num_pins > 16 && (diff >> 16) != 0) { + buffer[num_bytes++] = (value >> 16) & 0xFF; + } + if (self->num_pins > 24 && (diff >> 24) != 0) { + buffer[num_bytes++] = (value >> 24) & 0xFF; + } + + if (!common_hal_busio_i2c_try_lock(self->i2c)) { + return -MP_EBUSY; + } + + mp_errno_t result = common_hal_busio_i2c_write(self->i2c, self->address, buffer, num_bytes); + common_hal_busio_i2c_unlock(self->i2c); + if (result == 0) { + self->output_value = value; + } + return result; +} + +void common_hal_i2cioexpander_ioexpander_get_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t *mask) { + *mask = self->output_mask; +} + +mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t mask) { + self->output_mask = mask; + + // Only write to device if direction register is provided + if (!self->has_set_direction) { + return 0; + } + + uint8_t buffer[3]; + uint8_t num_bytes = 0; + + // Add register address + buffer[num_bytes++] = self->set_direction_reg; + + // Invert the mask so 0 indicates output. We taken 1 for output to match output enable. + size_t inverted_mask = ~mask; + + // Add mask byte(s) + buffer[num_bytes++] = inverted_mask & 0xFF; + if (self->num_pins > 8) { + buffer[num_bytes++] = (inverted_mask >> 8) & 0xFF; + } + + if (!common_hal_busio_i2c_try_lock(self->i2c)) { + return -MP_EBUSY; + } + + mp_errno_t result = common_hal_busio_i2c_write(self->i2c, self->address, buffer, num_bytes); + common_hal_busio_i2c_unlock(self->i2c); + return result; +} + +mp_obj_t common_hal_i2cioexpander_ioexpander_get_pins(i2cioexpander_ioexpander_obj_t *self) { + return MP_OBJ_FROM_PTR(self->pins); +} diff --git a/shared-module/i2cioexpander/IOExpander.h b/shared-module/i2cioexpander/IOExpander.h new file mode 100644 index 0000000000000..e225bfc75c778 --- /dev/null +++ b/shared-module/i2cioexpander/IOExpander.h @@ -0,0 +1,41 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" +#include "py/objtuple.h" +#include "shared-bindings/busio/I2C.h" + +#define NO_REGISTER (0x100) + +typedef struct { + mp_obj_base_t base; + busio_i2c_obj_t *i2c; + uint8_t address; + uint8_t num_pins; + uint8_t set_value_reg; + uint8_t get_value_reg; + uint8_t set_direction_reg; + size_t output_value; + size_t output_mask; + bool has_set_value; + bool has_get_value; + bool has_set_direction; + mp_obj_tuple_t *pins; +} i2cioexpander_ioexpander_obj_t; + +void common_hal_i2cioexpander_ioexpander_construct( + i2cioexpander_ioexpander_obj_t *self, + mp_obj_t i2c, + uint8_t address, + uint8_t num_pins, + uint16_t set_value_reg, + uint16_t get_value_reg, + uint16_t set_direction_reg); diff --git a/shared-module/i2cioexpander/IOPin.c b/shared-module/i2cioexpander/IOPin.c new file mode 100644 index 0000000000000..bf78fe6fbd4b7 --- /dev/null +++ b/shared-module/i2cioexpander/IOPin.c @@ -0,0 +1,147 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/i2cioexpander/IOPin.h" +#include "shared-bindings/i2cioexpander/IOExpander.h" + +#include "py/runtime.h" + +mp_errno_t i2cioexpander_iopin_construct( + i2cioexpander_iopin_obj_t *self, + i2cioexpander_ioexpander_obj_t *expander, + uint8_t pin_number) { + + if (pin_number >= expander->num_pins) { + return MP_EINVAL; // Reusing this for "invalid pin" + } + + self->expander = expander; + self->pin_number = pin_number; + self->direction = DIRECTION_INPUT; + + return 0; +} + +void common_hal_i2cioexpander_iopin_deinit(i2cioexpander_iopin_obj_t *self) { + self->expander = NULL; +} + +bool common_hal_i2cioexpander_iopin_deinited(i2cioexpander_iopin_obj_t *self) { + return self->expander == NULL; +} + +digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_input( + i2cioexpander_iopin_obj_t *self, + digitalio_pull_t pull) { + + if (pull != PULL_NONE) { + // IO expanders typically don't support pull resistors + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL + return DIGITALINOUT_INVALID_PULL; + #endif + } + + self->direction = DIRECTION_INPUT; + + // Clear the output mask bit for this pin + size_t new_mask = self->expander->output_mask & ~(1 << self->pin_number); + common_hal_i2cioexpander_ioexpander_set_output_mask(self->expander, new_mask); + + return DIGITALINOUT_OK; +} + +digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_output( + i2cioexpander_iopin_obj_t *self, + bool value, + digitalio_drive_mode_t drive_mode) { + + if (drive_mode != DRIVE_MODE_PUSH_PULL) { + // IO expanders typically only support push-pull + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE + return DIGITALINOUT_INVALID_DRIVE_MODE; + #endif + } + + self->direction = DIRECTION_OUTPUT; + + // Set the value first + size_t new_value = self->expander->output_value; + if (value) { + new_value |= (1 << self->pin_number); + } else { + new_value &= ~(1 << self->pin_number); + } + common_hal_i2cioexpander_ioexpander_set_output_value(self->expander, new_value); + + // Set the output mask bit for this pin + size_t new_mask = self->expander->output_mask | (1 << self->pin_number); + common_hal_i2cioexpander_ioexpander_set_output_mask(self->expander, new_mask); + + return DIGITALINOUT_OK; +} + +digitalio_direction_t common_hal_i2cioexpander_iopin_get_direction(i2cioexpander_iopin_obj_t *self) { + return self->direction; +} + +mp_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *self, bool value) { + size_t current_value; + common_hal_i2cioexpander_ioexpander_get_output_value(self->expander, ¤t_value); + size_t new_value; + if (value) { + new_value = current_value | (1 << self->pin_number); + } else { + new_value = current_value & ~(1 << self->pin_number); + } + if (new_value != current_value) { + return common_hal_i2cioexpander_ioexpander_set_output_value(self->expander, new_value); + } + return 0; +} + +mp_errno_t common_hal_i2cioexpander_iopin_get_value(i2cioexpander_iopin_obj_t *self, bool *value) { + size_t full_value; + mp_errno_t result = common_hal_i2cioexpander_ioexpander_get_input_value(self->expander, &full_value); + if (result != 0) { + return result; + } + *value = (full_value & (1 << self->pin_number)) != 0; + return 0; +} + +digitalinout_result_t common_hal_i2cioexpander_iopin_set_drive_mode( + i2cioexpander_iopin_obj_t *self, + digitalio_drive_mode_t drive_mode) { + + if (drive_mode != DRIVE_MODE_PUSH_PULL) { + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE + return DIGITALINOUT_INVALID_DRIVE_MODE; + #endif + } + + return DIGITALINOUT_OK; +} + +digitalio_drive_mode_t common_hal_i2cioexpander_iopin_get_drive_mode(i2cioexpander_iopin_obj_t *self) { + return DRIVE_MODE_PUSH_PULL; +} + +digitalinout_result_t common_hal_i2cioexpander_iopin_set_pull( + i2cioexpander_iopin_obj_t *self, + digitalio_pull_t pull) { + + if (pull != PULL_NONE) { + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL + return DIGITALINOUT_INVALID_PULL; + #endif + } + + return DIGITALINOUT_OK; +} + +digitalio_pull_t common_hal_i2cioexpander_iopin_get_pull(i2cioexpander_iopin_obj_t *self) { + return PULL_NONE; +} diff --git a/shared-module/i2cioexpander/IOPin.h b/shared-module/i2cioexpander/IOPin.h new file mode 100644 index 0000000000000..0c4da8a0213fc --- /dev/null +++ b/shared-module/i2cioexpander/IOPin.h @@ -0,0 +1,26 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-module/i2cioexpander/IOExpander.h" + +typedef struct { + mp_obj_base_t base; + i2cioexpander_ioexpander_obj_t *expander; + uint8_t pin_number; + digitalio_direction_t direction; +} i2cioexpander_iopin_obj_t; + +extern const mp_obj_type_t i2cioexpander_iopin_type; diff --git a/shared-module/i2cioexpander/__init__.c b/shared-module/i2cioexpander/__init__.c new file mode 100644 index 0000000000000..43e652db897e0 --- /dev/null +++ b/shared-module/i2cioexpander/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Nothing now. diff --git a/supervisor/port.h b/supervisor/port.h index 0bf8fd8d01350..192307a5f5714 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -115,3 +115,8 @@ bool port_boot_button_pressed(void); void *mp_obj_port_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); mp_obj_t mp_obj_new_port_tuple(size_t n, const mp_obj_t *items); + +// Safely free an object that may have been allocated from either the GC heap or port heap. +// If the pointer is on the GC heap, it will be freed by the GC automatically. +// If the pointer is not on the GC heap, it will be freed with port_free. +void circuitpy_free_obj(mp_obj_t obj); diff --git a/supervisor/shared/port.c b/supervisor/shared/port.c index aabf003fb302d..f908e3f0bd877 100644 --- a/supervisor/shared/port.c +++ b/supervisor/shared/port.c @@ -9,6 +9,7 @@ #include #include "py/runtime.h" +#include "py/gc.h" #include "lib/tlsf/tlsf.h" @@ -56,6 +57,22 @@ MP_WEAK void port_free(void *ptr) { tlsf_free(heap, ptr); } +// Safely free an object that may have been allocated from either the GC heap or port heap. +// If the pointer is on the GC heap, it will be freed by the GC automatically, so we do nothing. +// If the pointer is not on the GC heap (i.e., allocated with port_malloc), we free it with port_free. +// This is safe to call during shutdown when GC may not be available. +void circuitpy_free_obj(mp_obj_t obj) { + if (obj == mp_const_none) { + return; + } + void *ptr = MP_OBJ_TO_PTR(obj); + if (gc_alloc_possible() && gc_ptr_on_heap(ptr)) { + gc_free(ptr); + } else { + port_free(ptr); + } +} + MP_WEAK void *port_realloc(void *ptr, size_t size, bool dma_capable) { return tlsf_realloc(heap, ptr, size); } diff --git a/supervisor/shared/status_leds.c b/supervisor/shared/status_leds.c index ffe8b46b20b7c..ca29fd79212cf 100644 --- a/supervisor/shared/status_leds.c +++ b/supervisor/shared/status_leds.c @@ -133,9 +133,9 @@ void status_led_init(void) { memset(status_apa102_color + 4, 0xff, APA102_BUFFER_LENGTH - 4); #if CIRCUITPY_BITBANG_APA102 shared_module_bitbangio_spi_construct(&status_apa102, - MICROPY_HW_APA102_SCK, - MICROPY_HW_APA102_MOSI, - NULL); + MP_OBJ_FROM_PTR(MICROPY_HW_APA102_SCK), + MP_OBJ_FROM_PTR(MICROPY_HW_APA102_MOSI), + mp_const_none); #else if (!common_hal_busio_spi_deinited(&status_apa102)) { common_hal_busio_spi_deinit(&status_apa102); diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index eea11bd5fb4af..553fe8694c6ea 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -1085,6 +1085,10 @@ static void _reply_static(socketpool_socket_obj_t *socket, _request *request, co "Content-Encoding: gzip\r\n", "Content-Length: ", encoded_len, "\r\n", "Content-Type: ", content_type, "\r\n", + #if CIRCUITPY_DEBUG == 0 + "Cache-Control: max-age=31536000\r\n", // Cache for a year. + "Vary: Accept\r\n", + #endif "\r\n", NULL); web_workflow_send_raw(socket, true, response, response_len); } From 21c96452b3012db15de858289eceeb853e960af2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 23 Jan 2026 16:55:44 -0800 Subject: [PATCH 274/517] Fix codespell --- .../asf4_conf/samd21/hpl_sercom_config.h | 2 +- .../asf4_conf/samd51/hpl_sercom_config.h | 2 +- .../asf4_conf/same51/hpl_sercom_config.h | 2 +- .../asf4_conf/same54/hpl_sercom_config.h | 2 +- ports/atmel-samd/sd_mmc/sd_mmc.c | 14 +++++++------- ports/atmel-samd/sd_mmc/sd_mmc.h | 2 +- .../espressif/boards/sunton_esp32_2432S024C/pins.c | 2 +- .../espressif/boards/sunton_esp32_2432S028/pins.c | 2 +- ports/espressif/common-hal/_bleio/Adapter.c | 2 +- ports/espressif/common-hal/pulseio/PulseOut.c | 2 +- ports/raspberrypi/common-hal/socketpool/Socket.c | 2 +- ports/silabs/common-hal/_bleio/Characteristic.c | 4 ++-- ports/silabs/common-hal/_bleio/Service.c | 8 ++++---- ports/stm/boards/espruino_pico/README.md | 2 +- ports/stm/boards/espruino_wifi/README.MD | 2 +- ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c | 2 +- py/mpz.c | 12 ++++++------ shared-bindings/aurora_epaper/aurora_framebuffer.c | 2 +- shared-bindings/canio/__init__.c | 2 +- shared-bindings/ssl/SSLSocket.c | 2 +- tools/safe_mode_finder.py | 4 ++-- 21 files changed, 37 insertions(+), 37 deletions(-) diff --git a/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h index 5979d1ee9979f..157db0d3f7fbd 100644 --- a/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h @@ -431,7 +431,7 @@ // <0x2=>8x arithmetic // <0x3=>8x fractional // <0x3=>3x -// How many over-sampling bits used when samling data state +// How many over-sampling bits used when sampling data state // usart_arch_sampr #ifndef CONF_SERCOM_2_USART_SAMPR #define CONF_SERCOM_2_USART_SAMPR 0x0 diff --git a/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h index e05560e635823..6dd21277b78d0 100644 --- a/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h @@ -431,7 +431,7 @@ // <0x2=>8x arithmetic // <0x3=>8x fractional // <0x3=>3x -// How many over-sampling bits used when samling data state +// How many over-sampling bits used when sampling data state // usart_arch_sampr #ifndef CONF_SERCOM_2_USART_SAMPR #define CONF_SERCOM_2_USART_SAMPR 0x0 diff --git a/ports/atmel-samd/asf4_conf/same51/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/same51/hpl_sercom_config.h index e05560e635823..6dd21277b78d0 100644 --- a/ports/atmel-samd/asf4_conf/same51/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/same51/hpl_sercom_config.h @@ -431,7 +431,7 @@ // <0x2=>8x arithmetic // <0x3=>8x fractional // <0x3=>3x -// How many over-sampling bits used when samling data state +// How many over-sampling bits used when sampling data state // usart_arch_sampr #ifndef CONF_SERCOM_2_USART_SAMPR #define CONF_SERCOM_2_USART_SAMPR 0x0 diff --git a/ports/atmel-samd/asf4_conf/same54/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/same54/hpl_sercom_config.h index e05560e635823..6dd21277b78d0 100644 --- a/ports/atmel-samd/asf4_conf/same54/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/same54/hpl_sercom_config.h @@ -431,7 +431,7 @@ // <0x2=>8x arithmetic // <0x3=>8x fractional // <0x3=>3x -// How many over-sampling bits used when samling data state +// How many over-sampling bits used when sampling data state // usart_arch_sampr #ifndef CONF_SERCOM_2_USART_SAMPR #define CONF_SERCOM_2_USART_SAMPR 0x0 diff --git a/ports/atmel-samd/sd_mmc/sd_mmc.c b/ports/atmel-samd/sd_mmc/sd_mmc.c index 60b4d9f8985a1..6724e88f42a7a 100644 --- a/ports/atmel-samd/sd_mmc/sd_mmc.c +++ b/ports/atmel-samd/sd_mmc/sd_mmc.c @@ -160,9 +160,9 @@ static struct sd_mmc_card sd_mmc_cards[CONF_SD_MMC_MEM_CNT]; /** HAL driver instance */ static void *sd_mmc_hal; -/** Index of current slot configurated */ +/** Index of current slot configured */ static uint8_t sd_mmc_slot_sel; -/** Pointer on current slot configurated */ +/** Pointer on current slot configured */ static struct sd_mmc_card *sd_mmc_card; /** Number of block to read or write on the current transfer */ static uint16_t sd_mmc_nb_block_to_tranfer = 0; @@ -238,7 +238,7 @@ static bool mmc_mci_op_cond(void) { uint32_t retry, resp; /* - * Timeout 1s = 400KHz / ((6+6)*8) cylces = 4200 retry + * Timeout 1s = 400KHz / ((6+6)*8) cycles = 4200 retry * 6 = cmd byte size * 6 = response byte size */ @@ -277,7 +277,7 @@ static bool sd_mci_op_cond(uint8_t v2) { uint32_t arg, retry, resp; /* - * Timeout 1s = 400KHz / ((6+6+6+6)*8) cylces = 2100 retry + * Timeout 1s = 400KHz / ((6+6+6+6)*8) cycles = 2100 retry * 6 = cmd byte size * 6 = response byte size * 6 = cmd byte size @@ -339,7 +339,7 @@ static bool sdio_op_cond(void) { /* * Wait card ready - * Timeout 1s = 400KHz / ((6+4)*8) cylces = 5000 retry + * Timeout 1s = 400KHz / ((6+4)*8) cycles = 5000 retry * 6 = cmd byte size * 4(SPI) 6(MCI) = response byte size */ @@ -1292,13 +1292,13 @@ static bool sd_mmc_mci_install_mmc(void) { void sd_mmc_init(void *hal, sd_mmc_detect_t *card_detects, sd_mmc_detect_t *wp_detects) { /* GPIO will be used to detect card and write protect. - * The related clocks and pinmux must be configurated in good + * The related clocks and pinmux must be configured in good * condition. */ for (uint8_t slot = 0; slot < CONF_SD_MMC_MEM_CNT; slot++) { sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_NO_CARD; } - sd_mmc_slot_sel = 0xFF; /* No slot configurated */ + sd_mmc_slot_sel = 0xFF; /* No slot configured */ sd_mmc_hal = hal; _cd = card_detects; _wp = wp_detects; diff --git a/ports/atmel-samd/sd_mmc/sd_mmc.h b/ports/atmel-samd/sd_mmc/sd_mmc.h index cdc98e7108e2e..7b70a631164d5 100644 --- a/ports/atmel-samd/sd_mmc/sd_mmc.h +++ b/ports/atmel-samd/sd_mmc/sd_mmc.h @@ -113,7 +113,7 @@ typedef struct sd_mmc_detect { uint16_t val; /**< Detection value */ } sd_mmc_detect_t; -/** This SD MMC stack uses the maximum block size autorized (512 bytes) */ +/** This SD MMC stack uses the maximum block size authorized (512 bytes) */ #define SD_MMC_BLOCK_SIZE 512 /** diff --git a/ports/espressif/boards/sunton_esp32_2432S024C/pins.c b/ports/espressif/boards/sunton_esp32_2432S024C/pins.c index a69f3150e9248..6544017cd999c 100644 --- a/ports/espressif/boards/sunton_esp32_2432S024C/pins.c +++ b/ports/espressif/boards/sunton_esp32_2432S024C/pins.c @@ -46,7 +46,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO5) }, - // ILI9341 dsplay (spi) + // ILI9341 display (spi) { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO13) }, { MP_ROM_QSTR(MP_QSTR_LCD_MISO), MP_ROM_PTR(&pin_GPIO12) }, { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_GPIO14) }, diff --git a/ports/espressif/boards/sunton_esp32_2432S028/pins.c b/ports/espressif/boards/sunton_esp32_2432S028/pins.c index b852ab11e04c2..7791c1685eb38 100644 --- a/ports/espressif/boards/sunton_esp32_2432S028/pins.c +++ b/ports/espressif/boards/sunton_esp32_2432S028/pins.c @@ -45,7 +45,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO5) }, - // ILI9341 dsplay (spi) + // ILI9341 display (spi) { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO13) }, { MP_ROM_QSTR(MP_QSTR_LCD_MISO), MP_ROM_PTR(&pin_GPIO12) }, { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_GPIO14) }, diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c index f604e02c20007..352d01372a91c 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.c +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -384,7 +384,7 @@ static int _connect_event(struct ble_gap_event *event, void *self_in) { // connection and need a new tuple. self->connection_objs = NULL; } else { - // The loop waiting for the connection to be comnpleted will stop when _connection_status changes. + // The loop waiting for the connection to be completed will stop when _connection_status changes. _connection_status = -event->connect.status; } break; diff --git a/ports/espressif/common-hal/pulseio/PulseOut.c b/ports/espressif/common-hal/pulseio/PulseOut.c index 68cb64b5e60e1..8995043b8259c 100644 --- a/ports/espressif/common-hal/pulseio/PulseOut.c +++ b/ports/espressif/common-hal/pulseio/PulseOut.c @@ -69,7 +69,7 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t *self, uint16_t *pu // Circuitpython allows 16 bit pulse values, while ESP32 only allows 15 bits // Thus, we use entire items for one pulse, rather than switching inside each item for (size_t i = 0; i < length; i++) { - // Setting the RMT duration to 0 has undefined behavior, so avoid that pre-emptively. + // Setting the RMT duration to 0 has undefined behavior, so avoid that preemptively. if (pulses[i] == 0) { continue; } diff --git a/ports/raspberrypi/common-hal/socketpool/Socket.c b/ports/raspberrypi/common-hal/socketpool/Socket.c index aeab1498510a0..0543fd53dc3e5 100644 --- a/ports/raspberrypi/common-hal/socketpool/Socket.c +++ b/ports/raspberrypi/common-hal/socketpool/Socket.c @@ -251,7 +251,7 @@ static void _lwip_tcp_err_unaccepted(void *arg, err_t err) { // because it's only ever used by lwIP if tcp_connect is called on the TCP PCB. socketpool_socket_obj_t *socket = (socketpool_socket_obj_t *)pcb->connected; - // Array is not volatile because thiss callback is executed within the lwIP context + // Array is not volatile because this callback is executed within the lwIP context uint8_t alloc = socket->incoming.connection.alloc; struct tcp_pcb **tcp_array = (struct tcp_pcb **)lwip_socket_incoming_array(socket); diff --git a/ports/silabs/common-hal/_bleio/Characteristic.c b/ports/silabs/common-hal/_bleio/Characteristic.c index 37fb792cd4d75..a707b0ef1acc3 100644 --- a/ports/silabs/common-hal/_bleio/Characteristic.c +++ b/ports/silabs/common-hal/_bleio/Characteristic.c @@ -249,7 +249,7 @@ size_t common_hal_bleio_characteristic_get_value( return 0; } -// Get max length of charateristic +// Get max length of characteristic size_t common_hal_bleio_characteristic_get_max_length( bleio_characteristic_obj_t *self) { return self->max_length; @@ -373,7 +373,7 @@ void common_hal_bleio_characteristic_add_descriptor( // This indicates the new added descriptor shall be started. sc = sl_bt_gattdb_start_characteristic(gattdb_session, self->handle); if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Start charateristic fail.")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Start characteristic fail.")); return; } diff --git a/ports/silabs/common-hal/_bleio/Service.c b/ports/silabs/common-hal/_bleio/Service.c index abc8ffe74edff..50f0c4593a67e 100644 --- a/ports/silabs/common-hal/_bleio/Service.c +++ b/ports/silabs/common-hal/_bleio/Service.c @@ -128,7 +128,7 @@ bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { return self->uuid; } -// Get tuple charateristic of service +// Get tuple characteristic of service mp_obj_tuple_t *common_hal_bleio_service_get_characteristics( bleio_service_obj_t *self) { return mp_obj_new_tuple(self->characteristic_list->len, @@ -210,19 +210,19 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, } if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Add charateristic fail.")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Add characteristic fail.")); } sc = sl_bt_gattdb_start_characteristic(gattdb_session, characteristic->handle); if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Start charateristic fail.")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Start characteristic fail.")); return; } sc = sl_bt_gattdb_commit(gattdb_session); if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Commit charateristic fail.")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Commit characteristic fail.")); return; } mp_obj_list_append(self->characteristic_list, diff --git a/ports/stm/boards/espruino_pico/README.md b/ports/stm/boards/espruino_pico/README.md index 86df1ad32a380..d930e9aeca30c 100644 --- a/ports/stm/boards/espruino_pico/README.md +++ b/ports/stm/boards/espruino_pico/README.md @@ -12,4 +12,4 @@ The Espruino Pico is normally updated via a bootloader activated by the Espruino - Restart the board. -To reinstall Espruino, follow the same steps with the latest Espruino Pico binary from espruino.com/binaries. This will reinstall the usual Espruino bootloader. You must un-short the BOOT0/BTN jumper to re-use the original Espruino Bootloader again. If you used a Pencil mark then you may need to use cleaning fluid and a small brush to totally clear out the graphite. +To reinstall Espruino, follow the same steps with the latest Espruino Pico binary from espruino.com/binaries. This will reinstall the usual Espruino bootloader. You must un-short the BOOT0/BTN jumper to reuse the original Espruino Bootloader again. If you used a Pencil mark then you may need to use cleaning fluid and a small brush to totally clear out the graphite. diff --git a/ports/stm/boards/espruino_wifi/README.MD b/ports/stm/boards/espruino_wifi/README.MD index cc78811f1c7e0..cebe99bf6d1a8 100644 --- a/ports/stm/boards/espruino_wifi/README.MD +++ b/ports/stm/boards/espruino_wifi/README.MD @@ -12,4 +12,4 @@ The Espruino Wifi is normally updated via a bootloader activated by the Espruino - Restart the board. -To reinstall Espruino, follow the same steps with the latest Espruino Wifi binary from espruino.com/binaries. This will reinstall the usual Espruino bootloader. You must un-short the BOOT0/BTN jumper to re-use the original Espruino Bootloader again. If you used a Pencil mark then you may need to use cleaning fluid and a small brush to totally clear out the graphite. +To reinstall Espruino, follow the same steps with the latest Espruino Wifi binary from espruino.com/binaries. This will reinstall the usual Espruino bootloader. You must un-short the BOOT0/BTN jumper to reuse the original Espruino Bootloader again. If you used a Pencil mark then you may need to use cleaning fluid and a small brush to totally clear out the graphite. diff --git a/ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c b/ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c index 18fe8b760a267..a4adc58e42a0a 100644 --- a/ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c +++ b/ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c @@ -17,7 +17,7 @@ void stm32_peripherals_gpio_init(void) { __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); - // These ports are not used on the Swan R5 but may need to be enabeld on other boards + // These ports are not used on the Swan R5 but may need to be enabled on other boards // __HAL_RCC_GPIOH_CLK_ENABLE(); // __HAL_RCC_GPIOI_CLK_ENABLE(); diff --git a/py/mpz.c b/py/mpz.c index 7d8bc03ca8610..046d4fcdd5a03 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -782,15 +782,15 @@ void mpz_set_from_float(mpz_t *z, mp_float_t src) { // 2 <= value const int dig_cnt = (adj_exp + 1 + (DIG_SIZE - 1)) / DIG_SIZE; const unsigned int rem = adj_exp % DIG_SIZE; - int dig_ind, shft; + int dig_ind, shift; mp_float_uint_t frc = u.p.frc | ((mp_float_uint_t)1 << MP_FLOAT_FRAC_BITS); if (adj_exp < MP_FLOAT_FRAC_BITS) { - shft = 0; + shift = 0; dig_ind = 0; frc >>= MP_FLOAT_FRAC_BITS - adj_exp; } else { - shft = (rem - MP_FLOAT_FRAC_BITS) % DIG_SIZE; + shift = (rem - MP_FLOAT_FRAC_BITS) % DIG_SIZE; dig_ind = (adj_exp - MP_FLOAT_FRAC_BITS) / DIG_SIZE; } mpz_need_dig(z, dig_cnt); @@ -798,9 +798,9 @@ void mpz_set_from_float(mpz_t *z, mp_float_t src) { if (dig_ind != 0) { memset(z->dig, 0, dig_ind * sizeof(mpz_dig_t)); } - if (shft != 0) { - z->dig[dig_ind++] = (frc << shft) & DIG_MASK; - frc >>= DIG_SIZE - shft; + if (shift != 0) { + z->dig[dig_ind++] = (frc << shift) & DIG_MASK; + frc >>= DIG_SIZE - shift; } #if DIG_SIZE < (MP_FLOAT_FRAC_BITS + 1) while (dig_ind != dig_cnt) { diff --git a/shared-bindings/aurora_epaper/aurora_framebuffer.c b/shared-bindings/aurora_epaper/aurora_framebuffer.c index f1bb169328463..49e0b34a85616 100644 --- a/shared-bindings/aurora_epaper/aurora_framebuffer.c +++ b/shared-bindings/aurora_epaper/aurora_framebuffer.c @@ -64,7 +64,7 @@ //| ) -> None: //| """Create a framebuffer for the Aurora CoG display. //| -//| .. note:: Displays of size 1.9" and 2.6" are not tested, and may exibit unexpected behavior. +//| .. note:: Displays of size 1.9" and 2.6" are not tested, and may exhibit unexpected behavior. //| //| :param busio.SPI spi_bus: The SPI bus that the display is connected to //| :param microcontroller.Pin chip_select: The pin connected to the displays chip select input diff --git a/shared-bindings/canio/__init__.c b/shared-bindings/canio/__init__.c index c65b119ff2401..2300a6652dd6b 100644 --- a/shared-bindings/canio/__init__.c +++ b/shared-bindings/canio/__init__.c @@ -36,7 +36,7 @@ //| //| Other implementations of the CAN device may exist (for instance, attached //| via an SPI bus). If so their constructor arguments may differ, but -//| otherwise we encourage implementors to follow the API that the core uses. +//| otherwise we encourage implementers to follow the API that the core uses. //| //| For more information on working with this module, refer to //| `this Learn Guide on using it `_. diff --git a/shared-bindings/ssl/SSLSocket.c b/shared-bindings/ssl/SSLSocket.c index 92440f9ea30f7..4418a8e48d9c9 100644 --- a/shared-bindings/ssl/SSLSocket.c +++ b/shared-bindings/ssl/SSLSocket.c @@ -106,7 +106,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_connect_obj, ssl_sslsocket_connec //| def listen(self, backlog: int) -> None: //| """Set socket to listen for incoming connections //| -//| :param ~int backlog: length of backlog queue for waiting connetions""" +//| :param ~int backlog: length of backlog queue for waiting connections""" //| ... //| static mp_obj_t ssl_sslsocket_listen(mp_obj_t self_in, mp_obj_t backlog_in) { diff --git a/tools/safe_mode_finder.py b/tools/safe_mode_finder.py index fece85312788f..af3ff0d3c6d7b 100644 --- a/tools/safe_mode_finder.py +++ b/tools/safe_mode_finder.py @@ -86,8 +86,8 @@ print(e.has_dwarf_info()) d = e.get_dwarf_info() # print(dir(d)) - aranges = d.get_aranges() - cu_offset = aranges.cu_offset_at_addr(pc) + arranges = d.get_aranges() + cu_offset = arranges.cu_offset_at_addr(pc) if not cu_offset: cu_offset = 0 main_cu = d.get_CU_at(cu_offset) From 6ead6b432291730535e37aa9d8af308dfc9eefa2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 23 Jan 2026 17:07:11 -0800 Subject: [PATCH 275/517] Fix stubs and tweak IOPin deinit --- shared-bindings/i2cioexpander/IOPin.c | 17 ++++++----------- shared-module/i2cioexpander/IOPin.c | 5 +++-- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/shared-bindings/i2cioexpander/IOPin.c b/shared-bindings/i2cioexpander/IOPin.c index 3e43e18831fde..4a8ca46372bf5 100644 --- a/shared-bindings/i2cioexpander/IOPin.c +++ b/shared-bindings/i2cioexpander/IOPin.c @@ -42,16 +42,12 @@ static inline void check_for_deinit(i2cioexpander_iopin_obj_t *self) { raise_deinited_error(); } } - -//| def deinit(self) -> None: -//| """Turn off the pin and release it for other use.""" -//| ... -static mp_obj_t i2cioexpander_iopin_deinit(mp_obj_t self_in) { - i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_i2cioexpander_iopin_deinit(self); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_deinit_obj, i2cioexpander_iopin_deinit); +//| class IOPin: +//| """Control a single pin on an `IOExpander` in the same way as `DigitalInOut`. +//| +//| Not constructed directly. Get from `IOExpander.pins` instead. +//| """ +//| //| def switch_to_output( //| self, value: bool = False, drive_mode: digitalio.DriveMode = digitalio.DriveMode.PUSH_PULL @@ -310,7 +306,6 @@ static const digitalinout_p_t iopin_digitalinout_p = { static const mp_rom_map_elem_t i2cioexpander_iopin_locals_dict_table[] = { // Methods - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&i2cioexpander_iopin_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_switch_to_input), MP_ROM_PTR(&i2cioexpander_iopin_switch_to_input_obj) }, { MP_ROM_QSTR(MP_QSTR_switch_to_output), MP_ROM_PTR(&i2cioexpander_iopin_switch_to_output_obj) }, diff --git a/shared-module/i2cioexpander/IOPin.c b/shared-module/i2cioexpander/IOPin.c index bf78fe6fbd4b7..9502d2814f3fb 100644 --- a/shared-module/i2cioexpander/IOPin.c +++ b/shared-module/i2cioexpander/IOPin.c @@ -26,11 +26,12 @@ mp_errno_t i2cioexpander_iopin_construct( } void common_hal_i2cioexpander_iopin_deinit(i2cioexpander_iopin_obj_t *self) { - self->expander = NULL; + // Switch to input on deinit. + common_hal_i2cioexpander_iopin_switch_to_input(self, PULL_NONE); } bool common_hal_i2cioexpander_iopin_deinited(i2cioexpander_iopin_obj_t *self) { - return self->expander == NULL; + return self->expander == NULL || common_hal_i2cioexpander_ioexpander_deinited(self->expander); } digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_input( From 482620433c5428dfafee1b0d80a8f32c2a7674b5 Mon Sep 17 00:00:00 2001 From: Tim Walter Date: Sun, 25 Jan 2026 23:32:04 -0500 Subject: [PATCH 276/517] Add missing quote in hashlib.new docstring --- shared-bindings/hashlib/__init__.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/hashlib/__init__.c b/shared-bindings/hashlib/__init__.c index 2a6517791be95..354529cfa77cd 100644 --- a/shared-bindings/hashlib/__init__.c +++ b/shared-bindings/hashlib/__init__.c @@ -20,7 +20,7 @@ //| //| def new(name: str, data: bytes = b"") -> hashlib.Hash: //| """Returns a Hash object setup for the named algorithm. Raises ValueError when the named -//| algorithm is unsupported. Supported algorithms for ``name`` are ``'sha1`` and ``'sha256'``. +//| algorithm is unsupported. Supported algorithms for ``name`` are ``'sha1'`` and ``'sha256'``. //| //| :return: a hash object for the given algorithm //| :rtype: hashlib.Hash""" From bcf7549544c0aeafbe64fd3bc1d5eec3c12008b5 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 26 Jan 2026 09:38:13 -0800 Subject: [PATCH 277/517] Make board optionally mutable --- ports/espressif/boards/yoto_mini_2024/mpconfigboard.h | 2 ++ ports/espressif/boards/yoto_player_v3/mpconfigboard.h | 2 ++ py/circuitpy_mpconfig.h | 4 ++++ shared-bindings/board/__init__.h | 4 ++++ 4 files changed, 12 insertions(+) diff --git a/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h index b5ab8a7e6c2af..4fcc2afaae870 100644 --- a/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h +++ b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h @@ -11,6 +11,8 @@ #define MICROPY_HW_BOARD_NAME "Yoto Mini 2024" #define MICROPY_HW_MCU_NAME "ESP32" +#define CIRCUITPY_MUTABLE_BOARD (1) + #define CIRCUITPY_BOARD_I2C (1) #define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO25, .sda = &pin_GPIO21}} #define CIRCUITPY_BOARD_I2C_SPEED (400000) diff --git a/ports/espressif/boards/yoto_player_v3/mpconfigboard.h b/ports/espressif/boards/yoto_player_v3/mpconfigboard.h index ba53649275cd2..1660c4881f98a 100644 --- a/ports/espressif/boards/yoto_player_v3/mpconfigboard.h +++ b/ports/espressif/boards/yoto_player_v3/mpconfigboard.h @@ -11,6 +11,8 @@ #define MICROPY_HW_BOARD_NAME "Yoto Player V3" #define MICROPY_HW_MCU_NAME "ESP32" +#define CIRCUITPY_MUTABLE_BOARD (1) + #define CIRCUITPY_BOARD_I2C (1) #define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO25, .sda = &pin_GPIO21}} #define CIRCUITPY_BOARD_I2C_SPEED (400000) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index fb4f261ab6f49..6266f75ceae68 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -317,6 +317,10 @@ typedef long mp_off_t; // Default board buses. +#ifndef CIRCUITPY_MUTABLE_BOARD +#define CIRCUITPY_MUTABLE_BOARD (0) +#endif + #ifndef CIRCUITPY_BOARD_I2C #if defined(DEFAULT_I2C_BUS_SCL) && defined(DEFAULT_I2C_BUS_SDA) #define CIRCUITPY_BOARD_I2C (1) diff --git a/shared-bindings/board/__init__.h b/shared-bindings/board/__init__.h index 4466f3924bf3c..4334309973005 100644 --- a/shared-bindings/board/__init__.h +++ b/shared-bindings/board/__init__.h @@ -11,7 +11,11 @@ #include "shared-bindings/microcontroller/Pin.h" // for the pin definitions +#if CIRCUITPY_MUTABLE_BOARD extern mp_obj_dict_t board_module_globals; +#else +extern const mp_obj_dict_t board_module_globals; +#endif static const MP_DEFINE_STR_OBJ(board_module_id_obj, CIRCUITPY_BOARD_ID); mp_obj_t common_hal_board_get_i2c(const mp_int_t instance); From de4311396203ef1e0bb11d9ea3ecefef0c54903d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 26 Jan 2026 11:19:03 -0800 Subject: [PATCH 278/517] Fix other FourWire calls --- ports/atmel-samd/boards/hallowing_m0_express/board.c | 6 +++--- ports/atmel-samd/boards/hallowing_m4_express/board.c | 6 +++--- ports/atmel-samd/boards/monster_m4sk/board.c | 6 +++--- ports/atmel-samd/boards/openbook_m4/board.c | 6 +++--- ports/atmel-samd/boards/pewpew_lcd/board.c | 6 +++--- ports/atmel-samd/boards/pewpew_m4/board.c | 6 +++--- ports/atmel-samd/boards/pybadge/board.c | 6 +++--- ports/atmel-samd/boards/pygamer/board.c | 6 +++--- ports/atmel-samd/boards/seeeduino_wio_terminal/board.c | 6 +++--- ports/atmel-samd/boards/ugame10/board.c | 6 +++--- ports/espressif/boards/adafruit_esp32s3_camera/board.c | 6 +++--- .../boards/adafruit_feather_esp32s2_reverse_tft/board.c | 6 +++--- .../espressif/boards/adafruit_feather_esp32s2_tft/board.c | 6 +++--- .../boards/adafruit_feather_esp32s3_reverse_tft/board.c | 6 +++--- .../espressif/boards/adafruit_feather_esp32s3_tft/board.c | 6 +++--- ports/espressif/boards/adafruit_funhouse/board.c | 6 +++--- .../boards/adafruit_magtag_2.9_grayscale/board.c | 6 +++--- ports/espressif/boards/elecrow_crowpanel_3.5/board.c | 4 ++-- .../espressif/boards/elecrow_crowpanel_4_2_epaper/board.c | 6 +++--- ports/espressif/boards/espressif_esp32s3_box/board.c | 6 +++--- ports/espressif/boards/espressif_esp32s3_box_lite/board.c | 6 +++--- ports/espressif/boards/espressif_esp32s3_eye/board.c | 4 ++-- .../espressif/boards/espressif_esp32s3_usb_otg_n8/board.c | 6 +++--- ports/espressif/boards/hardkernel_odroid_go/board.c | 4 ++-- ports/espressif/boards/heltec_vision_master_e290/board.c | 6 +++--- ports/espressif/boards/heltec_wireless_paper/board.c | 6 +++--- ports/espressif/boards/hexky_s2/board.c | 6 +++--- ports/espressif/boards/hiibot_iots2/board.c | 4 ++-- ports/espressif/boards/lilygo_tdeck/board.c | 4 ++-- ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c | 6 +++--- ports/espressif/boards/lilygo_tdongle_s3/board.c | 6 +++--- ports/espressif/boards/lilygo_tembed_esp32s3/board.c | 6 +++--- ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c | 6 +++--- ports/espressif/boards/lilygo_tqt_pro_psram/board.c | 6 +++--- ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c | 6 +++--- .../boards/lilygo_ttgo_tdisplay_esp32_16m/board.c | 6 +++--- .../boards/lilygo_ttgo_tdisplay_esp32_4m/board.c | 6 +++--- ports/espressif/boards/lilygo_twatch_2020_v3/board.c | 4 ++-- ports/espressif/boards/lilygo_twatch_s3/board.c | 4 ++-- ports/espressif/boards/lolin_s3_mini_pro/board.c | 6 +++--- ports/espressif/boards/m5stack_atoms3/board.c | 6 +++--- ports/espressif/boards/m5stack_cardputer/board.c | 6 +++--- ports/espressif/boards/m5stack_cardputer_ros/board.c | 6 +++--- ports/espressif/boards/m5stack_core2/board.c | 6 +++--- ports/espressif/boards/m5stack_core_basic/board.c | 6 +++--- ports/espressif/boards/m5stack_core_fire/board.c | 6 +++--- ports/espressif/boards/m5stack_cores3/board.c | 6 +++--- ports/espressif/boards/m5stack_dial/board.c | 6 +++--- ports/espressif/boards/m5stack_dinmeter/board.c | 6 +++--- ports/espressif/boards/m5stack_stick_c/board.c | 6 +++--- ports/espressif/boards/m5stack_stick_c_plus/board.c | 6 +++--- ports/espressif/boards/m5stack_stick_c_plus2/board.c | 6 +++--- ports/espressif/boards/morpheans_morphesp-240/board.c | 6 +++--- ports/espressif/boards/oxocard_artwork/board.c | 6 +++--- ports/espressif/boards/oxocard_connect/board.c | 6 +++--- ports/espressif/boards/oxocard_galaxy/board.c | 6 +++--- ports/espressif/boards/oxocard_science/board.c | 6 +++--- ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c | 6 +++--- ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c | 6 +++--- ports/espressif/boards/sqfmi_watchy/board.c | 6 +++--- ports/espressif/boards/sunton_esp32_2424S012/board.c | 6 +++--- ports/espressif/boards/sunton_esp32_2432S024C/board.c | 6 +++--- ports/espressif/boards/sunton_esp32_2432S028/board.c | 6 +++--- ports/espressif/boards/sunton_esp32_2432S032C/board.c | 6 +++--- ports/espressif/boards/vidi_x/board.c | 6 +++--- .../espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c | 6 +++--- .../espressif/boards/waveshare_esp32_s2_pico_lcd/board.c | 6 +++--- ports/espressif/boards/waveshare_esp32_s3_geek/board.c | 6 +++--- .../espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c | 6 +++--- .../espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c | 6 +++--- .../boards/waveshare_esp32_s3_touch_lcd_1_47/board.c | 6 +++--- .../boards/waveshare_esp32_s3_touch_lcd_2/board.c | 6 +++--- .../boards/waveshare_esp32_s3_touch_lcd_2_8/board.c | 6 +++--- ports/nordic/boards/clue_nrf52840_express/board.c | 6 +++--- ports/nordic/boards/hiibot_bluefi/board.c | 6 +++--- ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c | 6 +++--- ports/nordic/boards/ohs2020_badge/board.c | 6 +++--- ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c | 6 +++--- ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c | 8 ++++---- .../boards/bradanlanestudio_explorer_rp2040/board.c | 6 +++--- ports/raspberrypi/boards/hack_club_sprig/board.c | 6 +++--- ports/raspberrypi/boards/heiafr_picomo_v2/board.c | 6 +++--- ports/raspberrypi/boards/heiafr_picomo_v3/board.c | 6 +++--- ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c | 6 +++--- ports/raspberrypi/boards/pajenicko_picopad/board.c | 6 +++--- ports/raspberrypi/boards/pimoroni_badger2040/board.c | 6 +++--- ports/raspberrypi/boards/pimoroni_badger2040w/board.c | 6 +++--- ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c | 6 +++--- ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c | 6 +++--- ports/raspberrypi/boards/pimoroni_picosystem/board.c | 6 +++--- ports/raspberrypi/boards/ugame22/board.c | 6 +++--- ports/raspberrypi/boards/waveshare_rp2040_geek/board.c | 6 +++--- .../raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c | 6 +++--- ports/raspberrypi/boards/waveshare_rp2350_geek/board.c | 6 +++--- .../raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c | 6 +++--- ports/stm/boards/meowbit_v121/board.c | 6 +++--- 96 files changed, 282 insertions(+), 282 deletions(-) diff --git a/ports/atmel-samd/boards/hallowing_m0_express/board.c b/ports/atmel-samd/boards/hallowing_m0_express/board.c index 637129a0e9d5e..fd5ad5485120f 100644 --- a/ports/atmel-samd/boards/hallowing_m0_express/board.c +++ b/ports/atmel-samd/boards/hallowing_m0_express/board.c @@ -54,9 +54,9 @@ void board_init(void) { common_hal_busio_spi_never_reset(spi); common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA28, // Command or data - &pin_PA01, // Chip select - &pin_PA27, // Reset + MP_OBJ_FROM_PTR(&pin_PA28), // Command or data + MP_OBJ_FROM_PTR(&pin_PA01), // Chip select + MP_OBJ_FROM_PTR(&pin_PA27), // Reset 12000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/hallowing_m4_express/board.c b/ports/atmel-samd/boards/hallowing_m4_express/board.c index bfef5fbcd75dd..c7217b70b0a97 100644 --- a/ports/atmel-samd/boards/hallowing_m4_express/board.c +++ b/ports/atmel-samd/boards/hallowing_m4_express/board.c @@ -35,9 +35,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PB31, // TFT_DC Command or data - &pin_PA27, // TFT_CS Chip select - &pin_PB30, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PB31), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PA27), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PB30), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/monster_m4sk/board.c b/ports/atmel-samd/boards/monster_m4sk/board.c index 1d4fd69b7f708..1c143ad92701f 100644 --- a/ports/atmel-samd/boards/monster_m4sk/board.c +++ b/ports/atmel-samd/boards/monster_m4sk/board.c @@ -35,9 +35,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA07, // TFT_DC Command or data - &pin_PA06, // TFT_CS Chip select - &pin_PA04, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PA07), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PA06), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA04), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/openbook_m4/board.c b/ports/atmel-samd/boards/openbook_m4/board.c index fb75cac3a4ee7..ebabfb84e9bfa 100644 --- a/ports/atmel-samd/boards/openbook_m4/board.c +++ b/ports/atmel-samd/boards/openbook_m4/board.c @@ -44,9 +44,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PB05, // EPD_DC Command or data - &pin_PB07, // EPD_CS Chip select - &pin_PA00, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_PB05), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_PB07), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA00), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/pewpew_lcd/board.c b/ports/atmel-samd/boards/pewpew_lcd/board.c index 6a10132e11dc2..d60efd39bf752 100644 --- a/ports/atmel-samd/boards/pewpew_lcd/board.c +++ b/ports/atmel-samd/boards/pewpew_lcd/board.c @@ -54,9 +54,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA19, // TFT_DC Command or data - &pin_PA17, // TFT_CS Chip select - &pin_PA18, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PA19), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PA17), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA18), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/pewpew_m4/board.c b/ports/atmel-samd/boards/pewpew_m4/board.c index 2007d755c694a..ac67b82a3dc7a 100644 --- a/ports/atmel-samd/boards/pewpew_m4/board.c +++ b/ports/atmel-samd/boards/pewpew_m4/board.c @@ -83,9 +83,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA16, // TFT_DC Command or data - &pin_PA11, // TFT_CS Chip select - &pin_PA17, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PA16), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PA11), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA17), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/pybadge/board.c b/ports/atmel-samd/boards/pybadge/board.c index 545918374e270..e938e96e19e3c 100644 --- a/ports/atmel-samd/boards/pybadge/board.c +++ b/ports/atmel-samd/boards/pybadge/board.c @@ -56,9 +56,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PB05, // TFT_DC Command or data - &pin_PB07, // TFT_CS Chip select - &pin_PA00, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PB05), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PB07), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA00), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/pygamer/board.c b/ports/atmel-samd/boards/pygamer/board.c index 53275ac686a15..fcf6aff0e6556 100644 --- a/ports/atmel-samd/boards/pygamer/board.c +++ b/ports/atmel-samd/boards/pygamer/board.c @@ -57,9 +57,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PB05, // TFT_DC Command or data - &pin_PB12, // TFT_CS Chip select - &pin_PA00, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PB05), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PB12), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA00), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c b/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c index d3d1ae3a956ff..1ee80c9878604 100644 --- a/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c +++ b/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c @@ -52,9 +52,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PC06, // TFT_DC Command or data - &pin_PB21, // TFT_CS Chip select - &pin_PC07, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PC06), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PB21), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PC07), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/ugame10/board.c b/ports/atmel-samd/boards/ugame10/board.c index bbf3839c516fb..78d03a3fd9a02 100644 --- a/ports/atmel-samd/boards/ugame10/board.c +++ b/ports/atmel-samd/boards/ugame10/board.c @@ -53,9 +53,9 @@ void board_init(void) { busio_spi_obj_t *spi = common_hal_board_create_spi(0); common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA09, // Command or data - &pin_PA08, // Chip select - NULL, // Reset + MP_OBJ_FROM_PTR(&pin_PA09), // Command or data + MP_OBJ_FROM_PTR(&pin_PA08), // Chip select + MP_OBJ_NULL, // Reset 24000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/adafruit_esp32s3_camera/board.c b/ports/espressif/boards/adafruit_esp32s3_camera/board.c index e0c357f04c80f..d8b0832d3d043 100644 --- a/ports/espressif/boards/adafruit_esp32s3_camera/board.c +++ b/ports/espressif/boards/adafruit_esp32s3_camera/board.c @@ -37,9 +37,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO40, // TFT_DC Command or data - &pin_GPIO39, // TFT_CS Chip select - &pin_GPIO38, // TFT_RESET Reset + MP_OBJ_FROM_PTR(&pin_GPIO40), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO39), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO38), // TFT_RESET Reset 40000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.c index e554be121dbef..1fe3fd6280021 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO40, // DC - &pin_GPIO42, // CS - &pin_GPIO41, // RST + MP_OBJ_FROM_PTR(&pin_GPIO40), // DC + MP_OBJ_FROM_PTR(&pin_GPIO42), // CS + MP_OBJ_FROM_PTR(&pin_GPIO41), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c index 1ebb357aed5bd..22af4caa652b7 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO39, // DC - &pin_GPIO7, // CS - &pin_GPIO40, // RST + MP_OBJ_FROM_PTR(&pin_GPIO39), // DC + MP_OBJ_FROM_PTR(&pin_GPIO7), // CS + MP_OBJ_FROM_PTR(&pin_GPIO40), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/board.c index e554be121dbef..1fe3fd6280021 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO40, // DC - &pin_GPIO42, // CS - &pin_GPIO41, // RST + MP_OBJ_FROM_PTR(&pin_GPIO40), // DC + MP_OBJ_FROM_PTR(&pin_GPIO42), // CS + MP_OBJ_FROM_PTR(&pin_GPIO41), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s3_tft/board.c index e3569d864e73c..587c742a64f36 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s3_tft/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO39, // DC - &pin_GPIO7, // CS - &pin_GPIO40, // RST + MP_OBJ_FROM_PTR(&pin_GPIO39), // DC + MP_OBJ_FROM_PTR(&pin_GPIO7), // CS + MP_OBJ_FROM_PTR(&pin_GPIO40), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/adafruit_funhouse/board.c b/ports/espressif/boards/adafruit_funhouse/board.c index 061a6abf0d8ca..83499f5a49229 100644 --- a/ports/espressif/boards/adafruit_funhouse/board.c +++ b/ports/espressif/boards/adafruit_funhouse/board.c @@ -37,9 +37,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO39, // TFT_DC Command or data - &pin_GPIO40, // TFT_CS Chip select - &pin_GPIO41, // TFT_RESET Reset + MP_OBJ_FROM_PTR(&pin_GPIO39), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO40), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO41), // TFT_RESET Reset 5000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index d37233b7f2420..dc117deee632e 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -206,9 +206,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO7, // EPD_DC Command or data - &pin_GPIO8, // EPD_CS Chip select - &pin_GPIO6, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO7), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO8), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO6), // EPD_RST Reset 4000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/elecrow_crowpanel_3.5/board.c b/ports/espressif/boards/elecrow_crowpanel_3.5/board.c index 3adfb512e3f27..0748e03fce88b 100755 --- a/ports/espressif/boards/elecrow_crowpanel_3.5/board.c +++ b/ports/espressif/boards/elecrow_crowpanel_3.5/board.c @@ -54,8 +54,8 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO2, // TFT_DC Command or data - &pin_GPIO15, // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO2), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO15), // TFT_CS Chip select NULL, // TFT_RST Reset 20000000, // Baudrate 0, // Polarity diff --git a/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c b/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c index 04f5f92f85ba9..c292ea113aa3f 100644 --- a/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c +++ b/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c @@ -56,9 +56,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO46, // EPD_DC Command or data - &pin_GPIO45, // EPD_CS Chip select - &pin_GPIO47, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO46), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO45), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO47), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/espressif_esp32s3_box/board.c b/ports/espressif/boards/espressif_esp32s3_box/board.c index 54edb47bac33d..a27411fd79c16 100644 --- a/ports/espressif/boards/espressif_esp32s3_box/board.c +++ b/ports/espressif/boards/espressif_esp32s3_box/board.c @@ -31,9 +31,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO48, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO48), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/espressif_esp32s3_box_lite/board.c b/ports/espressif/boards/espressif_esp32s3_box_lite/board.c index e675c0f915b6d..48a49e03bc3e6 100644 --- a/ports/espressif/boards/espressif_esp32s3_box_lite/board.c +++ b/ports/espressif/boards/espressif_esp32s3_box_lite/board.c @@ -32,9 +32,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO48, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO48), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/espressif_esp32s3_eye/board.c b/ports/espressif/boards/espressif_esp32s3_eye/board.c index c48c9428cf468..09a05cc72c446 100644 --- a/ports/espressif/boards/espressif_esp32s3_eye/board.c +++ b/ports/espressif/boards/espressif_esp32s3_eye/board.c @@ -56,8 +56,8 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO43, // DC - &pin_GPIO44, // CS + MP_OBJ_FROM_PTR(&pin_GPIO43), // DC + MP_OBJ_FROM_PTR(&pin_GPIO44), // CS NULL, // no reset pin 40000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c b/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c index 94d08bb56c817..3b5aa06b6767e 100644 --- a/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c +++ b/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c @@ -59,9 +59,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO8, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO8), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/hardkernel_odroid_go/board.c b/ports/espressif/boards/hardkernel_odroid_go/board.c index 647fcd740eba3..5a8b6ccaefee9 100644 --- a/ports/espressif/boards/hardkernel_odroid_go/board.c +++ b/ports/espressif/boards/hardkernel_odroid_go/board.c @@ -51,8 +51,8 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO21, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO21), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select NULL, // TFT_RST Reset 40000000, // Baudrate 0, // Polarity diff --git a/ports/espressif/boards/heltec_vision_master_e290/board.c b/ports/espressif/boards/heltec_vision_master_e290/board.c index 9749f5c704881..5a102c7119bd0 100644 --- a/ports/espressif/boards/heltec_vision_master_e290/board.c +++ b/ports/espressif/boards/heltec_vision_master_e290/board.c @@ -54,9 +54,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // EPD_DC Command or data - &pin_GPIO3, // EPD_CS Chip select - &pin_GPIO5, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO3), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO5), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/heltec_wireless_paper/board.c b/ports/espressif/boards/heltec_wireless_paper/board.c index c796a9be8843d..38f6472e085e6 100644 --- a/ports/espressif/boards/heltec_wireless_paper/board.c +++ b/ports/espressif/boards/heltec_wireless_paper/board.c @@ -94,9 +94,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO5, // EPD_DC Command or data - &pin_GPIO4, // EPD_CS Chip select - &pin_GPIO6, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO5), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO4), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO6), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/hexky_s2/board.c b/ports/espressif/boards/hexky_s2/board.c index c1e1801fed659..41f97796be4fb 100644 --- a/ports/espressif/boards/hexky_s2/board.c +++ b/ports/espressif/boards/hexky_s2/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO34, // DC - &pin_GPIO33, // CS - &pin_GPIO41, // RST + MP_OBJ_FROM_PTR(&pin_GPIO34), // DC + MP_OBJ_FROM_PTR(&pin_GPIO33), // CS + MP_OBJ_FROM_PTR(&pin_GPIO41), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/hiibot_iots2/board.c b/ports/espressif/boards/hiibot_iots2/board.c index e9941cbc3685c..e4bff05822c33 100644 --- a/ports/espressif/boards/hiibot_iots2/board.c +++ b/ports/espressif/boards/hiibot_iots2/board.c @@ -62,8 +62,8 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO35, // DC - &pin_GPIO36, // CS + MP_OBJ_FROM_PTR(&pin_GPIO35), // DC + MP_OBJ_FROM_PTR(&pin_GPIO36), // CS NULL, // NO RST ? 40000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/lilygo_tdeck/board.c b/ports/espressif/boards/lilygo_tdeck/board.c index 91a2f6d2ad702..a1008f2173f89 100644 --- a/ports/espressif/boards/lilygo_tdeck/board.c +++ b/ports/espressif/boards/lilygo_tdeck/board.c @@ -32,8 +32,8 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO11, // TFT_DC Command or data - &pin_GPIO12, // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO11), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO12), // TFT_CS Chip select NULL, // TFT_RST Reset 60000000, // Baudrate 0, // Polarity diff --git a/ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c b/ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c index ede20df200fa9..0782e2161ca8a 100644 --- a/ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c +++ b/ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c @@ -34,9 +34,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO9, // TFT_DC Command or data - &pin_GPIO39, // TFT_CS Chip select - &pin_GPIO47, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO39), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO47), // TFT_RST Reset 40000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/lilygo_tdongle_s3/board.c b/ports/espressif/boards/lilygo_tdongle_s3/board.c index 8907dc7fe9828..ede75a5842e35 100644 --- a/ports/espressif/boards/lilygo_tdongle_s3/board.c +++ b/ports/espressif/boards/lilygo_tdongle_s3/board.c @@ -60,9 +60,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO2, // DC - &pin_GPIO4, // CS - &pin_GPIO1, // RST + MP_OBJ_FROM_PTR(&pin_GPIO2), // DC + MP_OBJ_FROM_PTR(&pin_GPIO4), // CS + MP_OBJ_FROM_PTR(&pin_GPIO1), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/lilygo_tembed_esp32s3/board.c b/ports/espressif/boards/lilygo_tembed_esp32s3/board.c index 698e6980eceef..46e2ecf863c19 100644 --- a/ports/espressif/boards/lilygo_tembed_esp32s3/board.c +++ b/ports/espressif/boards/lilygo_tembed_esp32s3/board.c @@ -32,9 +32,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO13, // TFT_DC Command or data - &pin_GPIO10, // TFT_CS Chip select - &pin_GPIO9, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO13), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO10), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c b/ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c index c7e711aee66a5..fe72222a22cbe 100644 --- a/ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c +++ b/ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO6, // DC - &pin_GPIO5, // CS - &pin_GPIO1, // RST + MP_OBJ_FROM_PTR(&pin_GPIO6), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO1), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/lilygo_tqt_pro_psram/board.c b/ports/espressif/boards/lilygo_tqt_pro_psram/board.c index c7e711aee66a5..fe72222a22cbe 100644 --- a/ports/espressif/boards/lilygo_tqt_pro_psram/board.c +++ b/ports/espressif/boards/lilygo_tqt_pro_psram/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO6, // DC - &pin_GPIO5, // CS - &pin_GPIO1, // RST + MP_OBJ_FROM_PTR(&pin_GPIO6), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO1), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c b/ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c index 2b77e24b91e7b..6fcfb5c19a479 100644 --- a/ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c +++ b/ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c @@ -62,9 +62,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO37, // DC - &pin_GPIO34, // CS - &pin_GPIO38, // RST + MP_OBJ_FROM_PTR(&pin_GPIO37), // DC + MP_OBJ_FROM_PTR(&pin_GPIO34), // CS + MP_OBJ_FROM_PTR(&pin_GPIO38), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_16m/board.c b/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_16m/board.c index 97574398ca2c1..8ed5462a2d364 100644 --- a/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_16m/board.c +++ b/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_16m/board.c @@ -42,9 +42,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO16, // DC - &pin_GPIO5, // CS - &pin_GPIO23, // RST + MP_OBJ_FROM_PTR(&pin_GPIO16), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO23), // RST 24000000, // baudrate (default from the driver) // 40000000, 0, // polarity diff --git a/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_4m/board.c b/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_4m/board.c index 2d3cb64d4559c..a90cc5eb66adb 100644 --- a/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_4m/board.c +++ b/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_4m/board.c @@ -41,9 +41,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO16, // DC - &pin_GPIO5, // CS - &pin_GPIO23, // RST + MP_OBJ_FROM_PTR(&pin_GPIO16), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO23), // RST 24000000, // baudrate (default from the driver) // 40000000, 0, // polarity diff --git a/ports/espressif/boards/lilygo_twatch_2020_v3/board.c b/ports/espressif/boards/lilygo_twatch_2020_v3/board.c index 266515c2338e6..e88a82ea24f5b 100644 --- a/ports/espressif/boards/lilygo_twatch_2020_v3/board.c +++ b/ports/espressif/boards/lilygo_twatch_2020_v3/board.c @@ -46,8 +46,8 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO5, // CS + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS NULL, // RST 24000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/lilygo_twatch_s3/board.c b/ports/espressif/boards/lilygo_twatch_s3/board.c index 51f5933d177ec..801914a4991c0 100644 --- a/ports/espressif/boards/lilygo_twatch_s3/board.c +++ b/ports/espressif/boards/lilygo_twatch_s3/board.c @@ -108,8 +108,8 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO38, // DC - &pin_GPIO12, // CS + MP_OBJ_FROM_PTR(&pin_GPIO38), // DC + MP_OBJ_FROM_PTR(&pin_GPIO12), // CS NULL, // RST 40000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/lolin_s3_mini_pro/board.c b/ports/espressif/boards/lolin_s3_mini_pro/board.c index cd5a083dbdba1..39e645bc7b553 100644 --- a/ports/espressif/boards/lolin_s3_mini_pro/board.c +++ b/ports/espressif/boards/lolin_s3_mini_pro/board.c @@ -39,9 +39,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO36, // DC - &pin_GPIO35, // CS - &pin_GPIO34, // RST + MP_OBJ_FROM_PTR(&pin_GPIO36), // DC + MP_OBJ_FROM_PTR(&pin_GPIO35), // CS + MP_OBJ_FROM_PTR(&pin_GPIO34), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_atoms3/board.c b/ports/espressif/boards/m5stack_atoms3/board.c index df620976b61b4..3dbd8a1a2b8b6 100644 --- a/ports/espressif/boards/m5stack_atoms3/board.c +++ b/ports/espressif/boards/m5stack_atoms3/board.c @@ -39,9 +39,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO33, // DC - &pin_GPIO15, // CS - &pin_GPIO34, // RST + MP_OBJ_FROM_PTR(&pin_GPIO33), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO34), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_cardputer/board.c b/ports/espressif/boards/m5stack_cardputer/board.c index 6fdc2f8ea5c46..00630cd36d291 100644 --- a/ports/espressif/boards/m5stack_cardputer/board.c +++ b/ports/espressif/boards/m5stack_cardputer/board.c @@ -50,9 +50,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO34, // DC - &pin_GPIO37, // CS - &pin_GPIO33, // RST + MP_OBJ_FROM_PTR(&pin_GPIO34), // DC + MP_OBJ_FROM_PTR(&pin_GPIO37), // CS + MP_OBJ_FROM_PTR(&pin_GPIO33), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_cardputer_ros/board.c b/ports/espressif/boards/m5stack_cardputer_ros/board.c index 6fdc2f8ea5c46..00630cd36d291 100644 --- a/ports/espressif/boards/m5stack_cardputer_ros/board.c +++ b/ports/espressif/boards/m5stack_cardputer_ros/board.c @@ -50,9 +50,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO34, // DC - &pin_GPIO37, // CS - &pin_GPIO33, // RST + MP_OBJ_FROM_PTR(&pin_GPIO34), // DC + MP_OBJ_FROM_PTR(&pin_GPIO37), // CS + MP_OBJ_FROM_PTR(&pin_GPIO33), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_core2/board.c b/ports/espressif/boards/m5stack_core2/board.c index 9d5c2bd650a51..2d63436235af1 100644 --- a/ports/espressif/boards/m5stack_core2/board.c +++ b/ports/espressif/boards/m5stack_core2/board.c @@ -318,9 +318,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO15, // DC - &pin_GPIO5, // CS - NULL, // RST + MP_OBJ_FROM_PTR(&pin_GPIO15), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_NULL, // RST 32000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_core_basic/board.c b/ports/espressif/boards/m5stack_core_basic/board.c index 4ae128817a116..5a18d33e52767 100644 --- a/ports/espressif/boards/m5stack_core_basic/board.c +++ b/ports/espressif/boards/m5stack_core_basic/board.c @@ -42,9 +42,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO14, // CS - &pin_GPIO33, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO14), // CS + MP_OBJ_FROM_PTR(&pin_GPIO33), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_core_fire/board.c b/ports/espressif/boards/m5stack_core_fire/board.c index 4ae128817a116..5a18d33e52767 100644 --- a/ports/espressif/boards/m5stack_core_fire/board.c +++ b/ports/espressif/boards/m5stack_core_fire/board.c @@ -42,9 +42,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO14, // CS - &pin_GPIO33, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO14), // CS + MP_OBJ_FROM_PTR(&pin_GPIO33), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_cores3/board.c b/ports/espressif/boards/m5stack_cores3/board.c index f49d634999730..c14be4ae0d142 100644 --- a/ports/espressif/boards/m5stack_cores3/board.c +++ b/ports/espressif/boards/m5stack_cores3/board.c @@ -45,9 +45,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO35, // DC - &pin_GPIO3, // CS - NULL, // RST + MP_OBJ_FROM_PTR(&pin_GPIO35), // DC + MP_OBJ_FROM_PTR(&pin_GPIO3), // CS + MP_OBJ_NULL, // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_dial/board.c b/ports/espressif/boards/m5stack_dial/board.c index 4e7cf4b0240a9..6214255612bd2 100644 --- a/ports/espressif/boards/m5stack_dial/board.c +++ b/ports/espressif/boards/m5stack_dial/board.c @@ -49,9 +49,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO4, // DC - &pin_GPIO7, // CS - &pin_GPIO8, // RST + MP_OBJ_FROM_PTR(&pin_GPIO4), // DC + MP_OBJ_FROM_PTR(&pin_GPIO7), // CS + MP_OBJ_FROM_PTR(&pin_GPIO8), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_dinmeter/board.c b/ports/espressif/boards/m5stack_dinmeter/board.c index cd49318fd1cec..37fafa994dc4d 100644 --- a/ports/espressif/boards/m5stack_dinmeter/board.c +++ b/ports/espressif/boards/m5stack_dinmeter/board.c @@ -51,9 +51,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO4, // DC - &pin_GPIO7, // CS - &pin_GPIO8, // RST + MP_OBJ_FROM_PTR(&pin_GPIO4), // DC + MP_OBJ_FROM_PTR(&pin_GPIO7), // CS + MP_OBJ_FROM_PTR(&pin_GPIO8), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_stick_c/board.c b/ports/espressif/boards/m5stack_stick_c/board.c index 641d2d1db7e78..491ddead86035 100644 --- a/ports/espressif/boards/m5stack_stick_c/board.c +++ b/ports/espressif/boards/m5stack_stick_c/board.c @@ -154,9 +154,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO23, // DC - &pin_GPIO5, // CS - &pin_GPIO18, // RST + MP_OBJ_FROM_PTR(&pin_GPIO23), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO18), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_stick_c_plus/board.c b/ports/espressif/boards/m5stack_stick_c_plus/board.c index eec6a20826030..8902521db8e3f 100644 --- a/ports/espressif/boards/m5stack_stick_c_plus/board.c +++ b/ports/espressif/boards/m5stack_stick_c_plus/board.c @@ -154,9 +154,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO23, // DC - &pin_GPIO5, // CS - &pin_GPIO18, // RST + MP_OBJ_FROM_PTR(&pin_GPIO23), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO18), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/board.c b/ports/espressif/boards/m5stack_stick_c_plus2/board.c index a50e38df09e46..d84faa1888d21 100644 --- a/ports/espressif/boards/m5stack_stick_c_plus2/board.c +++ b/ports/espressif/boards/m5stack_stick_c_plus2/board.c @@ -50,9 +50,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO14, // DC - &pin_GPIO5, // CS - &pin_GPIO12, // RST + MP_OBJ_FROM_PTR(&pin_GPIO14), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/morpheans_morphesp-240/board.c b/ports/espressif/boards/morpheans_morphesp-240/board.c index 5b1c3f82bbbdb..44266cce6ed9f 100644 --- a/ports/espressif/boards/morpheans_morphesp-240/board.c +++ b/ports/espressif/boards/morpheans_morphesp-240/board.c @@ -138,9 +138,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO14, // DC - &pin_GPIO10, // CS - &pin_GPIO9, // RST + MP_OBJ_FROM_PTR(&pin_GPIO14), // DC + MP_OBJ_FROM_PTR(&pin_GPIO10), // CS + MP_OBJ_FROM_PTR(&pin_GPIO9), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/oxocard_artwork/board.c b/ports/espressif/boards/oxocard_artwork/board.c index a27cdb7003bdc..5fe8e8145583a 100644 --- a/ports/espressif/boards/oxocard_artwork/board.c +++ b/ports/espressif/boards/oxocard_artwork/board.c @@ -43,9 +43,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO15, // CS - &pin_GPIO4, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO4), // RST 24000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/oxocard_connect/board.c b/ports/espressif/boards/oxocard_connect/board.c index 89be45c82262e..2c01381147437 100644 --- a/ports/espressif/boards/oxocard_connect/board.c +++ b/ports/espressif/boards/oxocard_connect/board.c @@ -44,9 +44,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO15, // CS - &pin_GPIO4, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO4), // RST 24000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/oxocard_galaxy/board.c b/ports/espressif/boards/oxocard_galaxy/board.c index a27cdb7003bdc..5fe8e8145583a 100644 --- a/ports/espressif/boards/oxocard_galaxy/board.c +++ b/ports/espressif/boards/oxocard_galaxy/board.c @@ -43,9 +43,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO15, // CS - &pin_GPIO4, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO4), // RST 24000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/oxocard_science/board.c b/ports/espressif/boards/oxocard_science/board.c index a27cdb7003bdc..5fe8e8145583a 100644 --- a/ports/espressif/boards/oxocard_science/board.c +++ b/ports/espressif/boards/oxocard_science/board.c @@ -43,9 +43,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO15, // CS - &pin_GPIO4, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO4), // RST 24000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c b/ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c index 89bcbf6b8dd7f..f4288ae45ace5 100644 --- a/ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c +++ b/ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c @@ -42,9 +42,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO0, // DC - &pin_GPIO2, // CS - &pin_GPIO5, // RST + MP_OBJ_FROM_PTR(&pin_GPIO0), // DC + MP_OBJ_FROM_PTR(&pin_GPIO2), // CS + MP_OBJ_FROM_PTR(&pin_GPIO5), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c b/ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c index 6299402e8e07a..30879e714045d 100644 --- a/ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c +++ b/ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c @@ -39,9 +39,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO2, // DC - &pin_GPIO3, // CS - &pin_GPIO10, // RST + MP_OBJ_FROM_PTR(&pin_GPIO2), // DC + MP_OBJ_FROM_PTR(&pin_GPIO3), // CS + MP_OBJ_FROM_PTR(&pin_GPIO10), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/sqfmi_watchy/board.c b/ports/espressif/boards/sqfmi_watchy/board.c index 297190b53e200..9cea4e9809598 100644 --- a/ports/espressif/boards/sqfmi_watchy/board.c +++ b/ports/espressif/boards/sqfmi_watchy/board.c @@ -150,9 +150,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO10, // EPD_DC Command or data - &pin_GPIO5, // EPD_CS Chip select - &pin_GPIO9, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO10), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO9), // EPD_RST Reset 2000000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/espressif/boards/sunton_esp32_2424S012/board.c b/ports/espressif/boards/sunton_esp32_2424S012/board.c index c313ded863764..731694a4e1b8a 100644 --- a/ports/espressif/boards/sunton_esp32_2424S012/board.c +++ b/ports/espressif/boards/sunton_esp32_2424S012/board.c @@ -105,9 +105,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO2, // DC - &pin_GPIO10, // CS - NULL, // RST + MP_OBJ_FROM_PTR(&pin_GPIO2), // DC + MP_OBJ_FROM_PTR(&pin_GPIO10), // CS + MP_OBJ_NULL, // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/sunton_esp32_2432S024C/board.c b/ports/espressif/boards/sunton_esp32_2432S024C/board.c index 8b4201d88e81d..a43b5ad65a032 100644 --- a/ports/espressif/boards/sunton_esp32_2432S024C/board.c +++ b/ports/espressif/boards/sunton_esp32_2432S024C/board.c @@ -50,9 +50,9 @@ static void display_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO2, // TFT_DC Command or data - &pin_GPIO15, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO2), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO15), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 6000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/sunton_esp32_2432S028/board.c b/ports/espressif/boards/sunton_esp32_2432S028/board.c index b249c45dd8fa2..7380e252410ac 100644 --- a/ports/espressif/boards/sunton_esp32_2432S028/board.c +++ b/ports/espressif/boards/sunton_esp32_2432S028/board.c @@ -50,9 +50,9 @@ static void display_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO2, // TFT_DC Command or data - &pin_GPIO15, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO2), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO15), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 6000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/sunton_esp32_2432S032C/board.c b/ports/espressif/boards/sunton_esp32_2432S032C/board.c index b2160e0aa4f34..48e206040d819 100644 --- a/ports/espressif/boards/sunton_esp32_2432S032C/board.c +++ b/ports/espressif/boards/sunton_esp32_2432S032C/board.c @@ -43,9 +43,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO2, // TFT_DC - &pin_GPIO15, // TFT_CS - NULL, // TFT_RST + MP_OBJ_FROM_PTR(&pin_GPIO2), // TFT_DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // TFT_CS + MP_OBJ_NULL, // TFT_RST 26600000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/espressif/boards/vidi_x/board.c b/ports/espressif/boards/vidi_x/board.c index e53dfd82ad9de..7ba7094b91778 100644 --- a/ports/espressif/boards/vidi_x/board.c +++ b/ports/espressif/boards/vidi_x/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO21, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO21), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 40000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c b/ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c index 7dd0e0c0fa2ab..e56c096e40371 100644 --- a/ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c +++ b/ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c @@ -48,9 +48,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO15, // DC - &pin_GPIO14, // CS - &pin_GPIO21, // RST + MP_OBJ_FROM_PTR(&pin_GPIO15), // DC + MP_OBJ_FROM_PTR(&pin_GPIO14), // CS + MP_OBJ_FROM_PTR(&pin_GPIO21), // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s2_pico_lcd/board.c b/ports/espressif/boards/waveshare_esp32_s2_pico_lcd/board.c index cb6fb3103382e..0bf710ec34c69 100644 --- a/ports/espressif/boards/waveshare_esp32_s2_pico_lcd/board.c +++ b/ports/espressif/boards/waveshare_esp32_s2_pico_lcd/board.c @@ -61,9 +61,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO18, // DC - &pin_GPIO9, // CS - &pin_GPIO21, // RST + MP_OBJ_FROM_PTR(&pin_GPIO18), // DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // CS + MP_OBJ_FROM_PTR(&pin_GPIO21), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_geek/board.c b/ports/espressif/boards/waveshare_esp32_s3_geek/board.c index 414188fb46a52..0ce447532642c 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_geek/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_geek/board.c @@ -38,9 +38,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // TFT_DC - &pin_GPIO10, // TFT_CS - &pin_GPIO9, // TFT_RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // TFT_DC + MP_OBJ_FROM_PTR(&pin_GPIO10), // TFT_CS + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_RST 50000000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c b/ports/espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c index 36990c8556691..ee47ccb0bc96e 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c @@ -105,9 +105,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // DC - &pin_GPIO9, // CS - &pin_GPIO12, // RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c b/ports/espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c index f3dd38522f74f..4ecf87948ad04 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c @@ -48,9 +48,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO41, // DC - &pin_GPIO42, // CS - &pin_GPIO39, // RST + MP_OBJ_FROM_PTR(&pin_GPIO41), // DC + MP_OBJ_FROM_PTR(&pin_GPIO42), // CS + MP_OBJ_FROM_PTR(&pin_GPIO39), // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c index 4c037ce8df057..1bdd74e77ee68 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c @@ -94,9 +94,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO45, // DC - &pin_GPIO21, // CS - &pin_GPIO40, // RST + MP_OBJ_FROM_PTR(&pin_GPIO45), // DC + MP_OBJ_FROM_PTR(&pin_GPIO21), // CS + MP_OBJ_FROM_PTR(&pin_GPIO40), // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2/board.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2/board.c index e7a6b86440ee5..71e2d745623b9 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2/board.c @@ -34,9 +34,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO42, // DC - &pin_GPIO45, // CS - &pin_GPIO0, // RST + MP_OBJ_FROM_PTR(&pin_GPIO42), // DC + MP_OBJ_FROM_PTR(&pin_GPIO45), // CS + MP_OBJ_FROM_PTR(&pin_GPIO0), // RST // 24000000, 40000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c index deb764b80a5f7..49177f48fe7d9 100755 --- a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c @@ -34,9 +34,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO41, // DC - &pin_GPIO42, // CS - &pin_GPIO39, // RST + MP_OBJ_FROM_PTR(&pin_GPIO41), // DC + MP_OBJ_FROM_PTR(&pin_GPIO42), // CS + MP_OBJ_FROM_PTR(&pin_GPIO39), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/nordic/boards/clue_nrf52840_express/board.c b/ports/nordic/boards/clue_nrf52840_express/board.c index e2cd8a324bab7..0b331b63a9612 100644 --- a/ports/nordic/boards/clue_nrf52840_express/board.c +++ b/ports/nordic/boards/clue_nrf52840_express/board.c @@ -34,9 +34,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_P0_13, // TFT_DC Command or data - &pin_P0_12, // TFT_CS Chip select - &pin_P1_03, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_P0_13), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_P0_12), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_P1_03), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/nordic/boards/hiibot_bluefi/board.c b/ports/nordic/boards/hiibot_bluefi/board.c index 73430d675eb2d..7a681140ddbc4 100644 --- a/ports/nordic/boards/hiibot_bluefi/board.c +++ b/ports/nordic/boards/hiibot_bluefi/board.c @@ -34,9 +34,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_P0_27, // TFT_DC Command or data - &pin_P0_05, // TFT_CS Chip select - NULL, // no TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_P0_27), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_P0_05), // TFT_CS Chip select + MP_OBJ_NULL, // no TFT_RST Reset // &pin_P1_14, // TFT_RST Reset 60000000, // Baudrate 0, // Polarity diff --git a/ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c b/ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c index 64199b98009f0..4f9c051478ac3 100644 --- a/ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c +++ b/ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c @@ -35,9 +35,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_P0_08, // TFT_DC Command or data - &pin_P0_06, // TFT_CS Chip select - &pin_P1_09, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_P0_08), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_P0_06), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_P1_09), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/nordic/boards/ohs2020_badge/board.c b/ports/nordic/boards/ohs2020_badge/board.c index ea716006cb4bf..d16d99473f014 100644 --- a/ports/nordic/boards/ohs2020_badge/board.c +++ b/ports/nordic/boards/ohs2020_badge/board.c @@ -34,9 +34,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_P0_08, // TFT_DC Command or data - &pin_P0_14, // TFT_CS Chip select - &pin_P0_13, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_P0_08), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_P0_14), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_P0_13), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c b/ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c index 5056a4d9c7a15..882bc9a1a9e0b 100644 --- a/ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c +++ b/ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c @@ -34,9 +34,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - CIRCUITPY_BOARD_TFT_DC, - CIRCUITPY_BOARD_TFT_CS, - NULL, // TFT_RESET Reset + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_DC), + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_CS), + MP_OBJ_NULL, // TFT_RESET Reset 40000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c b/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c index 673d9303d6b55..ec37b398e229f 100644 --- a/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c +++ b/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c @@ -29,7 +29,7 @@ uint8_t display_init_sequence[] = { 0xda, 1, 0x12, // com pins 0x81, 1, 0xff, // contrast 255 0xd9, 1, 0x1f, // pre/dis-charge 2DCLKs/2CLKs - 0xdb, 1, 0x20, // VCOM deslect 0.770 + 0xdb, 1, 0x20, // VCOM select 0.770 0x20, 1, 0x20, 0x33, 0, // VPP 9V 0xa6, 0, // not inverted @@ -46,9 +46,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO24, // Command or data - &pin_GPIO22, // Chip select - &pin_GPIO23, // Reset + MP_OBJ_FROM_PTR(&pin_GPIO24), // Command or data + MP_OBJ_FROM_PTR(&pin_GPIO22), // Chip select + MP_OBJ_FROM_PTR(&pin_GPIO23), // Reset 10000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c index 35e128627bc0b..f091c4b05a8ad 100644 --- a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c +++ b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c @@ -212,9 +212,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO11, // DEFAULT_SPI_BUS_DC, // EPD_DC Command or data - &pin_GPIO13, // DEFAULT_SPI_BUS_CS, // EPD_CS Chip select - &pin_GPIO10, // DEFAULT_SPI_BUS_RESET, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO11), // DEFAULT_SPI_BUS_DC, // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO13), // DEFAULT_SPI_BUS_CS, // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO10), // DEFAULT_SPI_BUS_RESET, // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/hack_club_sprig/board.c b/ports/raspberrypi/boards/hack_club_sprig/board.c index 40711c554452e..94c5a7c5be9ec 100644 --- a/ports/raspberrypi/boards/hack_club_sprig/board.c +++ b/ports/raspberrypi/boards/hack_club_sprig/board.c @@ -62,9 +62,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - CIRCUITPY_BOARD_TFT_DC, - CIRCUITPY_BOARD_TFT_CS, - CIRCUITPY_BOARD_TFT_RESET, + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_DC), + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_CS), + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_RESET), 30000000, 0, 0); diff --git a/ports/raspberrypi/boards/heiafr_picomo_v2/board.c b/ports/raspberrypi/boards/heiafr_picomo_v2/board.c index 8c60341489fa1..3824c5959f5ee 100644 --- a/ports/raspberrypi/boards/heiafr_picomo_v2/board.c +++ b/ports/raspberrypi/boards/heiafr_picomo_v2/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO16, // TFT_DC Command or data - &pin_GPIO17, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO16), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 62500000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/heiafr_picomo_v3/board.c b/ports/raspberrypi/boards/heiafr_picomo_v3/board.c index 8c60341489fa1..3824c5959f5ee 100644 --- a/ports/raspberrypi/boards/heiafr_picomo_v3/board.c +++ b/ports/raspberrypi/boards/heiafr_picomo_v3/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO16, // TFT_DC Command or data - &pin_GPIO17, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO16), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 62500000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c b/ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c index 78cc912130e79..67e63daed1d67 100644 --- a/ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c +++ b/ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c @@ -63,9 +63,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO1, // DC - &pin_GPIO5, // CS - NULL, // RST (Reset pin tie to 0, do not set here) + MP_OBJ_FROM_PTR(&pin_GPIO1), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_NULL, // RST (Reset pin tie to 0, do not set here) 40000000, // baudrate 1, // polarity 0 // phase diff --git a/ports/raspberrypi/boards/pajenicko_picopad/board.c b/ports/raspberrypi/boards/pajenicko_picopad/board.c index 0ca13b3ec2e1b..d8f9392262383 100644 --- a/ports/raspberrypi/boards/pajenicko_picopad/board.c +++ b/ports/raspberrypi/boards/pajenicko_picopad/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO17, // TFT_DC Command or data - &pin_GPIO21, // TFT_CS Chip select - &pin_GPIO20, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO17), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO21), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO20), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/pimoroni_badger2040/board.c b/ports/raspberrypi/boards/pimoroni_badger2040/board.c index 07cc512b9a58e..80eddb49e1330 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2040/board.c @@ -263,9 +263,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO20, // EPD_DC Command or data - &pin_GPIO17, // EPD_CS Chip select - &pin_GPIO21, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO20), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO21), // EPD_RST Reset 1200000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/pimoroni_badger2040w/board.c b/ports/raspberrypi/boards/pimoroni_badger2040w/board.c index b992bcd6a1e37..ef5da7ceabcb7 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040w/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2040w/board.c @@ -263,9 +263,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO20, // EPD_DC Command or data - &pin_GPIO17, // EPD_CS Chip select - &pin_GPIO21, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO20), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO21), // EPD_RST Reset 1200000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c index 4bf7518c6a8df..58c54334d59af 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c @@ -58,9 +58,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO28, // EPD_DC Command or data - &pin_GPIO17, // EPD_CS Chip select - &pin_GPIO27, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO28), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO27), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c index 763a91a255569..fc6ca67f3ae65 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c @@ -117,9 +117,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO28, // EPD_DC Command or data - &pin_GPIO17, // EPD_CS Chip select - &pin_GPIO27, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO28), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO27), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/pimoroni_picosystem/board.c b/ports/raspberrypi/boards/pimoroni_picosystem/board.c index 40756b56426e4..e5956e43ed110 100644 --- a/ports/raspberrypi/boards/pimoroni_picosystem/board.c +++ b/ports/raspberrypi/boards/pimoroni_picosystem/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO9, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO4, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/ugame22/board.c b/ports/raspberrypi/boards/ugame22/board.c index 6b8152b14eedf..ed17a13ebc105 100644 --- a/ports/raspberrypi/boards/ugame22/board.c +++ b/ports/raspberrypi/boards/ugame22/board.c @@ -52,9 +52,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO1, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO1), // TFT_RST Reset 80000000L, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/waveshare_rp2040_geek/board.c b/ports/raspberrypi/boards/waveshare_rp2040_geek/board.c index 71269f0d1852f..431840fb0c5c1 100644 --- a/ports/raspberrypi/boards/waveshare_rp2040_geek/board.c +++ b/ports/raspberrypi/boards/waveshare_rp2040_geek/board.c @@ -37,9 +37,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // TFT_DC - &pin_GPIO9, // TFT_CS - &pin_GPIO12, // TFT_RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // TFT_DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // TFT_RST 50000000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c b/ports/raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c index f17ca53d72464..44d279da43fed 100644 --- a/ports/raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c +++ b/ports/raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c @@ -60,9 +60,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // DC - &pin_GPIO9, // CS - &pin_GPIO12, // RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/raspberrypi/boards/waveshare_rp2350_geek/board.c b/ports/raspberrypi/boards/waveshare_rp2350_geek/board.c index 71269f0d1852f..431840fb0c5c1 100644 --- a/ports/raspberrypi/boards/waveshare_rp2350_geek/board.c +++ b/ports/raspberrypi/boards/waveshare_rp2350_geek/board.c @@ -37,9 +37,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // TFT_DC - &pin_GPIO9, // TFT_CS - &pin_GPIO12, // TFT_RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // TFT_DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // TFT_RST 50000000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c b/ports/raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c index f17ca53d72464..44d279da43fed 100644 --- a/ports/raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c +++ b/ports/raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c @@ -60,9 +60,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // DC - &pin_GPIO9, // CS - &pin_GPIO12, // RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/stm/boards/meowbit_v121/board.c b/ports/stm/boards/meowbit_v121/board.c index 181f433a372b9..c3c745f64a14d 100644 --- a/ports/stm/boards/meowbit_v121/board.c +++ b/ports/stm/boards/meowbit_v121/board.c @@ -55,9 +55,9 @@ void board_init(void) { busio_spi_obj_t *internal_spi = &supervisor_flash_spi_bus; common_hal_fourwire_fourwire_construct(bus, internal_spi, - &pin_PA08, // Command or data - &pin_PB12, // Chip select - &pin_PB10, // Reset + MP_OBJ_FROM_PTR(&pin_PA08), // Command or data + MP_OBJ_FROM_PTR(&pin_PB12), // Chip select + MP_OBJ_FROM_PTR(&pin_PB10), // Reset 24000000, // Baudrate 0, // Polarity 0); // Phase From e1fe8a0827dccf17e7d736a88f98b1007754a6e4 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 27 Jan 2026 09:58:18 -0800 Subject: [PATCH 279/517] Fix Zephyr builds --- ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml | 1 + .../zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml | 1 + 11 files changed, 11 insertions(+) diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 5c1e64aedc70b..334cf4001ebe9 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index 2cd411fbc0651..40edb7b705827 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 227e4a2f6c187..509f14cd20d26 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 104a21115251a..3b0ecedcc6385 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index f22d066fe64c2..7e03594f1d1ec 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index 1768f33118079..4398a38ab8799 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 0a4bf3ce19fca..d60e8eb4b26f1 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 518af74c405d9..8743994bbc72a 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index 9806cd8a8116b..9b10fbe3c2d86 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 01e3314fa5ab4..ef54f4bb0f7fb 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index 9ec442d4cfabf..30ac22d00ccd7 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false From 8c22e671a257f38d67235929b0c93a46d7b1b47d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 27 Jan 2026 11:01:25 -0800 Subject: [PATCH 280/517] Save space by disabling DigitalInOut protocol support where needed --- locale/circuitpython.pot | 2 +- ports/atmel-samd/mpconfigport.mk | 2 + .../stm/boards/meowbit_v121/mpconfigboard.mk | 2 + py/circuitpy_mpconfig.mk | 6 +++ shared-bindings/digitalio/DigitalInOut.c | 53 ++++++++++--------- shared-bindings/digitalio/DigitalInOut.h | 13 +++++ .../digitalio/DigitalInOutProtocol.c | 37 ++++++++----- .../digitalio/DigitalInOutProtocol.h | 18 ++++++- shared-bindings/i2cioexpander/IOPin.c | 2 +- 9 files changed, 95 insertions(+), 40 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 9cf51a0c0251f..670ef5dcf0e4c 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -313,7 +313,7 @@ msgstr "" msgid "'%q' argument required" msgstr "" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "" diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 861ef37464633..c095220d0a3f2 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -14,6 +14,8 @@ CIRCUITPY_LTO = 1 CIRCUITPY_KEYPAD_DEMUX ?= 0 CIRCUITPY_LVFONTIO ?= 0 +CIRCUITPY_DIGITALINOUT_PROTOCOL = 0 + ###################################################################### # Put samd21-only choices here. diff --git a/ports/stm/boards/meowbit_v121/mpconfigboard.mk b/ports/stm/boards/meowbit_v121/mpconfigboard.mk index 3a128fb8343cc..74759abed5483 100644 --- a/ports/stm/boards/meowbit_v121/mpconfigboard.mk +++ b/ports/stm/boards/meowbit_v121/mpconfigboard.mk @@ -37,4 +37,6 @@ CIRCUITPY_ZLIB = 0 CIRCUITPY_STAGE = 1 +CIRCUITPY_DIGITALINOUT_PROTOCOL = 0 + FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/meowbit diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index e3a3e8fe8008e..8e0992f0418f3 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -241,6 +241,12 @@ CFLAGS += -DCIRCUITPY_CYW43=$(CIRCUITPY_CYW43) CIRCUITPY_DIGITALIO ?= 1 CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO) +# Enable the DigitalInOut protocol on the native DigitalInOut type. +# This allows other C code to use DigitalInOut objects polymorphically. +# Disable on small builds to save space. +CIRCUITPY_DIGITALINOUT_PROTOCOL ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_DIGITALINOUT_PROTOCOL=$(CIRCUITPY_DIGITALINOUT_PROTOCOL) + CIRCUITPY_COPROC ?= 0 CFLAGS += -DCIRCUITPY_COPROC=$(CIRCUITPY_COPROC) diff --git a/shared-bindings/digitalio/DigitalInOut.c b/shared-bindings/digitalio/DigitalInOut.c index 7d8471650495d..50cbc3bc36d62 100644 --- a/shared-bindings/digitalio/DigitalInOut.c +++ b/shared-bindings/digitalio/DigitalInOut.c @@ -343,85 +343,90 @@ static const mp_rom_map_elem_t digitalio_digitalinout_locals_dict_table[] = { static MP_DEFINE_CONST_DICT(digitalio_digitalinout_locals_dict, digitalio_digitalinout_locals_dict_table); // Protocol implementation - thin wrappers to match protocol signature -static void digitalinout_proto_deinit(mp_obj_t self_in) { +void digitalinout_deinit(mp_obj_t self_in) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); common_hal_digitalio_digitalinout_deinit(self); } -static bool digitalinout_proto_deinited(mp_obj_t self_in) { +bool digitalinout_deinited(mp_obj_t self_in) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_digitalio_digitalinout_deinited(self); } -static digitalinout_result_t digitalinout_proto_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull) { +digitalinout_result_t digitalinout_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_digitalio_digitalinout_switch_to_input(self, pull); } -static digitalinout_result_t digitalinout_proto_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode) { +digitalinout_result_t digitalinout_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_digitalio_digitalinout_switch_to_output(self, value, drive_mode); } -static digitalio_direction_t digitalinout_proto_get_direction(mp_obj_t self_in) { +digitalio_direction_t digitalinout_get_direction(mp_obj_t self_in) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_digitalio_digitalinout_get_direction(self); } -static mp_errno_t digitalinout_proto_set_value(mp_obj_t self_in, bool value) { +mp_errno_t digitalinout_set_value(mp_obj_t self_in, bool value) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); common_hal_digitalio_digitalinout_set_value(self, value); return 0; } -static mp_errno_t digitalinout_proto_get_value(mp_obj_t self_in, bool *value) { +mp_errno_t digitalinout_get_value(mp_obj_t self_in, bool *value) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); *value = common_hal_digitalio_digitalinout_get_value(self); return 0; } -static digitalinout_result_t digitalinout_proto_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode) { +digitalinout_result_t digitalinout_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_digitalio_digitalinout_set_drive_mode(self, drive_mode); } -static digitalio_drive_mode_t digitalinout_proto_get_drive_mode(mp_obj_t self_in) { +digitalio_drive_mode_t digitalinout_get_drive_mode(mp_obj_t self_in) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_digitalio_digitalinout_get_drive_mode(self); } -static digitalinout_result_t digitalinout_proto_set_pull(mp_obj_t self_in, digitalio_pull_t pull) { +digitalinout_result_t digitalinout_set_pull(mp_obj_t self_in, digitalio_pull_t pull) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_digitalio_digitalinout_set_pull(self, pull); } -static digitalio_pull_t digitalinout_proto_get_pull(mp_obj_t self_in) { +digitalio_pull_t digitalinout_get_pull(mp_obj_t self_in) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_digitalio_digitalinout_get_pull(self); } +#if CIRCUITPY_DIGITALINOUT_PROTOCOL static const digitalinout_p_t digitalinout_digitalinout_proto = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_digitalinout) - .deinit = digitalinout_proto_deinit, - .deinited = digitalinout_proto_deinited, - .switch_to_input = digitalinout_proto_switch_to_input, - .switch_to_output = digitalinout_proto_switch_to_output, - .get_direction = digitalinout_proto_get_direction, - .get_value = digitalinout_proto_get_value, - .set_value = digitalinout_proto_set_value, - .get_drive_mode = digitalinout_proto_get_drive_mode, - .set_drive_mode = digitalinout_proto_set_drive_mode, - .get_pull = digitalinout_proto_get_pull, - .set_pull = digitalinout_proto_set_pull, + MP_PROTO_IMPLEMENT(MP_QSTR_DigitalInOut) + .deinit = digitalinout_deinit, + .deinited = digitalinout_deinited, + .switch_to_input = digitalinout_switch_to_input, + .switch_to_output = digitalinout_switch_to_output, + .get_direction = digitalinout_get_direction, + .get_value = digitalinout_get_value, + .set_value = digitalinout_set_value, + .get_drive_mode = digitalinout_get_drive_mode, + .set_drive_mode = digitalinout_set_drive_mode, + .get_pull = digitalinout_get_pull, + .set_pull = digitalinout_set_pull, }; +#endif MP_DEFINE_CONST_OBJ_TYPE( digitalio_digitalinout_type, MP_QSTR_DigitalInOut, MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, make_new, digitalio_digitalinout_make_new, - locals_dict, &digitalio_digitalinout_locals_dict, + locals_dict, &digitalio_digitalinout_locals_dict + #if CIRCUITPY_DIGITALINOUT_PROTOCOL + , protocol, &digitalinout_digitalinout_proto + #endif ); // Helper for validating digitalio.DigitalInOut arguments diff --git a/shared-bindings/digitalio/DigitalInOut.h b/shared-bindings/digitalio/DigitalInOut.h index 7465af3e288bd..ad38cbec6a4d2 100644 --- a/shared-bindings/digitalio/DigitalInOut.h +++ b/shared-bindings/digitalio/DigitalInOut.h @@ -57,3 +57,16 @@ digitalio_digitalinout_obj_t *assert_digitalinout(mp_obj_t obj); volatile uint32_t *common_hal_digitalio_digitalinout_get_reg(digitalio_digitalinout_obj_t *self, digitalinout_reg_op_t op, uint32_t *mask); bool common_hal_digitalio_has_reg_op(digitalinout_reg_op_t op); + +// Protocol wrapper functions - always available for direct calls +void digitalinout_deinit(mp_obj_t self_in); +bool digitalinout_deinited(mp_obj_t self_in); +digitalinout_result_t digitalinout_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull); +digitalinout_result_t digitalinout_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode); +digitalio_direction_t digitalinout_get_direction(mp_obj_t self_in); +mp_errno_t digitalinout_set_value(mp_obj_t self_in, bool value); +mp_errno_t digitalinout_get_value(mp_obj_t self_in, bool *value); +digitalinout_result_t digitalinout_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode); +digitalio_drive_mode_t digitalinout_get_drive_mode(mp_obj_t self_in); +digitalinout_result_t digitalinout_set_pull(mp_obj_t self_in, digitalio_pull_t pull); +digitalio_pull_t digitalinout_get_pull(mp_obj_t self_in); diff --git a/shared-bindings/digitalio/DigitalInOutProtocol.c b/shared-bindings/digitalio/DigitalInOutProtocol.c index e0671a1b88133..df64fd729c29b 100644 --- a/shared-bindings/digitalio/DigitalInOutProtocol.c +++ b/shared-bindings/digitalio/DigitalInOutProtocol.c @@ -102,7 +102,7 @@ // // Example: // static const digitalinout_p_t my_type_proto = { -// MP_PROTO_IMPLEMENT(MP_QSTR_protocol_digitalinout) +// MP_PROTO_IMPLEMENT(MP_QSTR_DigitalInOut) // .construct = my_construct_func, // .deinit = my_deinit_func, // .deinited = my_deinited_func, @@ -128,7 +128,7 @@ // See shared-bindings/digitalio/DigitalInOut.c for a complete example. // - +#if CIRCUITPY_DIGITALINOUT_PROTOCOL static void check_object_has_method(mp_obj_t obj, qstr method_name) { mp_obj_t dest[2]; mp_load_method_protected(obj, method_name, dest, true); @@ -144,6 +144,7 @@ static void check_object_has_attr(mp_obj_t obj, qstr attr_name) { mp_raise_TypeError_varg(MP_ERROR_TEXT("%q object missing '%q' attribute"), MP_OBJ_TO_PTR(obj), attr_name); } } +#endif mp_obj_t digitalinout_protocol_from_pin( mp_obj_t pin_or_dio, @@ -195,8 +196,9 @@ mp_obj_t digitalinout_protocol_from_pin( return dio_obj; } + #if CIRCUITPY_DIGITALINOUT_PROTOCOL // Check if it natively implements the DigitalInOutProtocol - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, pin_or_dio); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, pin_or_dio); if (proto != NULL) { // Native protocol support - use it directly return pin_or_dio; @@ -214,8 +216,15 @@ mp_obj_t digitalinout_protocol_from_pin( // Object has all required attributes - use it as DigitalInOutProtocol return pin_or_dio; + #else + mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object does not support '%q'"), + mp_obj_get_type_qstr(pin_or_dio), MP_QSTR_DigitalInOut); + #endif } +// These functions are only used when CIRCUITPY_DIGITALINOUT_PROTOCOL is enabled. +// Otherwise, the digitalinout_* functions are called directly. +#if CIRCUITPY_DIGITALINOUT_PROTOCOL void digitalinout_protocol_deinit(mp_obj_t self) { const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); if (proto && proto->deinit) { @@ -233,7 +242,7 @@ void digitalinout_protocol_deinit(mp_obj_t self) { } bool digitalinout_protocol_deinited(mp_obj_t self) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->deinited) { return proto->deinited(self); } @@ -244,7 +253,7 @@ bool digitalinout_protocol_deinited(mp_obj_t self) { } digitalinout_result_t digitalinout_protocol_switch_to_input(mp_obj_t self, digitalio_pull_t pull) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->switch_to_input) { return proto->switch_to_input(self, pull); } @@ -268,7 +277,7 @@ digitalinout_result_t digitalinout_protocol_switch_to_input(mp_obj_t self, digit } digitalinout_result_t digitalinout_protocol_switch_to_output(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->switch_to_output) { return proto->switch_to_output(self, value, drive_mode); } @@ -289,7 +298,7 @@ digitalinout_result_t digitalinout_protocol_switch_to_output(mp_obj_t self, bool } digitalio_direction_t digitalinout_protocol_get_direction(mp_obj_t self) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->get_direction) { return proto->get_direction(self); } @@ -303,7 +312,7 @@ digitalio_direction_t digitalinout_protocol_get_direction(mp_obj_t self) { } mp_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->set_value) { return proto->set_value(self, value); } @@ -314,7 +323,7 @@ mp_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value) { } mp_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->get_value) { return proto->get_value(self, value); } @@ -325,7 +334,7 @@ mp_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value) { } digitalinout_result_t digitalinout_protocol_set_drive_mode(mp_obj_t self, digitalio_drive_mode_t drive_mode) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->set_drive_mode) { return proto->set_drive_mode(self, drive_mode); } @@ -339,7 +348,7 @@ digitalinout_result_t digitalinout_protocol_set_drive_mode(mp_obj_t self, digita } digitalio_drive_mode_t digitalinout_protocol_get_drive_mode(mp_obj_t self) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->get_drive_mode) { return proto->get_drive_mode(self); } @@ -353,7 +362,7 @@ digitalio_drive_mode_t digitalinout_protocol_get_drive_mode(mp_obj_t self) { } digitalinout_result_t digitalinout_protocol_set_pull(mp_obj_t self, digitalio_pull_t pull) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->set_pull) { return proto->set_pull(self, pull); } @@ -370,7 +379,7 @@ digitalinout_result_t digitalinout_protocol_set_pull(mp_obj_t self, digitalio_pu } digitalio_pull_t digitalinout_protocol_get_pull(mp_obj_t self) { - const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->get_pull) { return proto->get_pull(self); } @@ -384,3 +393,5 @@ digitalio_pull_t digitalinout_protocol_get_pull(mp_obj_t self) { } return PULL_NONE; } + +#endif // CIRCUITPY_DIGITALINOUT_PROTOCOL diff --git a/shared-bindings/digitalio/DigitalInOutProtocol.h b/shared-bindings/digitalio/DigitalInOutProtocol.h index 5ec8bcb185543..798cb6a9de1eb 100644 --- a/shared-bindings/digitalio/DigitalInOutProtocol.h +++ b/shared-bindings/digitalio/DigitalInOutProtocol.h @@ -19,7 +19,7 @@ // Protocol structure for DigitalInOut implementations // Note: mcu_pin_obj_t and digitalinout_result_t are defined by files that include this header typedef struct _digitalinout_p_t { - MP_PROTOCOL_HEAD // MP_QSTR_protocol_digitalinout + MP_PROTOCOL_HEAD // MP_QSTR_DigitalInOut void (*deinit)(mp_obj_t self); bool (*deinited)(mp_obj_t self); digitalinout_result_t (*switch_to_input)(mp_obj_t self, digitalio_pull_t pull); @@ -54,6 +54,8 @@ mp_obj_t digitalinout_protocol_from_pin( bool force_port_allocation, bool *out_owns_pin); +#if CIRCUITPY_DIGITALINOUT_PROTOCOL +// Protocol helper functions that do protocol lookup or Python fallback void digitalinout_protocol_deinit(mp_obj_t self); bool digitalinout_protocol_deinited(mp_obj_t self); digitalinout_result_t digitalinout_protocol_switch_to_input(mp_obj_t self, digitalio_pull_t pull); @@ -65,3 +67,17 @@ digitalinout_result_t digitalinout_protocol_set_drive_mode(mp_obj_t self, digita digitalio_drive_mode_t digitalinout_protocol_get_drive_mode(mp_obj_t self); digitalinout_result_t digitalinout_protocol_set_pull(mp_obj_t self, digitalio_pull_t pull); digitalio_pull_t digitalinout_protocol_get_pull(mp_obj_t self); +#else +// When protocol is disabled, map directly to native DigitalInOut functions +#define digitalinout_protocol_deinit digitalinout_deinit +#define digitalinout_protocol_deinited digitalinout_deinited +#define digitalinout_protocol_switch_to_input digitalinout_switch_to_input +#define digitalinout_protocol_switch_to_output digitalinout_switch_to_output +#define digitalinout_protocol_get_direction digitalinout_get_direction +#define digitalinout_protocol_set_value digitalinout_set_value +#define digitalinout_protocol_get_value digitalinout_get_value +#define digitalinout_protocol_set_drive_mode digitalinout_set_drive_mode +#define digitalinout_protocol_get_drive_mode digitalinout_get_drive_mode +#define digitalinout_protocol_set_pull digitalinout_set_pull +#define digitalinout_protocol_get_pull digitalinout_get_pull +#endif diff --git a/shared-bindings/i2cioexpander/IOPin.c b/shared-bindings/i2cioexpander/IOPin.c index 4a8ca46372bf5..e2957f9a220f9 100644 --- a/shared-bindings/i2cioexpander/IOPin.c +++ b/shared-bindings/i2cioexpander/IOPin.c @@ -290,7 +290,7 @@ static digitalinout_result_t iopin_protocol_set_pull(mp_obj_t self_in, digitalio } static const digitalinout_p_t iopin_digitalinout_p = { - MP_PROTO_IMPLEMENT(MP_QSTR_protocol_digitalinout) + MP_PROTO_IMPLEMENT(MP_QSTR_DigitalInOut) .deinit = iopin_protocol_deinit, .deinited = iopin_protocol_deinited, .switch_to_input = iopin_protocol_switch_to_input, From b79de65ad61d3d997969cef736bc75c2415fce80 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 27 Jan 2026 16:30:36 -0500 Subject: [PATCH 281/517] Initial defn for Freensy 2350 --- .../boards/adafruit_freensy_rp2350/board.c | 16 ++++++ .../adafruit_freensy_rp2350/mpconfigboard.h | 20 +++++++ .../adafruit_freensy_rp2350/mpconfigboard.mk | 10 ++++ .../pico-sdk-configboard.h | 10 ++++ .../boards/adafruit_freensy_rp2350/pins.c | 54 +++++++++++++++++++ 5 files changed, 110 insertions(+) create mode 100644 ports/raspberrypi/boards/adafruit_freensy_rp2350/board.c create mode 100644 ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.h create mode 100644 ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.mk create mode 100644 ports/raspberrypi/boards/adafruit_freensy_rp2350/pico-sdk-configboard.h create mode 100644 ports/raspberrypi/boards/adafruit_freensy_rp2350/pins.c diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/board.c b/ports/raspberrypi/boards/adafruit_freensy_rp2350/board.c new file mode 100644 index 0000000000000..fddd2572c1fcd --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_freensy_rp2350/board.c @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "supervisor/board.h" + +#include "common-hal/picodvi/__init__.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. + +void board_init(void) { + picodvi_autoconstruct(); +} diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.h b/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.h new file mode 100644 index 0000000000000..9bde1fdbdd70c --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.h @@ -0,0 +1,20 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "Adafruit Freensy RP2350" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO17) + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO19) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO18) + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO10) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO11) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO12) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO1) +#define DEFAULT_UART_BUS_TX (&pin_GPIO0) diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.mk new file mode 100644 index 0000000000000..6070a1fbe8f55 --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.mk @@ -0,0 +1,10 @@ +USB_VID = 0x239A +USB_PID = 0x8170 +USB_PRODUCT = "Freensy RP2350" +USB_MANUFACTURER = "Adafruit" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "GD25Q64C,W25Q64JVxQ" diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/pico-sdk-configboard.h b/ports/raspberrypi/boards/adafruit_freensy_rp2350/pico-sdk-configboard.h new file mode 100644 index 0000000000000..2d9283a9192f2 --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_freensy_rp2350/pico-sdk-configboard.h @@ -0,0 +1,10 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Put board-specific pico-sdk definitions here. This file must exist. + +// Allow extra time for xosc to start. +#define PICO_XOSC_STARTUP_DELAY_MULTIPLIER 64 diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/pins.c b/ports/raspberrypi/boards/adafruit_freensy_rp2350/pins.c new file mode 100644 index 0000000000000..0d769fd4d565d --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_freensy_rp2350/pins.c @@ -0,0 +1,54 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_D22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_D23), MP_ROM_PTR(&pin_GPIO23) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CS), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO17) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 928e6bc4aa3fc499b029c99b4d7ed33bf4bfdf0e Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 27 Jan 2026 14:43:07 -0800 Subject: [PATCH 282/517] Add copespell ignores and fix stubs --- ports/atmel-samd/Makefile | 2 +- ports/cxd56/mkspk/clefia.c | 10 +++++----- ports/espressif/tools/update_sdkconfig.py | 2 +- py/makeqstrdata.py | 2 +- shared-bindings/i2cioexpander/IOExpander.c | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 9582eff212d60..f8f420fda803a 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -169,7 +169,7 @@ LIBS += -lm endif ifeq ($(CHIP_FAMILY), samd21) -LDFLAGS += -mthumb -mcpu=cortex-m0plus -Lasf/thirdparty/CMSIS/Lib/GCC/ +LDFLAGS += -mthumb -mcpu=cortex-m0plus -Lasf/thirdparty/CMSIS/Lib/GCC/ # codespell:ignore thirdparty BOOTLOADER_SIZE := 0x2000 else ifeq ($(CHIP_FAMILY), samd51) LDFLAGS += -mthumb -mcpu=cortex-m4 diff --git a/ports/cxd56/mkspk/clefia.c b/ports/cxd56/mkspk/clefia.c index e946ee5348a5a..d36f638fa0b0d 100644 --- a/ports/cxd56/mkspk/clefia.c +++ b/ports/cxd56/mkspk/clefia.c @@ -466,16 +466,16 @@ int clefiakeyset(unsigned char *rk, const unsigned char *skey) { void clefiaencrypt(unsigned char *ct, const unsigned char *pt, const unsigned char *rk, const int r) { - unsigned char rin[16]; + unsigned char r_in[16]; unsigned char rout[16]; - bytecpy(rin, pt, 16); + bytecpy(r_in, pt, 16); - bytexor(rin + 4, rin + 4, rk + 0, 4); /* initial key whitening */ - bytexor(rin + 12, rin + 12, rk + 4, 4); + bytexor(r_in + 4, r_in + 4, rk + 0, 4); /* initial key whitening */ + bytexor(r_in + 12, r_in + 12, rk + 4, 4); rk += 8; - clefiagfn4(rout, rin, rk, r); /* GFN_{4,r} */ + clefiagfn4(rout, r_in, rk, r); /* GFN_{4,r} */ bytecpy(ct, rout, 16); bytexor(ct + 4, ct + 4, rk + r * 8 + 0, 4); /* final key whitening */ diff --git a/ports/espressif/tools/update_sdkconfig.py b/ports/espressif/tools/update_sdkconfig.py index 8837d84321b30..209b976b881e9 100644 --- a/ports/espressif/tools/update_sdkconfig.py +++ b/ports/espressif/tools/update_sdkconfig.py @@ -321,7 +321,7 @@ def update(debug, board, update_all): # noqa: C901: too complex if print_debug: print(" " * len(current_group), i, config_string.strip()) - # Some files are `rsource`d into another kconfig with $IDF_TARGET as + # Some files are `rsource`d into another kconfig with $IDF_TARGET as # codespell:ignore rsource # part of the path. kconfiglib doesn't show this as a reference so # we have to look ourselves. target_reference = target in item.name_and_loc diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 45cc896f9252e..998328bf1d250 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -51,7 +51,7 @@ 954: "kappa", 8656: "lArr", 955: "lambda", 9001: "lang", 171: "laquo", 8592: "larr", 8968: "lceil", 8220: "ldquo", 8804: "le", 8970: "lfloor", 8727: "lowast", 9674: "loz", 8206: "lrm", 8249: "lsaquo", 8216: "lsquo", 60: "lt", 175: "macr", 8212: "mdash", 181: "micro", 183: "middot", 8722: "minus", - 956: "mu", 8711: "nabla", 160: "nbsp", 8211: "ndash", 8800: "ne", 8715: "ni", 172: "not", 8713: "notin", + 956: "mu", 8711: "nabla", 160: "nbsp", 8211: "ndash", 8800: "ne", 8715: "ni", 172: "not", 8713: "notin", # codespell:ignore notin 8836: "nsub", 241: "ntilde", 957: "nu", 243: "oacute", 244: "ocirc", 339: "oelig", 242: "ograve", 8254: "oline", 969: "omega", 959: "omicron", 8853: "oplus", 8744: "or", 170: "ordf", 186: "ordm", 248: "oslash", 245: "otilde", 8855: "otimes", 246: "ouml", 182: "para", 8706: "part", 8240: "permil", diff --git a/shared-bindings/i2cioexpander/IOExpander.c b/shared-bindings/i2cioexpander/IOExpander.c index 09c404d289c74..f2257b4b63253 100644 --- a/shared-bindings/i2cioexpander/IOExpander.c +++ b/shared-bindings/i2cioexpander/IOExpander.c @@ -211,8 +211,8 @@ MP_PROPERTY_GETSET(i2cioexpander_ioexpander_output_mask_obj, (mp_obj_t)&i2cioexpander_ioexpander_set_output_mask_obj); //| @property -//| def pins(self) -> Tuple[DigitalInOutProtocol, ...]: -//| """A tuple of pin objects that implement the DigitalInOutProtocol. +//| def pins(self) -> Tuple[IOPin, ...]: +//| """A tuple of `IOPin` objects that implement the DigitalInOutProtocol. //| Each pin can be used like a digitalio.DigitalInOut object.""" //| ... static mp_obj_t i2cioexpander_ioexpander_obj_get_pins(mp_obj_t self_in) { From dd82fe0d1d95992058b5b8fffc0faa3b5f23221e Mon Sep 17 00:00:00 2001 From: George Hartzell Date: Wed, 28 Jan 2026 10:34:05 -0700 Subject: [PATCH 283/517] Update __init__.c Add a bit more info to the docs for the rainboxio.colorwheel function, making it more useful to someone just getting started. --- shared-bindings/rainbowio/__init__.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shared-bindings/rainbowio/__init__.c b/shared-bindings/rainbowio/__init__.c index a085669d7ff9a..e02df713eeba2 100644 --- a/shared-bindings/rainbowio/__init__.c +++ b/shared-bindings/rainbowio/__init__.c @@ -16,7 +16,8 @@ //| //| def colorwheel(n: float) -> int: //| """C implementation of the common colorwheel() function found in many examples. -//| Returns the colorwheel RGB value as an integer value for n (usable in neopixel and dotstar). +//| Takes a hue, a value between 0-255, and returns an RGB tuple encoded as an +//| integer value (usable in neopixel and dotstar). //| """ //| ... //| From 342a3649f69c03d7117b85376bc4fffadb12da5e Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 28 Jan 2026 14:20:23 -0500 Subject: [PATCH 284/517] name change to Tinychad --- .../board.c | 0 .../mpconfigboard.h | 2 +- .../mpconfigboard.mk | 2 +- .../pico-sdk-configboard.h | 0 .../pins.c | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename ports/raspberrypi/boards/{adafruit_freensy_rp2350 => adafruit_tinychad_rp2350}/board.c (100%) rename ports/raspberrypi/boards/{adafruit_freensy_rp2350 => adafruit_tinychad_rp2350}/mpconfigboard.h (90%) rename ports/raspberrypi/boards/{adafruit_freensy_rp2350 => adafruit_tinychad_rp2350}/mpconfigboard.mk (84%) rename ports/raspberrypi/boards/{adafruit_freensy_rp2350 => adafruit_tinychad_rp2350}/pico-sdk-configboard.h (100%) rename ports/raspberrypi/boards/{adafruit_freensy_rp2350 => adafruit_tinychad_rp2350}/pins.c (100%) diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/board.c b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/board.c similarity index 100% rename from ports/raspberrypi/boards/adafruit_freensy_rp2350/board.c rename to ports/raspberrypi/boards/adafruit_tinychad_rp2350/board.c diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.h b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.h similarity index 90% rename from ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.h rename to ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.h index 9bde1fdbdd70c..958ab01a0b6a8 100644 --- a/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.h +++ b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.h @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT -#define MICROPY_HW_BOARD_NAME "Adafruit Freensy RP2350" +#define MICROPY_HW_BOARD_NAME "Adafruit Tinychad RP2350" #define MICROPY_HW_MCU_NAME "rp2350a" #define MICROPY_HW_NEOPIXEL (&pin_GPIO17) diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.mk similarity index 84% rename from ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.mk rename to ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.mk index 6070a1fbe8f55..8d2a9c2f5273f 100644 --- a/ports/raspberrypi/boards/adafruit_freensy_rp2350/mpconfigboard.mk +++ b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.mk @@ -1,6 +1,6 @@ USB_VID = 0x239A USB_PID = 0x8170 -USB_PRODUCT = "Freensy RP2350" +USB_PRODUCT = "Tinychad RP2350" USB_MANUFACTURER = "Adafruit" CHIP_VARIANT = RP2350 diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/pico-sdk-configboard.h b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/pico-sdk-configboard.h similarity index 100% rename from ports/raspberrypi/boards/adafruit_freensy_rp2350/pico-sdk-configboard.h rename to ports/raspberrypi/boards/adafruit_tinychad_rp2350/pico-sdk-configboard.h diff --git a/ports/raspberrypi/boards/adafruit_freensy_rp2350/pins.c b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/pins.c similarity index 100% rename from ports/raspberrypi/boards/adafruit_freensy_rp2350/pins.c rename to ports/raspberrypi/boards/adafruit_tinychad_rp2350/pins.c From 44f38161919e0d838d43ccf9b71b79a4fca466c3 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 26 Jan 2026 17:07:33 -0800 Subject: [PATCH 285/517] Fixes for native sim --- .pre-commit-config.yaml | 2 +- main.c | 9 +- ports/zephyr-cp/Makefile | 8 +- ports/zephyr-cp/boards/board_aliases.cmake | 1 + .../native/native_sim/autogen_board_info.toml | 114 ++++++++++++++++++ .../native/native_sim/circuitpython.toml | 1 + ports/zephyr-cp/boards/native_sim.conf | 6 + ports/zephyr-cp/boards/native_sim.overlay | 39 ++++++ ports/zephyr-cp/common-hal/busio/UART.c | 2 +- .../common-hal/microcontroller/Processor.c | 4 + .../common-hal/zephyr_kernel/__init__.c | 1 + .../zephyr-cp/cptools/build_circuitpython.py | 7 +- ports/zephyr-cp/cptools/zephyr2cp.py | 47 +++++--- ports/zephyr-cp/supervisor/flash.c | 2 +- ports/zephyr-cp/supervisor/port.c | 12 +- shared/runtime/gchelper_generic.c | 8 +- supervisor/shared/background_callback.c | 2 + supervisor/shared/safe_mode.c | 12 ++ 18 files changed, 243 insertions(+), 34 deletions(-) create mode 100644 ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/native/native_sim/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/native_sim.conf create mode 100644 ports/zephyr-cp/boards/native_sim.overlay diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f7e7956498d68..4db04e1d0eddb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,7 +28,7 @@ repos: lib/mbedtls_errors/generate_errors.diff ) - repo: https://github.com/codespell-project/codespell - rev: v2.2.4 + rev: v2.4.1 hooks: - id: codespell args: [-w] diff --git a/main.c b/main.c index d636c851b700e..d187c56cee46d 100644 --- a/main.c +++ b/main.c @@ -21,6 +21,7 @@ #include "py/stackctrl.h" #include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" #include "background.h" @@ -1161,13 +1162,7 @@ int __attribute__((used)) main(void) { void gc_collect(void) { gc_collect_start(); - // Load register values onto the stack. They get collected below with the rest of the stack. - size_t regs[SAVED_REGISTER_COUNT]; - mp_uint_t sp = cpu_get_regs_and_sp(regs); - - // This naively collects all object references from an approximate stack - // range. - gc_collect_root((void **)sp, ((mp_uint_t)port_stack_get_top() - sp) / sizeof(mp_uint_t)); + gc_helper_collect_regs_and_stack(); // This collects root pointers from the VFS mount table. Some of them may // have lost their references in the VM even though they are mounted. diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index 037c14afad4c3..d31068e4c92ea 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -8,7 +8,7 @@ BUILD ?= build-$(BOARD) TRANSLATION ?= en_US -.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug clean menuconfig all clean-all test +.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug run clean menuconfig all clean-all test $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) @@ -20,12 +20,18 @@ $(BUILD)/firmware.elf: $(BUILD)/zephyr-cp/zephyr/zephyr.elf $(BUILD)/firmware.hex: $(BUILD)/zephyr-cp/zephyr/zephyr.elf cp $(BUILD)/zephyr-cp/zephyr/zephyr.hex $@ +$(BUILD)/firmware.exe: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + cp $(BUILD)/zephyr-cp/zephyr/zephyr.exe $@ + flash: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west flash -d $(BUILD) debug: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west debug -d $(BUILD) +run: $(BUILD)/firmware.exe + $^ + menuconfig: west build --sysbuild -d $(BUILD) -t menuconfig diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index 7fc973efc7852..954bce0b29823 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -1,6 +1,7 @@ set(pca10056_BOARD_ALIAS nrf52840dk/nrf52840) set(renesas_ek_ra6m5_BOARD_ALIAS ek_ra6m5) set(renesas_ek_ra8d1_BOARD_ALIAS ek_ra8d1) +set(native_native_sim_BOARD_ALIAS native_sim) set(nordic_nrf54l15dk_BOARD_ALIAS nrf54l15dk/nrf54l15/cpuapp) set(nordic_nrf54h20dk_BOARD_ALIAS nrf54h20dk/nrf54h20/cpuapp) set(nordic_nrf5340dk_BOARD_ALIAS nrf5340dk/nrf5340/cpuapp) diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml new file mode 100644 index 0000000000000..8a4eb3e6fb581 --- /dev/null +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -0,0 +1,114 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "POSIX/Native Boards Native simulator - native_sim" + +[modules] +__future__ = false +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = true # Zephyr board has busio +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml b/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/native_sim.conf b/ports/zephyr-cp/boards/native_sim.conf new file mode 100644 index 0000000000000..baf808b51427d --- /dev/null +++ b/ports/zephyr-cp/boards/native_sim.conf @@ -0,0 +1,6 @@ +CONFIG_EMUL=y +CONFIG_GPIO=y +CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME=n + +# So we can test safe mode +CONFIG_NATIVE_SIM_REBOOT=y diff --git a/ports/zephyr-cp/boards/native_sim.overlay b/ports/zephyr-cp/boards/native_sim.overlay new file mode 100644 index 0000000000000..6a13f58379ac4 --- /dev/null +++ b/ports/zephyr-cp/boards/native_sim.overlay @@ -0,0 +1,39 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Device tree overlay for CircuitPython on native_sim. + * Adds simulated SRAM region required by CircuitPython build system. + */ + +#include + +/ { + sram0: memory@20000000 { + device_type = "memory"; + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x20000000 DT_SIZE_M(1)>; + zephyr,memory-region = "SRAM"; + }; + + chosen { + zephyr,sram = &sram0; + /delete-property/ zephyr,flash; + /delete-property/ zephyr,code-partition; + }; +}; + +&flash0 { + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + circuitpy_partition: partition@0 { + label = "circuitpy"; + reg = <0x00000000 DT_SIZE_K(2048)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/common-hal/busio/UART.c b/ports/zephyr-cp/common-hal/busio/UART.c index b5aaadb3bb51d..9940853da50a5 100644 --- a/ports/zephyr-cp/common-hal/busio/UART.c +++ b/ports/zephyr-cp/common-hal/busio/UART.c @@ -126,7 +126,7 @@ void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrat struct uart_config config; uart_config_get(self->uart_device, &config); config.baudrate = baudrate; - uart_config_set(self->uart_device, &config); + uart_configure(self->uart_device, &config); } mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) { diff --git a/ports/zephyr-cp/common-hal/microcontroller/Processor.c b/ports/zephyr-cp/common-hal/microcontroller/Processor.c index ddc8b97056d2b..9f512a686ec14 100644 --- a/ports/zephyr-cp/common-hal/microcontroller/Processor.c +++ b/ports/zephyr-cp/common-hal/microcontroller/Processor.c @@ -21,7 +21,11 @@ float common_hal_mcu_processor_get_temperature(void) { extern uint32_t SystemCoreClock; uint32_t common_hal_mcu_processor_get_frequency(void) { + #ifdef __ARM__ return SystemCoreClock; + #else + return CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + #endif } float common_hal_mcu_processor_get_voltage(void) { diff --git a/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c b/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c index 042f06b0ed217..d5e6fd080062a 100644 --- a/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c +++ b/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c @@ -6,6 +6,7 @@ #include "bindings/zephyr_kernel/__init__.h" #include "py/runtime.h" +#include #include diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index a5e4d72b902bb..83f80945f3e00 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -277,9 +277,10 @@ async def build_circuitpython(): enable_mpy_native = False full_build = True usb_host = False + zephyr_board = cmake_args["BOARD"] board = cmake_args["BOARD_ALIAS"] if not board: - board = cmake_args["BOARD"] + board = zephyr_board translation = cmake_args["TRANSLATION"] if not translation: translation = "en_US" @@ -319,7 +320,7 @@ async def build_circuitpython(): ) board_autogen_task = tg.create_task( - zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir) + zephyr_dts_to_cp_board(zephyr_board, portdir, builddir, zephyrbuilddir) ) board_info = board_autogen_task.result() mpconfigboard_fn = board_tools.find_mpconfigboard(portdir, board) @@ -353,6 +354,8 @@ async def build_circuitpython(): "shared/readline/readline.c", "shared/runtime/buffer_helper.c", "shared/runtime/context_manager_helpers.c", + "shared/runtime/gchelper_generic.c", + "shared/runtime/gchelper_native.c", "shared/runtime/pyexec.c", "shared/runtime/interrupt_char.c", "shared/runtime/stdout_helpers.c", diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 77404ec2bec3f..0f09c21e83f70 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -16,6 +16,7 @@ MANUAL_COMPAT_TO_DRIVER = { "renesas_ra_nv_flash": "flash", + "soc_nv_flash": "flash", "nordic_nrf_uarte": "serial", "nordic_nrf_uart": "serial", "nordic_nrf_twim": "i2c", @@ -370,7 +371,7 @@ def find_ram_regions(device_tree): @cpbuild.run_in_thread -def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 +def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa: C901 board_dir = builddir / "board" # Auto generate board files from device tree. @@ -384,7 +385,14 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 zephyr_board_dir = pathlib.Path(runners["config"]["board_dir"]) board_yaml = zephyr_board_dir / "board.yml" board_yaml = yaml.safe_load(board_yaml.read_text()) - board_info["vendor_id"] = board_yaml["board"]["vendor"] + if "board" not in board_yaml and "boards" in board_yaml: + for board in board_yaml["boards"]: + if board["name"] == board_id: + board_yaml = board + break + else: + board_yaml = board_yaml["board"] + board_info["vendor_id"] = board_yaml["vendor"] vendor_index = zephyr_board_dir.parent / "index.rst" if vendor_index.exists(): vendor_index = vendor_index.read_text() @@ -393,9 +401,9 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 else: vendor_name = board_info["vendor_id"] board_info["vendor"] = vendor_name - soc_name = board_yaml["board"]["socs"][0]["name"] + soc_name = board_yaml["socs"][0]["name"] board_info["soc"] = soc_name - board_name = board_yaml["board"]["full_name"] + board_name = board_yaml["full_name"] board_info["name"] = board_name # board_id_yaml = zephyr_board_dir / (zephyr_board_dir.name + ".yaml") # board_id_yaml = yaml.safe_load(board_id_yaml.read_text()) @@ -540,15 +548,18 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 board_names[(ioport, num)].append("BUTTON") board_names[(ioport, num)].extend(node2alias[key]) - a, b = all_ioports[:2] - i = 0 - while a[i] == b[i]: - i += 1 - shared_prefix = a[:i] - for ioport in ioports: - if not ioport.startswith(shared_prefix): - shared_prefix = "" - break + if len(all_ioports) > 1: + a, b = all_ioports[:2] + i = 0 + while a[i] == b[i]: + i += 1 + shared_prefix = a[:i] + for ioport in ioports: + if not ioport.startswith(shared_prefix): + shared_prefix = "" + break + else: + shared_prefix = all_ioports[0] pin_defs = [] pin_declarations = ["#pragma once"] @@ -658,8 +669,14 @@ def zephyr_dts_to_cp_board(portdir, builddir, zephyrbuilddir): # noqa: C901 device, start, end, size, path = ram max_size = max(max_size, size) # We always start at the end of a Zephyr linker section so we need the externs and &. - ram_externs.append(f"extern uint32_t {start};") - start = "&" + start + if board_id in ["native_sim"]: + ram_externs.append("// This is a native board so we provide all of RAM for our heaps.") + ram_externs.append(f"static uint32_t _{device}[{size // 4}]; // {path}") + start = f"(const uint32_t *) (_{device})" + end = f"(const uint32_t *)(_{device} + {size // 4})" + else: + ram_externs.append(f"extern uint32_t {start};") + start = "&" + start ram_list.append(f" {start}, {end}, // {path}") ram_list = "\n".join(ram_list) ram_externs = "\n".join(ram_externs) diff --git a/ports/zephyr-cp/supervisor/flash.c b/ports/zephyr-cp/supervisor/flash.c index 8daef5fcdaa0d..38f35a5235afa 100644 --- a/ports/zephyr-cp/supervisor/flash.c +++ b/ports/zephyr-cp/supervisor/flash.c @@ -21,7 +21,7 @@ #include #define CIRCUITPY_PARTITION circuitpy_partition -static struct flash_area *filesystem_area = NULL; +static const struct flash_area *filesystem_area = NULL; #if !FIXED_PARTITION_EXISTS(CIRCUITPY_PARTITION) static struct flash_area _dynamic_area; diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 799e44800cf9a..7be2beb1d2085 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -7,6 +7,7 @@ #include "supervisor/port.h" #include "mpconfigboard.h" +#include "supervisor/shared/tick.h" #include #include @@ -66,6 +67,10 @@ void port_wake_main_task_from_isr(void) { void port_yield(void) { k_yield(); + // Make sure time advances in the simulator. + #if defined(CONFIG_ARCH_POSIX) + k_busy_wait(100); + #endif } void port_boot_info(void) { @@ -73,14 +78,14 @@ void port_boot_info(void) { // Get stack limit address uint32_t *port_stack_get_limit(void) { - return k_current_get()->stack_info.start; + return (uint32_t *)k_current_get()->stack_info.start; } // Get stack top address uint32_t *port_stack_get_top(void) { _thread_stack_info_t stack_info = k_current_get()->stack_info; - return stack_info.start + stack_info.size - stack_info.delta; + return (uint32_t *)(stack_info.start + stack_info.size - stack_info.delta); } // Save and retrieve a word from memory that is preserved over reset. Used for safe mode. @@ -181,7 +186,10 @@ size_t port_heap_get_largest_free_size(void) { void assert_post_action(const char *file, unsigned int line) { printk("Assertion failed at %s:%u\n", file, line); + // Check that this is arm + #if defined(__arm__) __asm__ ("bkpt"); + #endif while (1) { ; } diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index 45b2e4f7d848a..40c5865ed1c68 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -74,10 +74,10 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { #elif defined(__i386__) static void gc_helper_get_regs(gc_helper_regs_t arr) { - register long ebx asm ("ebx"); - register long esi asm ("esi"); - register long edi asm ("edi"); - register long ebp asm ("ebp"); + register long ebx __asm__ ("ebx"); + register long esi __asm__ ("esi"); + register long edi __asm__ ("edi"); + register long ebp __asm__ ("ebp"); #ifdef __clang__ // TODO: // This is dirty workaround for Clang. It tries to get around diff --git a/supervisor/shared/background_callback.c b/supervisor/shared/background_callback.c index ffeb78bbb8371..02aa9ae84d561 100644 --- a/supervisor/shared/background_callback.c +++ b/supervisor/shared/background_callback.c @@ -61,6 +61,7 @@ static int background_prevention_count; void PLACE_IN_ITCM(background_callback_run_all)(void) { port_background_task(); if (!background_callback_pending()) { + port_yield(); return; } CALLBACK_CRITICAL_BEGIN; @@ -87,6 +88,7 @@ void PLACE_IN_ITCM(background_callback_run_all)(void) { } --background_prevention_count; CALLBACK_CRITICAL_END; + port_yield(); } void background_callback_prevent(void) { diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index 5f24618f7f39b..a3e4de235c7bd 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -18,6 +18,8 @@ #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" +#include + #define SAFE_MODE_DATA_GUARD 0xad0000af #define SAFE_MODE_DATA_GUARD_MASK 0xff0000ff @@ -78,6 +80,7 @@ safe_mode_t wait_for_safe_mode_reset(void) { boot_in_safe_mode = true; break; } + port_yield(); diff = supervisor_ticks_ms64() - start_ticks; } #if CIRCUITPY_STATUS_LED @@ -99,10 +102,19 @@ void PLACE_IN_ITCM(safe_mode_on_next_reset)(safe_mode_t reason) { // Don't inline this so it's easy to break on it from GDB. void __attribute__((noinline, )) PLACE_IN_ITCM(reset_into_safe_mode)(safe_mode_t reason) { if (_safe_mode > SAFE_MODE_BROWNOUT && reason > SAFE_MODE_BROWNOUT) { + #if __ZEPHYR__ + printk("Already in safe mode\n"); + printk("Reason: %d\n", reason); + printk("Current safe mode: %d\n", _safe_mode); + while (true) { + k_cpu_idle(); + } + #else while (true) { // This very bad because it means running in safe mode didn't save us. Only ignore brownout // because it may be due to a switch bouncing. } + #endif } safe_mode_on_next_reset(reason); From 885428e43274856a27d265f4ca3acd5a04a1d916 Mon Sep 17 00:00:00 2001 From: Sean Carolan Date: Fri, 30 Jan 2026 15:04:38 -0500 Subject: [PATCH 286/517] Add M5Stack CoreS3 SE board definition The CoreS3 SE is a variant of the CoreS3 with the camera, IMU, magnetometer, proximity sensor, and audio codec removed, paired with the M5GO Battery Bottom3 which adds 10 NeoPixels (GPIO5) and an IR transmitter (GPIO7). Based on the CoreS3 board definition with the following changes: - Board name set to "M5Stack CoreS3 SE" - NEOPIXEL pin defined (GPIO5) for M5GO Bottom3 LEDs - Camera pin definitions and data tuple removed from pins.c - NEOPIXEL and IR named pin aliases added to pins.c - ESPCAMERA support disabled in mpconfigboard.mk - Camera-related AXP2101 power rails (ALDO2/ALDO3) disabled in board.c - Camera config removed from sdkconfig - SD card CS (GPIO4) driven high before display init to mitigate shared SPI bus contention (see #10536) Note: SD card does not work on any Core S3 variant in CircuitPython due to GPIO35 being shared between SPI MISO and display DC pin (upstream issues #10536, #6290). Tested on hardware: display, I2C (AXP2101, AW9523B, FT6336, BM8563), NeoPixels, IR pin, I2S speaker pins, Port B/C GPIOs all verified. Co-Authored-By: Claude Opus 4.5 --- .../boards/m5stack_cores3_se/board.c | 224 ++++++++++++++++++ .../boards/m5stack_cores3_se/mpconfigboard.h | 25 ++ .../boards/m5stack_cores3_se/mpconfigboard.mk | 27 +++ .../espressif/boards/m5stack_cores3_se/pins.c | 87 +++++++ .../boards/m5stack_cores3_se/sdkconfig | 14 ++ 5 files changed, 377 insertions(+) create mode 100644 ports/espressif/boards/m5stack_cores3_se/board.c create mode 100644 ports/espressif/boards/m5stack_cores3_se/mpconfigboard.h create mode 100644 ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk create mode 100644 ports/espressif/boards/m5stack_cores3_se/pins.c create mode 100644 ports/espressif/boards/m5stack_cores3_se/sdkconfig diff --git a/ports/espressif/boards/m5stack_cores3_se/board.c b/ports/espressif/boards/m5stack_cores3_se/board.c new file mode 100644 index 0000000000000..cf06bd8488d86 --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/board.c @@ -0,0 +1,224 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" +#include "common-hal/microcontroller/Pin.h" + + +#define DELAY 0x80 +#define AXP2101_I2C_ADDRESS 0x34 +#define AW9523B_I2C_ADDRESS 0x58 + +uint8_t display_init_sequence[] = { + 0x01, DELAY, 0x80, // Software reset then delay 0x80 (128ms) + 0xC8, 0x03, 0xFF, 0x93, 0x42, // Turn on the external command + 0xC0, 0x02, 0x12, 0x12, // Power Control 1 + 0xC1, 0x01, 0x03, // Power Control 2 + 0xC5, 0x01, 0xF2, // VCOM Control 1 + 0xB0, 0x01, 0xE0, // RGB Interface SYNC Mode + 0xF6, 0x03, 0x01, 0x00, 0x00, // Interface control + 0XE0, 0x0F, 0x00, 0x0C, 0x11, 0x04, 0x11, 0x08, 0x37, 0x89, 0x4C, 0x06, 0x0C, 0x0A, 0x2E, 0x34, 0x0F, // Positive Gamma Correction + 0xE1, 0x0F, 0x00, 0x0B, 0x11, 0x05, 0x13, 0x09, 0x33, 0x67, 0x48, 0x07, 0x0E, 0x0B, 0x2E, 0x33, 0x0F, // Negative Gamma Correction + 0xB6, 0x04, 0x08, 0x82, 0x1D, 0x04, // Display Function Control + 0x3A, 0x01, 0x55, // COLMOD: Pixel Format Set 16 bit + 0x21, 0x00, // Display inversion ON + 0x36, 0x01, 0x08, // Memory Access Control: RGB order + 0x11, DELAY, 0x78, // Exit Sleep then delay 0x78 (120ms) + 0x29, DELAY, 0x78, // Display on then delay 0x78 (120ms) +}; + +static bool display_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + &pin_GPIO35, // DC + &pin_GPIO3, // CS + NULL, // RST + 40000000, // baudrate + 0, // polarity + 0 // phase + ); + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 320, // width (after rotation) + 240, // height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + NULL, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 61, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); + + return true; +} + +static bool axp2101_init(busio_i2c_obj_t *i2c) { + int rc; + uint8_t write_buf[2]; + + // 0x90 = 0b1011_0011 // LDOS ON/OFF control 0 + // Compared to CoreS3: ALDO2 (bit 2) and ALDO3 (bit 3) disabled (no ES7210/camera) + write_buf[0] = 0x90; + write_buf[1] = 0b10110011; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x92, 0x0D // ALDO1 set to 1.8v for AW88298 + write_buf[0] = 0x92; + write_buf[1] = 0x0D; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x95, 0x1C // ALDO4 set to 3.3v for TF card slot + write_buf[0] = 0x95; + write_buf[1] = 0x1C; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x99, 0x18 // DLDO1 set to 2.9v for TFT backlight + write_buf[0] = 0x99; + write_buf[1] = 0x18; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x27, 0x00 // PowerKey Hold=1sec / PowerOff=4sec + write_buf[0] = 0x27; + write_buf[1] = 0x00; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x69, 0x11 // CHGLED setting + write_buf[0] = 0x69; + write_buf[1] = 0x11; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x10, 0x30 // PMU common config + write_buf[0] = 0x10; + write_buf[1] = 0x30; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + return true; +} + +static bool aw9523b_init(busio_i2c_obj_t *i2c) { + int rc; + uint8_t write_buf[2]; + + // 0x02 = 0b0000_0111 // AW_RST, BUD_OUT_EN, TOUCH_RST + write_buf[0] = 0x02; + write_buf[1] = 0b00000111; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x03 = 0b1000_0011 // BOOST_EN, CAM_RST, LCD_RST + write_buf[0] = 0x03; + write_buf[1] = 0b10000011; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x04 = 0b0001_1000 // Set TF_SW, ES_INT as input + write_buf[0] = 0x04; + write_buf[1] = 0b00011000; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x05 = 0b0000_1100 // Set AW_INT, TOUCH_INT as input + write_buf[0] = 0x05; + write_buf[1] = 0b00001100; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x11 = 0b0001_0000 // Set P0 outputs in push pull mode + write_buf[0] = 0x11; + write_buf[1] = 0b00010000; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + return true; +} + +void board_init(void) { + // Deselect SD card on shared SPI bus before display init (see #10536) + config_pin_as_output_with_level(GPIO_NUM_4, true); + + busio_i2c_obj_t *internal_i2c = common_hal_board_create_i2c(0); + + if (!axp2101_init(internal_i2c)) { + mp_printf(&mp_plat_print, "could not initialize AXP2101"); + return; + } + + if (!aw9523b_init(internal_i2c)) { + mp_printf(&mp_plat_print, "could not initialize AW9523B"); + return; + } + + if (!display_init()) { + mp_printf(&mp_plat_print, "could not initialize the display"); + return; + } +} diff --git a/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.h b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.h new file mode 100644 index 0000000000000..070ccf9195e5b --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.h @@ -0,0 +1,25 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "M5Stack CoreS3 SE" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO5) + +#define CIRCUITPY_BOARD_I2C (2) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO11, .sda = &pin_GPIO12}, \ + {.scl = &pin_GPIO1, .sda = &pin_GPIO2}} + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO36) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO37) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO35) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO18) +#define DEFAULT_UART_BUS_TX (&pin_GPIO17) diff --git a/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk new file mode 100644 index 0000000000000..6b43abb68396f --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk @@ -0,0 +1,27 @@ +USB_VID = 0x303A +USB_PID = 0x811B + +USB_PRODUCT = "M5Stack CoreS3 SE" +USB_MANUFACTURER = "M5Stack" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +CIRCUITPY_PARALLELDISPLAYBUS = 0 + +OPTIMIZATION_FLAGS = -Os + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Shapes +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests diff --git a/ports/espressif/boards/m5stack_cores3_se/pins.c b/ports/espressif/boards/m5stack_cores3_se/pins.c new file mode 100644 index 0000000000000..f0e20926bfa67 --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/pins.c @@ -0,0 +1,87 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(porta_i2c, i2c, 1) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // M5 Bus (except I2S) + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_D44), MP_ROM_PTR(&pin_GPIO44) }, + + { MP_ROM_QSTR(MP_QSTR_PORTC_RX), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_A18), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_PORTA_SDA), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_A8), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_A9), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_D43), MP_ROM_PTR(&pin_GPIO43) }, + + { MP_ROM_QSTR(MP_QSTR_PORTC_TX), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_A17), MP_ROM_PTR(&pin_GPIO17) }, + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_PORTA_SCL), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_IR), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO7) }, + + // I2S + { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_OUT), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_IN), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, + + // Display + { MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO35) }, + + // Misc + { MP_ROM_QSTR(MP_QSTR_I2C_INTERRUPT), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_SDCARD_CS), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_I2C), MP_ROM_PTR(&board_porta_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)} +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/m5stack_cores3_se/sdkconfig b/ports/espressif/boards/m5stack_cores3_se/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration From 069ad6aecffe6ae48b9bdc34ace41791d50a39d7 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 27 Jan 2026 16:19:40 -0800 Subject: [PATCH 287/517] Add basic native sim tests --- .github/workflows/run-tests.yml | 2 +- ports/zephyr-cp/CMakeLists.txt | 5 + ports/zephyr-cp/Makefile | 3 +- ports/zephyr-cp/boards/native_sim.conf | 14 + ports/zephyr-cp/boards/native_sim.overlay | 12 + ports/zephyr-cp/docs/perfetto-tracing.md | 150 +++++ ports/zephyr-cp/native_sim_i2c_emul_control.c | 169 +++++ ports/zephyr-cp/tests/TEST_IDEAS.md | 623 ++++++++++++++++++ ports/zephyr-cp/tests/conftest.py | 312 +++++++++ .../docs/i2c_emulator_cmdline_control.md | 306 +++++++++ ports/zephyr-cp/tests/test_basics.py | 199 ++++++ ports/zephyr-cp/tests/test_i2c.py | 98 +++ 12 files changed, 1891 insertions(+), 2 deletions(-) create mode 100644 ports/zephyr-cp/docs/perfetto-tracing.md create mode 100644 ports/zephyr-cp/native_sim_i2c_emul_control.c create mode 100644 ports/zephyr-cp/tests/TEST_IDEAS.md create mode 100644 ports/zephyr-cp/tests/conftest.py create mode 100644 ports/zephyr-cp/tests/docs/i2c_emulator_cmdline_control.md create mode 100644 ports/zephyr-cp/tests/test_basics.py create mode 100644 ports/zephyr-cp/tests/test_i2c.py diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 06fe3bf6aea29..ed0dd65a4f1d3 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -88,5 +88,5 @@ jobs: uses: ./.github/actions/deps/ports/zephyr-cp - name: Set up external uses: ./.github/actions/deps/external - - name: Run Zephyr build tests + - name: Run Zephyr tests run: make -C ports/zephyr-cp test diff --git a/ports/zephyr-cp/CMakeLists.txt b/ports/zephyr-cp/CMakeLists.txt index e35b4b7c764d2..0ba4a3c48b3de 100644 --- a/ports/zephyr-cp/CMakeLists.txt +++ b/ports/zephyr-cp/CMakeLists.txt @@ -5,6 +5,11 @@ project(circuitpython) target_sources(app PRIVATE zephyr_main.c) +# Add I2C emulator control for native_sim testing +if(CONFIG_BOARD_NATIVE_SIM) + target_sources(app PRIVATE native_sim_i2c_emul_control.c) +endif() + # From: https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/application_development/external_lib/CMakeLists.txt # The external static library that we are linking with does not know # how to build for this platform so we export all the flags used in diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index d31068e4c92ea..15ae523fb72ca 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -46,5 +46,6 @@ all: clean-all: rm -rf build build-* -test: +test: build-native_native_sim/zephyr-cp/zephyr/zephyr.exe pytest cptools/tests + pytest tests/ -v diff --git a/ports/zephyr-cp/boards/native_sim.conf b/ports/zephyr-cp/boards/native_sim.conf index baf808b51427d..cc295949d0351 100644 --- a/ports/zephyr-cp/boards/native_sim.conf +++ b/ports/zephyr-cp/boards/native_sim.conf @@ -4,3 +4,17 @@ CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME=n # So we can test safe mode CONFIG_NATIVE_SIM_REBOOT=y + +CONFIG_TRACING=y +CONFIG_TRACING_PERFETTO=y +CONFIG_TRACING_SYNC=y +CONFIG_TRACING_BACKEND_POSIX=y +CONFIG_TRACING_GPIO=y + +# I2C emulation for testing +CONFIG_I2C_EMUL=y + +# EEPROM emulation for testing +CONFIG_EEPROM=y +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT2X_EMUL=y diff --git a/ports/zephyr-cp/boards/native_sim.overlay b/ports/zephyr-cp/boards/native_sim.overlay index 6a13f58379ac4..2a07108627fc9 100644 --- a/ports/zephyr-cp/boards/native_sim.overlay +++ b/ports/zephyr-cp/boards/native_sim.overlay @@ -36,4 +36,16 @@ }; }; +/* Add emulated I2C devices for testing */ +&i2c0 { + at24_eeprom: eeprom@50 { + compatible = "atmel,at24"; + reg = <0x50>; + size = <256>; + pagesize = <8>; + address-width = <8>; + timeout = <5>; + }; +}; + #include "../app.overlay" diff --git a/ports/zephyr-cp/docs/perfetto-tracing.md b/ports/zephyr-cp/docs/perfetto-tracing.md new file mode 100644 index 0000000000000..ff35d361f70bd --- /dev/null +++ b/ports/zephyr-cp/docs/perfetto-tracing.md @@ -0,0 +1,150 @@ +# Perfetto Tracing + +The Zephyr port supports Perfetto tracing for performance analysis. This document +describes how to capture, validate, and view traces. + +## Capturing Traces + +Traces are written to `circuitpython.perfetto-trace` in the port directory when +running with tracing enabled (e.g., on native_sim). + +## Validating Traces + +### Using trace_processor + +The Perfetto trace_processor tool can validate and query trace files: + +```bash +~/repos/perfetto/tools/trace_processor circuitpython.perfetto-trace +``` + +This will download the trace_processor binary if needed and open an interactive +SQL shell. If the trace loads successfully, you can query it: + +```sql +SELECT COUNT(*) FROM slice; +``` + +### Using the Perfetto UI + +Open https://ui.perfetto.dev and drag your trace file onto the page. + +## Debugging Invalid Traces + +### Common Error: Packets Skipped Due to Invalid Incremental State + +If trace_processor reports packets being skipped with messages like: + +``` +packet_skipped_seq_needs_incremental_state_invalid +``` + +This means packets have `SEQ_NEEDS_INCREMENTAL_STATE` (value 2) set but no +prior packet set `SEQ_INCREMENTAL_STATE_CLEARED` (value 1) to initialize the +incremental state. + +**Root Cause**: The process descriptor packet (which sets `SEQ_INCREMENTAL_STATE_CLEARED`) +must be emitted before any other trace packets. + +**Diagnosis**: Use protoc to inspect the raw trace: + +```bash +protoc --decode_raw < circuitpython.perfetto-trace | head -100 +``` + +Look for field 13 (sequence_flags) in the first few packets: + +- `13: 1` = SEQ_INCREMENTAL_STATE_CLEARED (good - should be first) +- `13: 2` = SEQ_NEEDS_INCREMENTAL_STATE (requires prior cleared packet) + +A valid trace should have the process descriptor with `13: 1` as one of the +first packets. + +**Fix**: Ensure `perfetto_start()` is called before any trace events are emitted. +The descriptor emit functions in `perfetto_encoder.c` should check: + +```c +if (!started) { + perfetto_start(); +} +``` + +### Analyzing Raw Trace Structure + +To understand the trace structure: + +```bash +# Count total packets +protoc --decode_raw < circuitpython.perfetto-trace | grep -c "^1 {" + +# Find all sequence_flags values +protoc --decode_raw < circuitpython.perfetto-trace | grep "13:" | sort | uniq -c + +# Look for track descriptors (field 60) +protoc --decode_raw < circuitpython.perfetto-trace | grep -A20 "60 {" + +# Look for process descriptors (field 3 inside track_descriptor) +protoc --decode_raw < circuitpython.perfetto-trace | grep -B5 "3 {" +``` + +### Key Protobuf Field Numbers + +TracePacket fields: + +| Field | Description | +|-------|-------------| +| 8 | timestamp | +| 10 | trusted_packet_sequence_id | +| 11 | track_event | +| 12 | interned_data | +| 13 | sequence_flags | +| 60 | track_descriptor | + +TrackDescriptor fields (inside field 60): + +| Field | Description | +|-------|-------------| +| 1 | uuid | +| 2 | name | +| 3 | process (ProcessDescriptor) | +| 4 | thread (ThreadDescriptor) | +| 5 | parent_uuid | + +## Build Verification + +After modifying tracing code, verify the build is updated: + +```bash +# Check source vs object file timestamps +ls -la zephyr/subsys/tracing/perfetto/perfetto_encoder.c +ls -la zephyr/build/zephyr/subsys/tracing/perfetto/CMakeFiles/subsys__tracing__perfetto.dir/perfetto_encoder.c.obj +``` + +The object file timestamp must be newer than the source file timestamp. If not, +rebuild the project before capturing a new trace. + +## Architecture + +The tracing implementation consists of: + +- `perfetto_encoder.c`: Encodes trace packets using nanopb +- `perfetto_top.c`: Implements Zephyr tracing hooks (sys_trace_*) +- `perfetto_encoder.h`: Public API and UUID definitions + +Key UUIDs: + +| Constant | Value | Description | +|----------|-------|-------------| +| PROCESS_UUID | 1 | Root process track | +| ISR_TRACK_UUID | 2 | Interrupt service routine track | +| TRACE_TRACK_UUID | 3 | Top-level trace track | + +### Initialization Flow + +1. `SYS_INIT` calls `perfetto_init()` at POST_KERNEL priority 0 +2. `perfetto_init()` calls `perfetto_encoder_init()` +3. `perfetto_initialized` is set to true +4. Thread hooks start firing +5. First emit function calls `perfetto_start()` +6. `perfetto_start()` emits process descriptor with `SEQ_INCREMENTAL_STATE_CLEARED` +7. Subsequent packets use `SEQ_NEEDS_INCREMENTAL_STATE` diff --git a/ports/zephyr-cp/native_sim_i2c_emul_control.c b/ports/zephyr-cp/native_sim_i2c_emul_control.c new file mode 100644 index 0000000000000..de467122a7bf5 --- /dev/null +++ b/ports/zephyr-cp/native_sim_i2c_emul_control.c @@ -0,0 +1,169 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Command-line control for enabling/disabling emulated I2C devices + * on native_sim. This allows testing device hot-plug and error scenarios. + */ + +#include +#include +#include +#include +#include +#include + +#include "nsi_cmdline.h" +#include "posix_native_task.h" + +LOG_MODULE_REGISTER(i2c_emul_control, LOG_LEVEL_INF); + +#define MAX_DISABLED_DEVICES 16 + +struct disabled_device { + const char *name; + const struct emul *emul; + struct i2c_emul_api mock_api; + bool disabled; +}; + +static struct disabled_device disabled_devices[MAX_DISABLED_DEVICES]; +static int num_disabled_devices = 0; + +static char *disabled_device_args[MAX_DISABLED_DEVICES]; +static int num_disabled_device_args = 0; + +/* + * Mock transfer function that returns -EIO (NACK) when device is disabled, + * or -ENOSYS to fall back to the real emulator. + */ +static int disabled_device_transfer(const struct emul *target, + struct i2c_msg *msgs, + int num_msgs, + int addr) { + ARG_UNUSED(msgs); + ARG_UNUSED(num_msgs); + ARG_UNUSED(addr); + + for (int i = 0; i < num_disabled_devices; i++) { + if (disabled_devices[i].emul == target) { + if (disabled_devices[i].disabled) { + LOG_DBG("Device %s is disabled, returning -EIO", + disabled_devices[i].name); + return -EIO; + } + break; + } + } + /* Fall back to normal emulator behavior */ + return -ENOSYS; +} + +int i2c_emul_control_disable_device(const char *name) { + const struct emul *emul = emul_get_binding(name); + if (!emul) { + LOG_ERR("Emulator '%s' not found", name); + return -ENODEV; + } + + if (emul->bus_type != EMUL_BUS_TYPE_I2C) { + LOG_ERR("Emulator '%s' is not an I2C device", name); + return -EINVAL; + } + + /* Find existing entry or create new one */ + int idx = -1; + for (int i = 0; i < num_disabled_devices; i++) { + if (disabled_devices[i].emul == emul) { + idx = i; + break; + } + } + + if (idx < 0) { + if (num_disabled_devices >= MAX_DISABLED_DEVICES) { + LOG_ERR("Too many disabled devices"); + return -ENOMEM; + } + idx = num_disabled_devices++; + disabled_devices[idx].name = name; + disabled_devices[idx].emul = emul; + disabled_devices[idx].mock_api.transfer = disabled_device_transfer; + + /* Install our mock_api to intercept transfers */ + emul->bus.i2c->mock_api = &disabled_devices[idx].mock_api; + } + + disabled_devices[idx].disabled = true; + LOG_INF("Disabled I2C emulator: %s", name); + return 0; +} + +int i2c_emul_control_enable_device(const char *name) { + for (int i = 0; i < num_disabled_devices; i++) { + if (strcmp(disabled_devices[i].name, name) == 0) { + disabled_devices[i].disabled = false; + LOG_INF("Enabled I2C emulator: %s", name); + return 0; + } + } + LOG_ERR("Device '%s' not in disabled list", name); + return -ENODEV; +} + +bool i2c_emul_control_is_disabled(const char *name) { + for (int i = 0; i < num_disabled_devices; i++) { + if (strcmp(disabled_devices[i].name, name) == 0) { + return disabled_devices[i].disabled; + } + } + return false; +} + +/* Command-line option handler */ +static void cmd_disable_i2c_device(char *argv, int offset) { + /* The value is at argv + offset (after the '=' in --disable-i2c=value) */ + char *value = argv + offset; + if (num_disabled_device_args < MAX_DISABLED_DEVICES) { + disabled_device_args[num_disabled_device_args++] = value; + } else { + printk("i2c_emul_control: Too many --disable-i2c arguments, ignoring: %s\n", value); + } +} + +static struct args_struct_t i2c_emul_args[] = { + { + .option = "disable-i2c", + .name = "device", + .type = 's', + .call_when_found = cmd_disable_i2c_device, + .descript = "Disable an emulated I2C device by name (can be repeated). " + "Example: --disable-i2c=bmi160" + }, + ARG_TABLE_ENDMARKER +}; + +static void register_cmdline_opts(void) { + nsi_add_command_line_opts(i2c_emul_args); +} + +/* Register command-line options early in boot */ +NATIVE_TASK(register_cmdline_opts, PRE_BOOT_1, 0); + +static int apply_disabled_devices(void) { + LOG_DBG("Applying %d disabled device(s)", num_disabled_device_args); + for (int i = 0; i < num_disabled_device_args; i++) { + int rc = i2c_emul_control_disable_device(disabled_device_args[i]); + if (rc != 0) { + LOG_WRN("Failed to disable I2C device '%s': %d", + disabled_device_args[i], rc); + } + } + return 0; +} + +/* + * Apply after emulators are initialized. + * I2C emulators are registered at POST_KERNEL level, so we need to run + * at APPLICATION level to ensure they exist. + */ +SYS_INIT(apply_disabled_devices, APPLICATION, 99); diff --git a/ports/zephyr-cp/tests/TEST_IDEAS.md b/ports/zephyr-cp/tests/TEST_IDEAS.md new file mode 100644 index 0000000000000..6ef71d7a5a041 --- /dev/null +++ b/ports/zephyr-cp/tests/TEST_IDEAS.md @@ -0,0 +1,623 @@ +# CircuitPython Simulator Test Ideas + +Test ideas for the native_sim simulator, organized by module/category. + +## Core Python / Interpreter + +### 1. Multiple file priority +Test that boot.py runs before code.py, and main.py is used as fallback when code.py doesn't exist. + +```python +# boot.py +print("boot.py ran") + +# code.py +print("code.py ran") +``` + +Expected output order: "boot.py ran" then "code.py ran" + +### 2. Exception handling +Verify tracebacks print correctly with file/line info, and exception types propagate properly. + +```python +def inner(): + raise ValueError("test error") + +def outer(): + inner() + +outer() +``` + +### 3. Memory / gc module +Test `gc.collect()`, `gc.mem_free()`, `gc.mem_alloc()`, and behavior under memory pressure. + +```python +import gc +gc.collect() +free_before = gc.mem_free() +data = [0] * 1000 +free_after = gc.mem_free() +assert free_after < free_before +del data +gc.collect() +free_final = gc.mem_free() +print("done") +``` + +### 4. Import system +Test importing frozen modules vs filesystem modules, verify import errors are clear. + +```python +import sys +import board +import digitalio +print(f"modules loaded: {len(sys.modules)}") +print("done") +``` + +--- + +## digitalio + +### 5. Input mode +Test reading GPIO input state. May require trace file injection or loopback configuration. + +```python +import board +import digitalio + +pin = digitalio.DigitalInOut(board.D0) +pin.direction = digitalio.Direction.INPUT +value = pin.value +print(f"input value: {value}") +print("done") +``` + +### 6. Pull resistors +Verify pull-up/pull-down configuration affects input readings. + +```python +import board +import digitalio + +pin = digitalio.DigitalInOut(board.D0) +pin.direction = digitalio.Direction.INPUT +pin.pull = digitalio.Pull.UP +value_up = pin.value +pin.pull = digitalio.Pull.DOWN +value_down = pin.value +print(f"pull-up: {value_up}, pull-down: {value_down}") +print("done") +``` + +### 7. Direction switching +Switch same pin between input/output modes multiple times. + +```python +import board +import digitalio + +pin = digitalio.DigitalInOut(board.D0) +pin.direction = digitalio.Direction.OUTPUT +pin.value = True +pin.direction = digitalio.Direction.INPUT +_ = pin.value +pin.direction = digitalio.Direction.OUTPUT +pin.value = False +print("done") +``` + +--- + +## time module + +### 8. time.sleep() precision +Verify sleep timing via Perfetto trace timestamps. Use GPIO transitions as timing markers. + +```python +import time +import board +import digitalio + +led = digitalio.DigitalInOut(board.LED) +led.direction = digitalio.Direction.OUTPUT + +led.value = True +time.sleep(0.05) # 50ms +led.value = False +time.sleep(0.1) # 100ms +led.value = True +time.sleep(0.05) # 50ms +led.value = False +print("done") +``` + +Verify trace shows: 50ms high, 100ms low, 50ms high pattern. + +### 9. time.monotonic() +Test monotonic clock increments correctly and never goes backward. + +```python +import time + +samples = [] +for _ in range(10): + samples.append(time.monotonic()) + time.sleep(0.01) + +# Verify monotonic increase +for i in range(1, len(samples)): + assert samples[i] > samples[i-1], "monotonic went backward!" + +elapsed = samples[-1] - samples[0] +assert 0.08 < elapsed < 0.15, f"unexpected elapsed: {elapsed}" +print("done") +``` + +### 10. time.localtime / struct_time +Test time structure operations if available. + +```python +import time + +t = time.localtime() +print(f"year: {t.tm_year}") +print(f"month: {t.tm_mon}") +print(f"day: {t.tm_mday}") +print("done") +``` + +--- + +## microcontroller + +### 11. microcontroller.cpu properties +Test frequency, temperature, UID properties. + +```python +import microcontroller + +print(f"frequency: {microcontroller.cpu.frequency}") +print(f"uid: {microcontroller.cpu.uid.hex()}") +print("done") +``` + +### 12. Safe mode +Trigger and verify safe mode entry (requires CONFIG_NATIVE_SIM_REBOOT=y). + +```python +import microcontroller +# This would trigger safe mode - test carefully +# microcontroller.on_next_reset(microcontroller.RunMode.SAFE_MODE) +# microcontroller.reset() +print("done") +``` + +### 13. Reset reason +Test microcontroller.reset_reason after various reset types. + +```python +import microcontroller + +reason = microcontroller.reset_reason +print(f"reset reason: {reason}") +print("done") +``` + +--- + +## os module + +### 14. os.uname() +Verify returns correct system info for native_sim. + +```python +import os + +info = os.uname() +print(f"sysname: {info.sysname}") +print(f"machine: {info.machine}") +print(f"release: {info.release}") +print("done") +``` + +### 15. Filesystem operations +Test os.listdir(), os.stat(), os.remove(). + +```python +import os + +# List root +files = os.listdir("/") +print(f"root files: {files}") + +# Stat a file +if "code.py" in files: + stat = os.stat("/code.py") + print(f"code.py size: {stat[6]}") + +print("done") +``` + +### 16. os.getenv() +Test environment variable reading. + +```python +import os + +# May return None if not set +path = os.getenv("PATH") +print(f"PATH exists: {path is not None}") +print("done") +``` + +--- + +## board module + +### 17. board.board_id +Verify board ID matches expected value. + +```python +import board + +print(f"board_id: {board.board_id}") +assert board.board_id == "native_native_sim" +print("done") +``` + +### 18. Pin availability +Test that board.LED and other defined pins exist. + +```python +import board + +assert hasattr(board, "LED"), "board.LED missing" +print(f"LED pin: {board.LED}") +print("done") +``` + +--- + +## Filesystem + +### 19. File read/write +Create, read, and modify files on CIRCUITPY. + +```python +# Write a test file +with open("/test.txt", "w") as f: + f.write("hello world") + +# Read it back +with open("/test.txt", "r") as f: + content = f.read() + +assert content == "hello world" +print("done") +``` + +### 20. Directory operations +Test mkdir, rmdir, nested paths. + +```python +import os + +# Create directory +os.mkdir("/testdir") +assert "testdir" in os.listdir("/") + +# Create file in directory +with open("/testdir/file.txt", "w") as f: + f.write("nested") + +# Clean up +os.remove("/testdir/file.txt") +os.rmdir("/testdir") +assert "testdir" not in os.listdir("/") +print("done") +``` + +### 21. Large file handling +Test with larger files approaching flash limits. + +```python +import gc + +# Write 10KB file +data = "x" * 10240 +with open("/large.txt", "w") as f: + f.write(data) + +# Verify size +import os +stat = os.stat("/large.txt") +assert stat[6] == 10240 + +# Clean up +os.remove("/large.txt") +gc.collect() +print("done") +``` + +--- + +## Error Conditions + +### 22. Syntax error in code.py +Verify graceful error message when code.py has syntax error. + +```python +# code.py with intentional syntax error: +def broken( + print("missing close paren" +``` + +Expected: Clear syntax error message with line number. + +### 23. Runtime error +Verify traceback format shows file, line, and function. + +```python +def cause_error(): + x = 1 / 0 + +cause_error() +``` + +Expected: Traceback showing ZeroDivisionError with line info. + +### 24. Keyboard interrupt +Test Ctrl+C handling via PTY if applicable. + +```python +import time + +print("starting long loop") +for i in range(100): + print(f"iteration {i}") + time.sleep(0.1) +print("done") +``` + +Send Ctrl+C during execution, verify clean KeyboardInterrupt. + +--- + +## busio (if emulation available) + +### 25. busio.UART +Basic UART operations if configured. + +```python +import board +import busio + +# Check if UART pins exist +if hasattr(board, "TX") and hasattr(board, "RX"): + uart = busio.UART(board.TX, board.RX, baudrate=115200) + uart.write(b"test") + uart.deinit() +print("done") +``` + +### 26. busio.I2C scan +Scan for emulated I2C devices. + +```python +import board +import busio + +if hasattr(board, "SCL") and hasattr(board, "SDA"): + i2c = busio.I2C(board.SCL, board.SDA) + while not i2c.try_lock(): + pass + devices = i2c.scan() + print(f"I2C devices: {[hex(d) for d in devices]}") + i2c.unlock() + i2c.deinit() +print("done") +``` + +### 27. busio.SPI +Basic SPI transfer to emulated device. + +```python +import board +import busio + +if hasattr(board, "SCK") and hasattr(board, "MOSI"): + spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=getattr(board, "MISO", None)) + while not spi.try_lock(): + pass + spi.configure(baudrate=1000000) + result = bytearray(4) + spi.write_readinto(b"\x00\x00\x00\x00", result) + spi.unlock() + spi.deinit() +print("done") +``` + +--- + +## Serial/PTY Input + +These tests require extending conftest.py to support writing to the PTY, not just reading. + +### 28. Basic serial input +Test reading single characters from serial via PTY write. + +```python +import sys + +print("ready") +char = sys.stdin.read(1) +print(f"received: {repr(char)}") +print("done") +``` + +**Test harness**: After seeing "ready", write "A" to PTY, verify output shows "received: 'A'". + +### 29. input() function +Test the built-in input() function with PTY input. + +```python +print("ready") +name = input("Enter name: ") +print(f"hello {name}") +print("done") +``` + +**Test harness**: After seeing "Enter name:", write "World\n" to PTY, verify "hello World". + +### 30. Serial line buffering +Test reading a complete line with newline termination. + +```python +import sys + +print("ready") +line = sys.stdin.readline() +print(f"got: {repr(line)}") +print("done") +``` + +**Test harness**: Write "test line\n" to PTY, verify complete line received. + +### 31. usb_cdc.console data read +Test reading from usb_cdc.console.data if available. + +```python +import usb_cdc + +print("ready") +if usb_cdc.console: + while not usb_cdc.console.in_waiting: + pass + data = usb_cdc.console.read(usb_cdc.console.in_waiting) + print(f"console got: {data}") +print("done") +``` + +**Test harness**: Write bytes to PTY, verify they're received via usb_cdc.console. + +### 32. REPL interaction +Test entering REPL mode and executing commands interactively. + +```python +# No code.py - boots to REPL +``` + +**Test harness**: +1. Boot with empty/no code.py to get REPL prompt +2. Write "1 + 1\r\n" to PTY +3. Verify output contains "2" +4. Write "print('hello')\r\n" +5. Verify output contains "hello" + +### 33. Ctrl+C interrupt via PTY +Test sending Ctrl+C (0x03) to interrupt running code. + +```python +import time + +print("starting") +for i in range(100): + print(f"loop {i}") + time.sleep(0.1) +print("completed") # Should not reach this +``` + +**Test harness**: +1. Wait for "loop 5" in output +2. Write b"\x03" (Ctrl+C) to PTY +3. Verify KeyboardInterrupt raised +4. Verify "completed" NOT in output + +### 34. Ctrl+D soft reload via PTY +Test sending Ctrl+D (0x04) to trigger soft reload. + +```python +print("first run") +import time +time.sleep(10) # Long sleep to allow interrupt +print("done") +``` + +**Test harness**: +1. Wait for "first run" +2. Write b"\x04" (Ctrl+D) to PTY +3. Verify code restarts (see "first run" again or reload message) + +### 35. Serial input timeout +Test behavior when waiting for input with timeout. + +```python +import sys +import select + +print("ready") +# Poll for input with timeout +readable, _, _ = select.select([sys.stdin], [], [], 0.5) +if readable: + data = sys.stdin.read(1) + print(f"got: {repr(data)}") +else: + print("timeout") +print("done") +``` + +**Test harness**: Don't send anything, verify "timeout" appears. + +--- + +## Fixture Changes for PTY Input + +The `run_circuitpython` fixture in conftest.py needs to be extended: + +```python +@dataclass +class SimulatorResult: + output: str + trace_file: Path + pty_write_fd: int # New: file descriptor for writing to PTY + +def _run(code: str | None, timeout: float = 5.0, ...) -> SimulatorResult: + # Open PTY for both read AND write + pty_fd = os.open(pty_path, os.O_RDWR | os.O_NONBLOCK) + # ... +``` + +Or provide a callback/queue mechanism: + +```python +def _run(code, timeout=5.0, input_sequence=None): + """ + input_sequence: list of (trigger_text, bytes_to_send) tuples + When trigger_text is seen in output, send bytes_to_send to PTY + """ +``` + +--- + +## Implementation Priority + +Suggested order for implementation: + +### Phase 1: Basic module tests (no fixture changes) +1. **#17 board.board_id** - Quick sanity check +2. **#14 os.uname()** - Tests another module, simple +3. **#9 time.monotonic()** - Core timing functionality +4. **#8 time.sleep() precision** - Builds on GPIO trace infrastructure +5. **#1 Multiple file priority** - Tests interpreter boot sequence +6. **#19 File read/write** - Tests filesystem layer +7. **#3 Memory / gc** - Tests memory management +8. **#2 Exception handling** - Tests error reporting + +### Phase 2: PTY input tests (requires fixture extension) +9. **#28 Basic serial input** - Foundation for all input tests +10. **#33 Ctrl+C interrupt** - Important for interactive use +11. **#29 input() function** - Common user pattern +12. **#32 REPL interaction** - Tests interactive mode diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py new file mode 100644 index 0000000000000..f26c7f0f45f55 --- /dev/null +++ b/ports/zephyr-cp/tests/conftest.py @@ -0,0 +1,312 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Pytest fixtures for CircuitPython native_sim testing.""" + +import logging +import os +import re +import select +import subprocess +import time +from dataclasses import dataclass +from pathlib import Path + +import pytest +from perfetto.trace_processor import TraceProcessor + +logger = logging.getLogger(__name__) + +ZEPHYR_CP = Path(__file__).parent.parent +BUILD_DIR = ZEPHYR_CP / "build-native_native_sim" +BINARY = BUILD_DIR / "zephyr-cp/zephyr/zephyr.exe" + + +@dataclass +class InputTrigger: + """A trigger for sending input to the simulator. + + Attributes: + trigger: Text to match in output to trigger input, or None for immediate send. + data: Bytes to send when triggered. + sent: Whether this trigger has been sent (set internally). + """ + + trigger: str | None + data: bytes + sent: bool = False + + +@dataclass +class SimulatorResult: + """Result from running CircuitPython on the simulator.""" + + output: str + trace_file: Path + + +def parse_gpio_trace(trace_file: Path, pin_name: str = "gpio_emul.00") -> list[tuple[int, int]]: + """Parse GPIO trace from Perfetto trace file. + + Args: + trace_file: Path to the Perfetto trace file. + pin_name: Name of the GPIO pin track (e.g., "gpio_emul.00"). + + Returns: + List of (timestamp_ns, value) tuples for the specified GPIO pin. + """ + tp = TraceProcessor(file_path=str(trace_file)) + result = tp.query( + f''' + SELECT c.ts, c.value + FROM counter c + JOIN track t ON c.track_id = t.id + WHERE t.name = "{pin_name}" + ORDER BY c.ts + ''' + ) + return [(row.ts, int(row.value)) for row in result] + + +def _iter_uart_tx_slices(trace_file: Path) -> list[tuple[int, int, str, str]]: + """Return UART TX slices as (timestamp_ns, duration_ns, text, device_name).""" + tp = TraceProcessor(file_path=str(trace_file)) + result = tp.query( + """ + SELECT s.ts, s.dur, s.name, dev.name AS device_name + FROM slice s + JOIN track tx ON s.track_id = tx.id + JOIN track dev ON tx.parent_id = dev.id + JOIN track uart ON dev.parent_id = uart.id + WHERE tx.name = "TX" AND uart.name = "UART" + ORDER BY s.ts + """ + ) + return [ + (int(row.ts), int(row.dur or 0), row.name or "", row.device_name or "UART") + for row in result + ] + + +def log_uart_trace_output(trace_file: Path) -> None: + """Log UART TX output from Perfetto trace with timestamps for line starts.""" + if not logger.isEnabledFor(logging.INFO): + return + slices = _iter_uart_tx_slices(trace_file) + if not slices: + return + + buffers: dict[str, list[str]] = {} + line_start_ts: dict[str, int | None] = {} + + for ts, dur, text, device in slices: + if device not in buffers: + buffers[device] = [] + line_start_ts[device] = None + + if not text: + continue + + char_step = dur / max(len(text), 1) if dur > 0 else 0.0 + for idx, ch in enumerate(text): + if line_start_ts[device] is None: + line_start_ts[device] = int(ts + idx * char_step) + buffers[device].append(ch) + if ch == "\n": + line_text = "".join(buffers[device]).rstrip("\n") + logger.info( + "UART trace %s @%d ns: %s", + device, + line_start_ts[device], + repr(line_text), + ) + buffers[device] = [] + line_start_ts[device] = None + + for device, buf in buffers.items(): + if buf: + logger.info( + "UART trace %s @%d ns (partial): %s", + device, + line_start_ts[device] or 0, + repr("".join(buf)), + ) + + +@pytest.fixture +def native_sim_binary(): + """Return path to native_sim binary, skip if not built.""" + if not BINARY.exists(): + pytest.skip(f"native_sim not built: {BINARY}") + return BINARY + + +@pytest.fixture +def create_flash_image(tmp_path): + """Factory fixture to create FAT flash images.""" + + def _create(files: dict[str, str]) -> Path: + flash = tmp_path / "flash.bin" + + # Create 2MB empty file + flash.write_bytes(b"\x00" * (2 * 1024 * 1024)) + + # Format as FAT (mformat) + subprocess.run(["mformat", "-i", str(flash), "::"], check=True) + + # Copy files (mcopy) + for name, content in files.items(): + src = tmp_path / name + src.write_text(content) + subprocess.run(["mcopy", "-i", str(flash), str(src), f"::{name}"], check=True) + + return flash + + return _create + + +@pytest.fixture +def run_circuitpython(native_sim_binary, create_flash_image, tmp_path): + """Run CircuitPython with given code string and return output from PTY. + + Args: + code: Python code to write to code.py, or None for no code.py. + timeout: Timeout in seconds for the simulation. + erase_flash: If True, erase flash before running. + input_sequence: List of InputTrigger objects. When trigger text is seen + in output, the corresponding data is written to the PTY. If trigger + is None, the data is sent immediately when PTY is opened. + """ + + def _run( + code: str | None, + timeout: float = 5.0, + erase_flash: bool = False, + input_sequence: list[InputTrigger] | None = None, + disabled_i2c_devices: list[str] | None = None, + ) -> SimulatorResult: + files = {"code.py": code} if code is not None else {} + flash = create_flash_image(files) + triggers = list(input_sequence) if input_sequence else [] + trace_file = tmp_path / "trace.perfetto" + + cmd = [ + str(native_sim_binary), + f"--flash={flash}", + "--flash_rm", + "-no-rt", + "-wait_uart", + f"-stop_at={timeout}", + f"--trace-file={trace_file}", + ] + if erase_flash: + cmd.append("--flash_erase") + if disabled_i2c_devices: + for device in disabled_i2c_devices: + cmd.append(f"--disable-i2c={device}") + logger.info("Running: %s", " ".join(cmd)) + + # Start the process + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + + pty_path = None + pty_fd = None + output = [] + stdout_lines = [] + + try: + # Read stdout to find the PTY path + start_time = time.time() + while time.time() - start_time < timeout + 5: + if proc.poll() is not None: + # Process exited + break + + # Check if stdout has data + ready, _, _ = select.select([proc.stdout], [], [], 0.1) + if ready: + line = proc.stdout.readline() + if not line: + break + + stdout_lines.append(line.rstrip()) + + # Look for PTY path + match = re.search(r"uart connected to pseudotty: (/dev/pts/\d+)", line) + if match: + pty_path = match.group(1) + # Open the PTY for reading and writing + pty_fd = os.open(pty_path, os.O_RDWR | os.O_NONBLOCK) + + # Send any immediate triggers (trigger=None) + for t in triggers: + if t.trigger is None and not t.sent: + os.write(pty_fd, t.data) + logger.info("PTY input (immediate): %r", t.data) + t.sent = True + break + + if pty_fd is None: + raise RuntimeError("Failed to find PTY path in output") + + def check_triggers(accumulated_output: str) -> None: + """Check accumulated output against triggers and send input.""" + for t in triggers: + if t.trigger is not None and not t.sent: + if t.trigger in accumulated_output: + os.write(pty_fd, t.data) + logger.info("PTY input (trigger %r): %r", t.trigger, t.data) + t.sent = True + + # Read from PTY until process exits or timeout + while time.time() - start_time < timeout + 1: + if proc.poll() is not None: + # Process exited, do one final read + try: + ready, _, _ = select.select([pty_fd], [], [], 0.1) + if ready: + data = os.read(pty_fd, 4096) + if data: + output.append(data.decode("utf-8", errors="replace")) + except (OSError, BlockingIOError): + pass + break + + # Check if PTY has data + try: + ready, _, _ = select.select([pty_fd], [], [], 0.1) + if ready: + data = os.read(pty_fd, 4096) + if data: + output.append(data.decode("utf-8", errors="replace")) + check_triggers("".join(output)) + except (OSError, BlockingIOError): + pass + + # Read any remaining stdout + remaining_stdout = proc.stdout.read() + if remaining_stdout: + stdout_lines.extend(remaining_stdout.rstrip().split("\n")) + + # Log stdout + for line in stdout_lines: + logger.info("stdout: %s", line) + + pty_output = "".join(output) + for line in pty_output.split("\n"): + logger.info("PTY output: %s", repr(line.strip())) + log_uart_trace_output(trace_file) + return SimulatorResult(output=pty_output, trace_file=trace_file) + + finally: + if pty_fd is not None: + os.close(pty_fd) + proc.terminate() + proc.wait(timeout=1) + + return _run diff --git a/ports/zephyr-cp/tests/docs/i2c_emulator_cmdline_control.md b/ports/zephyr-cp/tests/docs/i2c_emulator_cmdline_control.md new file mode 100644 index 0000000000000..8ee2925915c83 --- /dev/null +++ b/ports/zephyr-cp/tests/docs/i2c_emulator_cmdline_control.md @@ -0,0 +1,306 @@ +# Command-Line Control of Emulated I2C Devices in native_sim + +This document describes an approach for enabling/disabling emulated I2C devices +at runtime via command-line options in Zephyr's native_sim environment. + +## Background + +Zephyr's I2C emulation framework (`zephyr,i2c-emul-controller`) provides: + +1. **Bus emulation** - Fake I2C controller that routes transfers to emulated devices +2. **Device emulators** - Software implementations of I2C peripherals (sensors, etc.) +3. **Backend APIs** - Test interfaces for manipulating emulator state + +However, there's no built-in mechanism to enable/disable emulated devices from the +command line. This capability would be useful for: + +- Testing device hot-plug scenarios +- Simulating hardware failures +- Testing error handling paths in CircuitPython + +## Relevant Zephyr APIs + +### I2C Emulator Structure + +From `include/zephyr/drivers/i2c_emul.h`: + +```c +struct i2c_emul { + sys_snode_t node; + const struct emul *target; + const struct i2c_emul_api *api; + struct i2c_emul_api *mock_api; // If non-NULL, takes precedence + uint16_t addr; +}; + +struct i2c_emul_api { + i2c_emul_transfer_t transfer; +}; +``` + +Key insight: The `mock_api` field allows overriding the normal transfer function. +If `mock_api->transfer()` returns `-ENOSYS`, it falls back to the real API. + +### Command-Line Registration + +From `boards/native/native_sim/cmdline.h`: + +```c +void native_add_command_line_opts(struct args_struct_t *args); +``` + +### Emulator Lookup + +From `include/zephyr/drivers/emul.h`: + +```c +const struct emul *emul_get_binding(const char *name); +``` + +## Implementation Approach + +### 1. Create a Disabled Device Registry + +Track which devices are "disabled" (should NACK all transactions): + +```c +// i2c_emul_cmdline.c + +#include +#include +#include +#include "nsi_cmdline.h" + +#define MAX_DISABLED_DEVICES 16 + +static struct { + const char *name; + const struct emul *emul; + struct i2c_emul_api mock_api; + struct i2c_emul_api *original_mock_api; + bool disabled; +} disabled_devices[MAX_DISABLED_DEVICES]; + +static int num_disabled_devices = 0; +``` + +### 2. Mock Transfer Function + +Return `-EIO` (simulates NACK) when device is disabled: + +```c +static int disabled_device_transfer(const struct emul *target, + struct i2c_msg *msgs, + int num_msgs, + int addr) +{ + // Find this device in our registry + for (int i = 0; i < num_disabled_devices; i++) { + if (disabled_devices[i].emul == target) { + if (disabled_devices[i].disabled) { + // Device is disabled - simulate NACK + return -EIO; + } + break; + } + } + // Fall back to normal emulator behavior + return -ENOSYS; +} +``` + +### 3. Enable/Disable Functions + +```c +int i2c_emul_cmdline_disable_device(const char *name) +{ + const struct emul *emul = emul_get_binding(name); + if (!emul || emul->bus_type != EMUL_BUS_TYPE_I2C) { + return -ENODEV; + } + + // Find or create registry entry + int idx = -1; + for (int i = 0; i < num_disabled_devices; i++) { + if (disabled_devices[i].emul == emul) { + idx = i; + break; + } + } + + if (idx < 0) { + if (num_disabled_devices >= MAX_DISABLED_DEVICES) { + return -ENOMEM; + } + idx = num_disabled_devices++; + disabled_devices[idx].name = name; + disabled_devices[idx].emul = emul; + disabled_devices[idx].mock_api.transfer = disabled_device_transfer; + + // Save and replace mock_api + disabled_devices[idx].original_mock_api = emul->bus.i2c->mock_api; + emul->bus.i2c->mock_api = &disabled_devices[idx].mock_api; + } + + disabled_devices[idx].disabled = true; + return 0; +} + +int i2c_emul_cmdline_enable_device(const char *name) +{ + for (int i = 0; i < num_disabled_devices; i++) { + if (strcmp(disabled_devices[i].name, name) == 0) { + disabled_devices[i].disabled = false; + return 0; + } + } + return -ENODEV; +} +``` + +### 4. Command-Line Option Registration + +```c +static char *disabled_device_args[MAX_DISABLED_DEVICES]; +static int num_disabled_device_args = 0; + +static void cmd_disable_i2c_device(char *argv, int offset) +{ + ARG_UNUSED(offset); + if (num_disabled_device_args < MAX_DISABLED_DEVICES) { + disabled_device_args[num_disabled_device_args++] = argv; + } +} + +static struct args_struct_t i2c_emul_args[] = { + { + .option = "disable-i2c", + .name = "device_name", + .type = 's', + .call_when_found = cmd_disable_i2c_device, + .descript = "Disable an emulated I2C device (can be repeated)" + }, + ARG_TABLE_ENDMARKER +}; + +static void register_cmdline_opts(void) +{ + native_add_command_line_opts(i2c_emul_args); +} + +// Hook into native_sim initialization +NATIVE_TASK(register_cmdline_opts, PRE_BOOT_1, 0); + +static void apply_disabled_devices(void) +{ + for (int i = 0; i < num_disabled_device_args; i++) { + int rc = i2c_emul_cmdline_disable_device(disabled_device_args[i]); + if (rc != 0) { + printk("Warning: Failed to disable I2C device '%s': %d\n", + disabled_device_args[i], rc); + } + } +} + +// Apply after emulators are initialized +NATIVE_TASK(apply_disabled_devices, PRE_BOOT_3, 0); +``` + +### 5. Usage + +After building with this code: + +```bash +# Disable a sensor at startup +./build/zephyr/zephyr.exe --disable-i2c=bmi160@68 + +# Disable multiple devices +./build/zephyr/zephyr.exe --disable-i2c=bmi160@68 --disable-i2c=sht4x@44 +``` + +## Runtime Control Extension + +For runtime enable/disable (not just at startup), you could: + +### Option A: Use a Named Pipe / FIFO + +```c +// Create a FIFO that accepts commands +// "disable bmi160@68" or "enable bmi160@68" +static void *cmdline_control_thread(void *arg) +{ + int fd = open("/tmp/i2c_emul_control", O_RDONLY); + char buf[256]; + while (read(fd, buf, sizeof(buf)) > 0) { + if (strncmp(buf, "disable ", 8) == 0) { + i2c_emul_cmdline_disable_device(buf + 8); + } else if (strncmp(buf, "enable ", 7) == 0) { + i2c_emul_cmdline_enable_device(buf + 7); + } + } + return NULL; +} +``` + +### Option B: Signal Handler + +Use `SIGUSR1`/`SIGUSR2` with a config file that specifies which device to toggle. + +### Option C: Shared Memory + +Map a shared memory region that external tools can write to for device state. + +## Integration with CircuitPython Tests + +For pytest-based tests, you could: + +```python +import subprocess +import os + +class NativeSimProcess: + def __init__(self, exe_path): + self.exe_path = exe_path + self.control_fifo = "/tmp/i2c_emul_control" + + def start(self, disabled_devices=None): + args = [self.exe_path] + if disabled_devices: + for dev in disabled_devices: + args.extend(["--disable-i2c", dev]) + self.proc = subprocess.Popen(args, ...) + + def disable_device(self, name): + """Runtime disable via FIFO""" + with open(self.control_fifo, 'w') as f: + f.write(f"disable {name}\n") + + def enable_device(self, name): + """Runtime enable via FIFO""" + with open(self.control_fifo, 'w') as f: + f.write(f"enable {name}\n") +``` + +## Alternative: Device Tree Approach + +For compile-time configuration, use device tree overlays: + +```dts +// boards/native_sim_no_bmi160.overlay +&bmi160 { + status = "disabled"; +}; +``` + +Build separate variants: +```bash +west build -b native_sim -- -DDTC_OVERLAY_FILE=boards/native_sim_no_bmi160.overlay +``` + +## References + +- Zephyr I2C Emulation: `zephyr/drivers/i2c/i2c_emul.c` +- Emulator Framework: `zephyr/doc/hardware/emulator/bus_emulators.rst` +- Native Sim Docs: `zephyr/boards/native/native_sim/doc/index.rst` +- Command-line handling: `zephyr/boards/native/native_sim/cmdline.c` +- Example mock API usage: `zephyr/tests/drivers/sensor/bmi160/src/i2c.c` diff --git a/ports/zephyr-cp/tests/test_basics.py b/ports/zephyr-cp/tests/test_basics.py new file mode 100644 index 0000000000000..9abb7f451b722 --- /dev/null +++ b/ports/zephyr-cp/tests/test_basics.py @@ -0,0 +1,199 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test LED blink functionality on native_sim.""" + +from conftest import InputTrigger, parse_gpio_trace + + +def test_blank_flash_hello_world(run_circuitpython): + """Test that an erased flash shows code.py output header.""" + result = run_circuitpython(None, timeout=4, erase_flash=True) + + assert "Board ID:native_native_sim" in result.output + assert "UID:" in result.output + assert "code.py output:" in result.output + assert "Hello World" in result.output + assert "done" in result.output + + +BLINK_CODE = """\ +import time +import board +import digitalio + +led = digitalio.DigitalInOut(board.LED) +led.direction = digitalio.Direction.OUTPUT + +for i in range(3): + print(f"LED on {i}") + led.value = True + time.sleep(0.1) + print(f"LED off {i}") + led.value = False + time.sleep(0.1) + +print("done") +""" + + +def test_blink_output(run_circuitpython): + """Test blink program produces expected output and GPIO traces.""" + result = run_circuitpython(BLINK_CODE, timeout=5) + + # Check serial output + assert "LED on 0" in result.output + assert "LED off 0" in result.output + assert "LED on 2" in result.output + assert "LED off 2" in result.output + assert "done" in result.output + + # Check GPIO traces - LED is on gpio_emul.00 + gpio_trace = parse_gpio_trace(result.trace_file, "gpio_emul.00") + + # Deduplicate by timestamp (keep last value at each timestamp) + by_timestamp = {} + for ts, val in gpio_trace: + by_timestamp[ts] = val + sorted_trace = sorted(by_timestamp.items()) + + # Find transition points (where value changes), skipping initialization at ts=0 + transitions = [] + for i in range(1, len(sorted_trace)): + prev_ts, prev_val = sorted_trace[i - 1] + curr_ts, curr_val = sorted_trace[i] + if prev_val != curr_val and curr_ts > 0: + transitions.append((curr_ts, curr_val)) + + # We expect at least 6 transitions (3 on + 3 off) from the blink loop + assert len(transitions) >= 6, f"Expected at least 6 transitions, got {len(transitions)}" + + # Verify timing between consecutive transitions + # Each sleep is 0.1s = 100ms = 100,000,000 ns + expected_interval_ns = 100_000_000 + tolerance_ns = 20_000_000 # 20ms tolerance + + # Find a sequence of 6 consecutive transitions with ~100ms intervals (the blink loop) + # This filters out initialization and cleanup noise + blink_transitions = [] + for i in range(len(transitions) - 1): + interval = transitions[i + 1][0] - transitions[i][0] + if abs(interval - expected_interval_ns) < tolerance_ns: + if not blink_transitions: + blink_transitions.append(transitions[i]) + blink_transitions.append(transitions[i + 1]) + elif blink_transitions: + # Found end of blink sequence + break + + assert len(blink_transitions) >= 6, ( + f"Expected at least 6 blink transitions with ~100ms intervals, got {len(blink_transitions)}" + ) + + # Verify timing between blink transitions + for i in range(1, min(6, len(blink_transitions))): + prev_ts = blink_transitions[i - 1][0] + curr_ts = blink_transitions[i][0] + interval = curr_ts - prev_ts + assert abs(interval - expected_interval_ns) < tolerance_ns, ( + f"Transition interval {interval / 1_000_000:.1f}ms deviates from " + f"expected {expected_interval_ns / 1_000_000:.1f}ms by more than " + f"{tolerance_ns / 1_000_000:.1f}ms tolerance" + ) + + +# --- PTY Input Tests --- + + +INPUT_CODE = """\ +import sys + +print("ready") +char = sys.stdin.read(1) +print(f"received: {repr(char)}") +print("done") +""" + + +def test_basic_serial_input(run_circuitpython): + """Test reading single character from serial via PTY write.""" + result = run_circuitpython( + INPUT_CODE, + timeout=5.0, + input_sequence=[InputTrigger(trigger="ready", data=b"A")], + ) + + assert "ready" in result.output + assert "received: 'A'" in result.output + assert "done" in result.output + + +INPUT_FUNC_CODE = """\ +print("ready") +name = input("Enter name: ") +print(f"hello {name}") +print("done") +""" + + +def test_input_function(run_circuitpython): + """Test the built-in input() function with PTY input.""" + result = run_circuitpython( + INPUT_FUNC_CODE, + timeout=5.0, + input_sequence=[InputTrigger(trigger="Enter name:", data=b"World\r")], + ) + + assert "ready" in result.output + assert "Enter name:" in result.output + assert "hello World" in result.output + assert "done" in result.output + + +INTERRUPT_CODE = """\ +import time + +print("starting") +for i in range(100): + print(f"loop {i}") + time.sleep(0.1) +print("completed") +""" + + +def test_ctrl_c_interrupt(run_circuitpython): + """Test sending Ctrl+C (0x03) to interrupt running code.""" + result = run_circuitpython( + INTERRUPT_CODE, + timeout=15.0, + input_sequence=[InputTrigger(trigger="loop 5", data=b"\x03")], + ) + + assert "starting" in result.output + assert "loop 5" in result.output + assert "KeyboardInterrupt" in result.output + assert "completed" not in result.output + + +RELOAD_CODE = """\ +print("first run") +import time +time.sleep(1) +print("done") +""" + + +def test_ctrl_d_soft_reload(run_circuitpython): + """Test sending Ctrl+D (0x04) to trigger soft reload.""" + result = run_circuitpython( + RELOAD_CODE, + timeout=10.0, + input_sequence=[InputTrigger(trigger="first run", data=b"\x04")], + ) + + # Should see "first run" appear multiple times due to reload + # or see a soft reboot message + assert "first run" in result.output + # The soft reload should restart the code before "done" is printed + assert "done" in result.output + assert result.output.count("first run") > 1 diff --git a/ports/zephyr-cp/tests/test_i2c.py b/ports/zephyr-cp/tests/test_i2c.py new file mode 100644 index 0000000000000..594dfcc8f4d1c --- /dev/null +++ b/ports/zephyr-cp/tests/test_i2c.py @@ -0,0 +1,98 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test I2C functionality on native_sim.""" + +I2C_SCAN_CODE = """\ +import board + +i2c = board.I2C() +while not i2c.try_lock(): + pass +devices = i2c.scan() +print(f"I2C devices: {[hex(d) for d in devices]}") +i2c.unlock() +i2c.deinit() +print("done") +""" + + +def test_i2c_scan(run_circuitpython): + """Test I2C bus scanning finds emulated devices. + + The AT24 EEPROM emulator responds to zero-length probe writes, + so it should appear in scan results at address 0x50. + """ + result = run_circuitpython(I2C_SCAN_CODE, timeout=5.0) + + assert "I2C devices:" in result.output + # AT24 EEPROM should be at address 0x50 + assert "0x50" in result.output + assert "done" in result.output + + +AT24_READ_CODE = """\ +import board + +i2c = board.I2C() +while not i2c.try_lock(): + pass + +# AT24 EEPROM at address 0x50 +AT24_ADDR = 0x50 + +# Read first byte from address 0 +result = bytearray(1) +try: + i2c.writeto_then_readfrom(AT24_ADDR, bytes([0x00]), result) + value = result[0] + print(f"AT24 byte 0: 0x{value:02X}") + # Fresh EEPROM should be 0xFF + if value == 0xFF: + print("eeprom_valid") + else: + print(f"unexpected value: expected 0xFF, got 0x{value:02X}") +except OSError as e: + print(f"I2C error: {e}") + +i2c.unlock() +i2c.deinit() +print("done") +""" + + +def test_i2c_at24_read(run_circuitpython): + """Test reading from AT24 EEPROM emulator.""" + result = run_circuitpython(AT24_READ_CODE, timeout=5.0) + + assert "AT24 byte 0: 0xFF" in result.output + assert "eeprom_valid" in result.output + assert "done" in result.output + + +def test_i2c_device_disabled(run_circuitpython): + """Test that disabled I2C device doesn't appear in scan.""" + result = run_circuitpython( + I2C_SCAN_CODE, + timeout=5.0, + disabled_i2c_devices=["eeprom@50"], + ) + + assert "I2C devices:" in result.output + # AT24 at 0x50 should NOT appear when disabled + assert "0x50" not in result.output + assert "done" in result.output + + +def test_i2c_device_disabled_communication_fails(run_circuitpython): + """Test that communication with disabled I2C device fails.""" + result = run_circuitpython( + AT24_READ_CODE, + timeout=5.0, + disabled_i2c_devices=["eeprom@50"], + ) + + # Should get an I2C error when trying to communicate + assert "I2C error" in result.output + assert "eeprom_valid" not in result.output + assert "done" in result.output From e9b74b1471972cdcbdd1780ffc8765edc7003b89 Mon Sep 17 00:00:00 2001 From: Alec Delaney Date: Sun, 1 Feb 2026 12:36:34 -0500 Subject: [PATCH 288/517] Update audiobusio.PDMIn.record() --- shared-bindings/audiobusio/PDMIn.c | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/shared-bindings/audiobusio/PDMIn.c b/shared-bindings/audiobusio/PDMIn.c index 2a3fd3540c38f..2890afda0fd3a 100644 --- a/shared-bindings/audiobusio/PDMIn.c +++ b/shared-bindings/audiobusio/PDMIn.c @@ -155,11 +155,11 @@ static void check_for_deinit(audiobusio_pdmin_obj_t *self) { // Provided by context manager helper. -//| def record(self, destination: WriteableBuffer, destination_length: int) -> None: +//| def record(self, destination: WriteableBuffer, destination_length: int) -> int: //| """Records destination_length bytes of samples to destination. This is //| blocking. //| -//| An IOError may be raised when the destination is too slow to record the +//| An OSError may be raised when the destination is too slow to record the //| audio at the given rate. For internal flash, writing all 1s to the file //| before recording is recommended to speed up writes. //| @@ -176,22 +176,22 @@ static mp_obj_t audiobusio_pdmin_obj_record(mp_obj_t self_obj, mp_obj_t destinat mp_buffer_info_t bufinfo; if (mp_obj_is_type(destination, &mp_type_fileio)) { mp_raise_NotImplementedError(MP_ERROR_TEXT("Cannot record to a file")); - } else if (mp_get_buffer(destination, &bufinfo, MP_BUFFER_WRITE)) { - if (bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL) < length) { - mp_raise_ValueError(MP_ERROR_TEXT("Destination capacity is smaller than destination_length.")); - } - uint8_t bit_depth = common_hal_audiobusio_pdmin_get_bit_depth(self); - if (bufinfo.typecode != 'H' && bit_depth == 16) { - mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be an array of type 'H' for bit_depth = 16")); - } else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE && bit_depth == 8) { - mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be a bytearray or array of type 'B' for bit_depth = 8")); - } - // length is the buffer length in slots, not bytes. - uint32_t length_written = - common_hal_audiobusio_pdmin_record_to_buffer(self, bufinfo.buf, length); - return MP_OBJ_NEW_SMALL_INT(length_written); } - return mp_const_none; + + mp_get_buffer_raise(destination, &bufinfo, MP_BUFFER_WRITE); + if (bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL) < length) { + mp_raise_ValueError(MP_ERROR_TEXT("Destination capacity is smaller than destination_length.")); + } + uint8_t bit_depth = common_hal_audiobusio_pdmin_get_bit_depth(self); + if (bufinfo.typecode != 'H' && bit_depth == 16) { + mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be an array of type 'H' for bit_depth = 16")); + } else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE && bit_depth == 8) { + mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be a bytearray or array of type 'B' for bit_depth = 8")); + } + // length is the buffer length in slots, not bytes. + uint32_t length_written = + common_hal_audiobusio_pdmin_record_to_buffer(self, bufinfo.buf, length); + return MP_OBJ_NEW_SMALL_INT(length_written); } MP_DEFINE_CONST_FUN_OBJ_3(audiobusio_pdmin_record_obj, audiobusio_pdmin_obj_record); From 9b25e5f1c7fcc9b654b93673942f84f50dfaf014 Mon Sep 17 00:00:00 2001 From: tyeth Date: Tue, 20 Jan 2026 19:56:05 +0000 Subject: [PATCH 289/517] add(board): add Pimoroni Explorer RP2350 (PIM720) --- .../boards/pimoroni_explorer2350/board.c | 145 ++++++++++++++++++ .../pimoroni_explorer2350/mpconfigboard.h | 18 +++ .../pimoroni_explorer2350/mpconfigboard.mk | 10 ++ .../pico-sdk-configboard.h | 7 + .../boards/pimoroni_explorer2350/pins.c | 124 +++++++++++++++ 5 files changed, 304 insertions(+) create mode 100644 ports/raspberrypi/boards/pimoroni_explorer2350/board.c create mode 100644 ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.h create mode 100644 ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.mk create mode 100644 ports/raspberrypi/boards/pimoroni_explorer2350/pico-sdk-configboard.h create mode 100644 ports/raspberrypi/boards/pimoroni_explorer2350/pins.c diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/board.c b/ports/raspberrypi/boards/pimoroni_explorer2350/board.c new file mode 100644 index 0000000000000..d0c6d9a57ce9f --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/board.c @@ -0,0 +1,145 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/paralleldisplaybus/ParallelBus.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "hardware/gpio.h" + +// Display pins from Pimoroni Explorer parallel bus: {cs=27, dc=28, wr=30, rd=31, d0=32, bl=26} +#define LCD_BACKLIGHT_PIN 26 +#define LCD_CS_PIN 27 +#define LCD_DC_PIN 28 +#define LCD_WR_PIN 30 +#define LCD_RD_PIN 31 +#define LCD_D0_PIN 32 // Data pins are GPIO32-39 (8 consecutive pins) + +#define DELAY 0x80 + +// ST7789V display init sequence for 320x240 parallel bus +// Based on Pimoroni's pimoroni-pico ST7789 driver configuration +uint8_t display_init_sequence[] = { + // Software reset + 0x01, 0 | DELAY, 150, + // Sleep out + 0x11, 0 | DELAY, 255, + // Tearing effect line on (frame sync) + 0x35, 1, 0x00, + // COLMOD: 16-bit color (5-6-5 RGB) + 0x3A, 1, 0x55, + // Porch control (PORCTRL) + 0xB2, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33, + // Gate control (GCTRL) - VGH=13.26V, VGL=-10.43V + 0xB7, 1, 0x35, + // VCOM setting (VCOMS) + 0xBB, 1, 0x1F, + // LCM control (LCMCTRL) + 0xC0, 1, 0x2C, + // VDV and VRH command enable (VDVVRHEN) + 0xC2, 1, 0x01, + // VRH set (VRHS) + 0xC3, 1, 0x12, + // VDV set (VDVS) + 0xC4, 1, 0x20, + // Frame rate control (FRCTRL2) + 0xC6, 1, 0x0F, + // Power control 1 (PWCTRL1) + 0xD0, 2, 0xA4, 0xA1, + // RAM control (RAMCTRL) - for proper endianness + 0xB0, 2, 0x00, 0xC0, + // Positive gamma correction + 0xE0, 14, 0xD0, 0x08, 0x11, 0x08, 0x0C, 0x15, 0x39, 0x33, 0x50, 0x36, 0x13, 0x14, 0x29, 0x2D, + // Negative gamma correction + 0xE1, 14, 0xD0, 0x08, 0x10, 0x08, 0x06, 0x06, 0x39, 0x44, 0x51, 0x0B, 0x16, 0x14, 0x2F, 0x31, + // Inversion on + 0x21, 0, + // Normal display mode on + 0x13, 0 | DELAY, 10, + // MADCTL: MX=0, MY=1, MV=1, ML=1 (COL_ORDER | SWAP_XY | SCAN_ORDER) = 0x70 + // This configures the 320x240 display in landscape orientation + 0x36, 1, 0x70, + // Display on + 0x29, 0 | DELAY, 100, +}; + +static void display_init(void) { + paralleldisplaybus_parallelbus_obj_t *bus = &allocate_display_bus()->parallel_bus; + bus->base.type = ¶lleldisplaybus_parallelbus_type; + + common_hal_paralleldisplaybus_parallelbus_construct(bus, + &pin_GPIO32, // Data0 (D0) - data pins are sequential GPIO32-39 + &pin_GPIO28, // Command/Data (DC) + &pin_GPIO27, // Chip select (CS) + &pin_GPIO30, // Write (WR) + &pin_GPIO31, // Read (RD) + NULL, // Reset (directly connected to board reset) + 15000000); // Frequency - ST7789 min clock cycle ~66ns = ~15MHz + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct(display, + bus, + 320, // Width + 240, // Height + 0, // column start + 0, // row start + 0, // rotation + 16, // Color depth + false, // grayscale + false, // pixels_in_byte_share_row + 1, // bytes per cell + false, // reverse_pixels_in_byte + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO26, // Backlight pin (BL) + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + +void board_init(void) { + // Ensure backlight is on before display init + board_reset_pin_number(LCD_BACKLIGHT_PIN); + display_init(); +} + +// Prevent the backlight pin from being reset, keeping display visible across soft resets +bool board_reset_pin_number(uint8_t pin_number) { + if (pin_number == LCD_BACKLIGHT_PIN) { + // Keep backlight on - set high output without glitching + gpio_put(pin_number, 1); + gpio_set_dir(pin_number, GPIO_OUT); + gpio_set_function(pin_number, GPIO_FUNC_SIO); + return true; + } + return false; +} + +void reset_board(void) { + // Keep backlight on during reset + board_reset_pin_number(LCD_BACKLIGHT_PIN); +} + +void board_deinit(void) { + // Backlight will be handled by board_reset_pin_number +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.h new file mode 100644 index 0000000000000..dd269aa6b844a --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Pimoroni Explorer" +#define MICROPY_HW_MCU_NAME "rp2350b" + +#define MICROPY_HW_LED_STATUS (&pin_GPIO25) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO21, .sda = &pin_GPIO20}} + +#define DEFAULT_UART_BUS_RX (&pin_GPIO1) +#define DEFAULT_UART_BUS_TX (&pin_GPIO0) diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.mk new file mode 100644 index 0000000000000..4e4882910c8ab --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.mk @@ -0,0 +1,10 @@ +USB_VID = 0x2E8A +USB_PID = 0x10C0 +USB_PRODUCT = "Explorer" +USB_MANUFACTURER = "Pimoroni" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = B +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/pico-sdk-configboard.h b/ports/raspberrypi/boards/pimoroni_explorer2350/pico-sdk-configboard.h new file mode 100644 index 0000000000000..66b57dfd13dc2 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/pico-sdk-configboard.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/pins.c b/ports/raspberrypi/boards/pimoroni_explorer2350/pins.c new file mode 100644 index 0000000000000..8154a43de005d --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/pins.c @@ -0,0 +1,124 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // User GPIOs (accent connector) + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + + // Servo pins + { MP_ROM_QSTR(MP_QSTR_SERVO1), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_SERVO2), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_SERVO3), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SERVO4), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + + // Audio + { MP_ROM_QSTR(MP_QSTR_AUDIO), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_AMP_EN), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + + // Buttons + { MP_ROM_QSTR(MP_QSTR_SW_C), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_SW_B), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_SW_A), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SW_X), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SW_Y), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SW_Z), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + + // I2C + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + + // User button + { MP_ROM_QSTR(MP_QSTR_SW_USER), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + + // LED? + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_GP25), MP_ROM_PTR(&pin_GPIO25) }, + + // Display parallel bus pins (ST7789V 320x240) + // Pins from Pimoroni: {cs=27, dc=28, wr=30, rd=31, d0=32, bl=26} + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_LCD_WR), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_GP30), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RD), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_GP31), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D0), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_GP32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D1), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_GP33), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D2), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_GP34), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D3), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_GP35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D4), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_GP36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D5), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_GP37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D6), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_GP38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D7), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_GP39), MP_ROM_PTR(&pin_GPIO39) }, + + // ADC pins (RP2350B extended GPIOs) + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_ADC0), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_GP40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_ADC1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_GP41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_ADC2), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_GP42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_ADC3), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_GP43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_ADC4), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_GP44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_ADC5), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_GP45), MP_ROM_PTR(&pin_GPIO45) }, + + // I2C object + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + // Display object + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 23be085b5e79a0904789b41b3aa71f46f214359d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Feb 2026 12:05:42 -0800 Subject: [PATCH 290/517] Build native sim in CI explicitly --- .github/workflows/run-tests.yml | 2 ++ requirements-dev.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index ed0dd65a4f1d3..4fded6bbbdf27 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -88,5 +88,7 @@ jobs: uses: ./.github/actions/deps/ports/zephyr-cp - name: Set up external uses: ./.github/actions/deps/external + - name: Build native sim target + run: make -C ports/zephyr-cp -j2 BOARD=native_native_sim - name: Run Zephyr tests run: make -C ports/zephyr-cp test diff --git a/requirements-dev.txt b/requirements-dev.txt index cdfb62da1fa76..566618d2c4721 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -42,3 +42,4 @@ setuptools # For zephyr port tomlkit pytest +perfetto From 4d2f974712cb595c9c5c825a8f5cb1c418db79c1 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Feb 2026 12:09:04 -0800 Subject: [PATCH 291/517] Dunder asm for Zephyr toolchain --- shared/runtime/gchelper_generic.c | 98 +++++++++++++++---------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index 40c5865ed1c68..464aeaa9981de 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -43,12 +43,12 @@ #if defined(__x86_64__) static void gc_helper_get_regs(gc_helper_regs_t arr) { - register long rbx asm ("rbx"); - register long rbp asm ("rbp"); - register long r12 asm ("r12"); - register long r13 asm ("r13"); - register long r14 asm ("r14"); - register long r15 asm ("r15"); + register long rbx __asm__ ("rbx"); + register long rbp __asm__ ("rbp"); + register long r12 __asm__ ("r12"); + register long r13 __asm__ ("r13"); + register long r14 __asm__ ("r14"); + register long r15 __asm__ ("r15"); #ifdef __clang__ // TODO: // This is dirty workaround for Clang. It tries to get around @@ -56,12 +56,12 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { // Application of this patch here is random, and done only to unbreak // MacOS build. Better, cross-arch ways to deal with Clang issues should // be found. - asm ("" : "=r" (rbx)); - asm ("" : "=r" (rbp)); - asm ("" : "=r" (r12)); - asm ("" : "=r" (r13)); - asm ("" : "=r" (r14)); - asm ("" : "=r" (r15)); + __asm__ ("" : "=r" (rbx)); + __asm__ ("" : "=r" (rbp)); + __asm__ ("" : "=r" (r12)); + __asm__ ("" : "=r" (r13)); + __asm__ ("" : "=r" (r14)); + __asm__ ("" : "=r" (r15)); #endif arr[0] = rbx; arr[1] = rbp; @@ -85,10 +85,10 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { // Application of this patch here is random, and done only to unbreak // MacOS build. Better, cross-arch ways to deal with Clang issues should // be found. - asm ("" : "=r" (ebx)); - asm ("" : "=r" (esi)); - asm ("" : "=r" (edi)); - asm ("" : "=r" (ebp)); + __asm__ ("" : "=r" (ebx)); + __asm__ ("" : "=r" (esi)); + __asm__ ("" : "=r" (edi)); + __asm__ ("" : "=r" (ebp)); #endif arr[0] = ebx; arr[1] = esi; @@ -105,16 +105,16 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wuninitialized" #endif - register long r4 asm ("r4"); - register long r5 asm ("r5"); - register long r6 asm ("r6"); - register long r7 asm ("r7"); - register long r8 asm ("r8"); - register long r9 asm ("r9"); - register long r10 asm ("r10"); - register long r11 asm ("r11"); - register long r12 asm ("r12"); - register long r13 asm ("r13"); + register long r4 __asm__ ("r4"); + register long r5 __asm__ ("r5"); + register long r6 __asm__ ("r6"); + register long r7 __asm__ ("r7"); + register long r8 __asm__ ("r8"); + register long r9 __asm__ ("r9"); + register long r10 __asm__ ("r10"); + register long r11 __asm__ ("r11"); + register long r12 __asm__ ("r12"); + register long r13 __asm__ ("r13"); arr[0] = r4; arr[1] = r5; arr[2] = r6; @@ -133,17 +133,17 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { #elif defined(__aarch64__) static void gc_helper_get_regs(gc_helper_regs_t arr) { - const register long x19 asm ("x19"); - const register long x20 asm ("x20"); - const register long x21 asm ("x21"); - const register long x22 asm ("x22"); - const register long x23 asm ("x23"); - const register long x24 asm ("x24"); - const register long x25 asm ("x25"); - const register long x26 asm ("x26"); - const register long x27 asm ("x27"); - const register long x28 asm ("x28"); - const register long x29 asm ("x29"); + const register long x19 __asm__ ("x19"); + const register long x20 __asm__ ("x20"); + const register long x21 __asm__ ("x21"); + const register long x22 __asm__ ("x22"); + const register long x23 __asm__ ("x23"); + const register long x24 __asm__ ("x24"); + const register long x25 __asm__ ("x25"); + const register long x26 __asm__ ("x26"); + const register long x27 __asm__ ("x27"); + const register long x28 __asm__ ("x28"); + const register long x29 __asm__ ("x29"); arr[0] = x19; arr[1] = x20; arr[2] = x21; @@ -163,18 +163,18 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { // for RV32I targets or gchelper_rv64i.s for RV64I targets. static void gc_helper_get_regs(gc_helper_regs_t arr) { - register uintptr_t s0 asm ("x8"); - register uintptr_t s1 asm ("x9"); - register uintptr_t s2 asm ("x18"); - register uintptr_t s3 asm ("x19"); - register uintptr_t s4 asm ("x20"); - register uintptr_t s5 asm ("x21"); - register uintptr_t s6 asm ("x22"); - register uintptr_t s7 asm ("x23"); - register uintptr_t s8 asm ("x24"); - register uintptr_t s9 asm ("x25"); - register uintptr_t s10 asm ("x26"); - register uintptr_t s11 asm ("x27"); + register uintptr_t s0 __asm__ ("x8"); + register uintptr_t s1 __asm__ ("x9"); + register uintptr_t s2 __asm__ ("x18"); + register uintptr_t s3 __asm__ ("x19"); + register uintptr_t s4 __asm__ ("x20"); + register uintptr_t s5 __asm__ ("x21"); + register uintptr_t s6 __asm__ ("x22"); + register uintptr_t s7 __asm__ ("x23"); + register uintptr_t s8 __asm__ ("x24"); + register uintptr_t s9 __asm__ ("x25"); + register uintptr_t s10 __asm__ ("x26"); + register uintptr_t s11 __asm__ ("x27"); arr[0] = s0; arr[1] = s1; arr[2] = s2; From 03ec5c8f6af546dca7e3bae4c95aab06b56d3658 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Feb 2026 14:41:24 -0800 Subject: [PATCH 292/517] Changes based on Dan's feedback --- ports/analog/common-hal/busio/I2C.c | 6 ++--- ports/atmel-samd/common-hal/busio/I2C.c | 10 +++---- ports/atmel-samd/common-hal/sdioio/SDCard.c | 8 +++--- ports/broadcom/common-hal/busio/I2C.c | 10 +++---- ports/broadcom/common-hal/sdioio/SDCard.c | 8 +++--- ports/cxd56/common-hal/busio/I2C.c | 10 +++---- ports/cxd56/common-hal/sdioio/SDCard.c | 8 +++--- ports/espressif/common-hal/busio/I2C.c | 8 +++--- ports/espressif/common-hal/sdioio/SDCard.c | 8 +++--- ports/mimxrt10xx/common-hal/busio/I2C.c | 10 +++---- ports/nordic/common-hal/busio/I2C.c | 12 ++++----- ports/raspberrypi/common-hal/busio/I2C.c | 10 +++---- ports/renode/common-hal/busio/I2C.c | 6 ++--- ports/silabs/common-hal/busio/I2C.c | 6 ++--- ports/stm/common-hal/busio/I2C.c | 10 +++---- ports/stm/common-hal/sdioio/SDCard.c | 8 +++--- ports/zephyr-cp/common-hal/busio/I2C.c | 6 ++--- py/mperrno.h | 3 ++- py/mpz.c | 12 ++++----- pyproject.toml | 26 ++++++++++++++++++- shared-bindings/busio/I2C.c | 6 ++--- shared-bindings/busio/I2C.h | 6 ++--- shared-bindings/digitalio/DigitalInOut.c | 4 +-- shared-bindings/digitalio/DigitalInOut.h | 4 +-- .../digitalio/DigitalInOutProtocol.c | 4 +-- .../digitalio/DigitalInOutProtocol.h | 8 +++--- shared-bindings/i2cioexpander/IOExpander.c | 4 +-- shared-bindings/i2cioexpander/IOExpander.h | 6 ++--- shared-bindings/i2cioexpander/IOPin.c | 8 +++--- shared-bindings/i2cioexpander/IOPin.h | 6 ++--- shared-bindings/sdcardio/SDCard.h | 6 ++--- shared-bindings/sdioio/SDCard.h | 8 +++--- shared-module/i2cioexpander/IOExpander.c | 12 ++++----- shared-module/i2cioexpander/IOPin.c | 8 +++--- tools/safe_mode_finder.py | 4 +-- 35 files changed, 152 insertions(+), 127 deletions(-) diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c index b74c921bae3ac..7ebe721b3f427 100644 --- a/ports/analog/common-hal/busio/I2C.c +++ b/ports/analog/common-hal/busio/I2C.c @@ -187,7 +187,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } // Write data to the device selected by address -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { int ret; @@ -209,7 +209,7 @@ mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, } // Read into buffer from the device selected by address -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { @@ -233,7 +233,7 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, } // Write the bytes from out_data to the device selected by address -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { diff --git a/ports/atmel-samd/common-hal/busio/I2C.c b/ports/atmel-samd/common-hal/busio/I2C.c index 700c3e4aba24b..d9da050dabeae 100644 --- a/ports/atmel-samd/common-hal/busio/I2C.c +++ b/ports/atmel-samd/common-hal/busio/I2C.c @@ -176,7 +176,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { uint16_t attempts = ATTEMPTS; @@ -202,12 +202,12 @@ static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t ad return -MP_EIO; } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { uint16_t attempts = ATTEMPTS; @@ -233,9 +233,9 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, return -MP_EIO; } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/atmel-samd/common-hal/sdioio/SDCard.c b/ports/atmel-samd/common-hal/sdioio/SDCard.c index af3610c89e9a1..e38a664ed48a8 100644 --- a/ports/atmel-samd/common-hal/sdioio/SDCard.c +++ b/ports/atmel-samd/common-hal/sdioio/SDCard.c @@ -171,7 +171,7 @@ static void debug_print_state(sdioio_sdcard_obj_t *self, const char *what, sd_mm #endif } -mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); @@ -189,7 +189,7 @@ mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { check_for_deinit(self); check_whole_block(bufinfo); @@ -198,7 +198,7 @@ mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint3 start_block, num_blocks); } -mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); @@ -216,7 +216,7 @@ mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { check_for_deinit(self); check_whole_block(bufinfo); diff --git a/ports/broadcom/common-hal/busio/I2C.c b/ports/broadcom/common-hal/busio/I2C.c index cdc986f05ae29..7c1eafe281dc9 100644 --- a/ports/broadcom/common-hal/busio/I2C.c +++ b/ports/broadcom/common-hal/busio/I2C.c @@ -137,7 +137,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { // Discussion of I2C implementation is here: https://github.com/raspberrypi/linux/issues/254 -static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { COMPLETE_MEMORY_READS; self->peripheral->S_b.DONE = true; @@ -192,12 +192,12 @@ static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t ad return 0; } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { COMPLETE_MEMORY_READS; self->peripheral->A_b.ADDR = addr; @@ -242,9 +242,9 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, return 0; } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/broadcom/common-hal/sdioio/SDCard.c b/ports/broadcom/common-hal/sdioio/SDCard.c index 5dc405bdc18e8..2c9e6a2bb587a 100644 --- a/ports/broadcom/common-hal/sdioio/SDCard.c +++ b/ports/broadcom/common-hal/sdioio/SDCard.c @@ -306,7 +306,7 @@ static void check_whole_block(mp_buffer_info_t *bufinfo) { } // Native function for VFS blockdev layer -mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!self->init) { @@ -327,7 +327,7 @@ mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (!self->init) { return -MP_EIO; } @@ -339,7 +339,7 @@ mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint3 } // Native function for VFS blockdev layer -mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!self->init) { @@ -358,7 +358,7 @@ mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (!self->init) { return -MP_EIO; } diff --git a/ports/cxd56/common-hal/busio/I2C.c b/ports/cxd56/common-hal/busio/I2C.c index c8b32b6a023b0..8e846b2402dc3 100644 --- a/ports/cxd56/common-hal/busio/I2C.c +++ b/ports/cxd56/common-hal/busio/I2C.c @@ -91,7 +91,7 @@ bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { return I2C_TRANSFER(self->i2c_dev, &msg, 1) < 0 ? false : true; } -static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len, bool stop) { +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len, bool stop) { struct i2c_msg_s msg; msg.frequency = self->frequency; @@ -102,12 +102,12 @@ static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t ad return -I2C_TRANSFER(self->i2c_dev, &msg, 1); } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len) { struct i2c_msg_s msg; msg.frequency = self->frequency; @@ -118,9 +118,9 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, ui return -I2C_TRANSFER(self->i2c_dev, &msg, 1); } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/cxd56/common-hal/sdioio/SDCard.c b/ports/cxd56/common-hal/sdioio/SDCard.c index 95dde482dde0c..227011a88710f 100644 --- a/ports/cxd56/common-hal/sdioio/SDCard.c +++ b/ports/cxd56/common-hal/sdioio/SDCard.c @@ -97,7 +97,7 @@ static void check_whole_block(mp_buffer_info_t *bufinfo) { } // Native function for VFS blockdev layer -mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); int result = self->inode->u.i_bops->read(self->inode, buf, start_block, num_blocks); @@ -107,7 +107,7 @@ mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (common_hal_sdioio_sdcard_deinited(self)) { raise_deinited_error(); } @@ -119,7 +119,7 @@ mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32 } // Native function for VFS blockdev layer -mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); int result = self->inode->u.i_bops->write(self->inode, buf, start_block, num_blocks); @@ -129,7 +129,7 @@ mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (common_hal_sdioio_sdcard_deinited(self)) { raise_deinited_error(); } diff --git a/ports/espressif/common-hal/busio/I2C.c b/ports/espressif/common-hal/busio/I2C.c index 723fa7d740000..8ff608717c78f 100644 --- a/ports/espressif/common-hal/busio/I2C.c +++ b/ports/espressif/common-hal/busio/I2C.c @@ -169,7 +169,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static mp_errno_t convert_esp_err(esp_err_t result) { +static mp_negative_errno_t convert_esp_err(esp_err_t result) { switch (result) { case ESP_OK: return 0; @@ -189,7 +189,7 @@ static size_t _transaction_duration_ms(size_t frequency, size_t len) { return (len + 1) / bytes_per_ms + 1000; } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = addr, @@ -202,7 +202,7 @@ mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, cons return convert_esp_err(result); } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = addr, @@ -215,7 +215,7 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8 return convert_esp_err(result); } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, diff --git a/ports/espressif/common-hal/sdioio/SDCard.c b/ports/espressif/common-hal/sdioio/SDCard.c index f774561816258..102f4a4048e48 100644 --- a/ports/espressif/common-hal/sdioio/SDCard.c +++ b/ports/espressif/common-hal/sdioio/SDCard.c @@ -169,7 +169,7 @@ static void check_whole_block(mp_buffer_info_t *bufinfo, int sector_size) { } } -mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); ESP_LOGI(TAG, "in sdioio_sdcard_writeblocks"); @@ -181,7 +181,7 @@ mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { common_hal_sdioio_sdcard_check_for_deinit(self); check_whole_block(bufinfo, self->card.csd.sector_size); @@ -190,7 +190,7 @@ mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint3 start_block, num_blocks); } -mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); ESP_LOGI(TAG, "in sdioio_sdcard_readblocks"); @@ -202,7 +202,7 @@ mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { common_hal_sdioio_sdcard_check_for_deinit(self); check_whole_block(bufinfo, self->card.csd.sector_size); diff --git a/ports/mimxrt10xx/common-hal/busio/I2C.c b/ports/mimxrt10xx/common-hal/busio/I2C.c index 38777f2c9038c..93c0ab301235b 100644 --- a/ports/mimxrt10xx/common-hal/busio/I2C.c +++ b/ports/mimxrt10xx/common-hal/busio/I2C.c @@ -201,7 +201,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { lpi2c_master_transfer_t xfer = { 0 }; @@ -218,12 +218,12 @@ static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t ad return -MP_EIO; } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { lpi2c_master_transfer_t xfer = { 0 }; @@ -240,9 +240,9 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, return -MP_EIO; } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/nordic/common-hal/busio/I2C.c b/ports/nordic/common-hal/busio/I2C.c index 904d35cae3d0b..999737f632bba 100644 --- a/ports/nordic/common-hal/busio/I2C.c +++ b/ports/nordic/common-hal/busio/I2C.c @@ -47,7 +47,7 @@ void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { never_reset_pin_number(self->sda_pin_number); } -static mp_errno_t twi_error_to_mp(const nrfx_err_t err) { +static mp_negative_errno_t twi_error_to_mp(const nrfx_err_t err) { switch (err) { case NRFX_ERROR_DRV_TWI_ERR_ANACK: return -MP_ENODEV; @@ -258,7 +258,7 @@ static nrfx_err_t _twim_xfer_with_timeout(busio_i2c_obj_t *self, nrfx_twim_xfer_ } } -static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool stopBit) { +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool stopBit) { if (len == 0) { return common_hal_busio_i2c_probe(self, addr) ? 0 : -MP_ENODEV; } @@ -286,11 +286,11 @@ static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t ad return twi_error_to_mp(err); } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { if (len == 0) { return 0; } @@ -317,9 +317,9 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8 return twi_error_to_mp(err); } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/raspberrypi/common-hal/busio/I2C.c b/ports/raspberrypi/common-hal/busio/I2C.c index 41df0eac72429..1441a2a7a0668 100644 --- a/ports/raspberrypi/common-hal/busio/I2C.c +++ b/ports/raspberrypi/common-hal/busio/I2C.c @@ -144,7 +144,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { if (len == 0) { // The RP2040 I2C peripheral will not perform 0 byte writes. @@ -182,12 +182,12 @@ static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t ad } } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { size_t result = i2c_read_timeout_us(self->peripheral, addr, data, len, false, BUS_TIMEOUT_US); if (result == len) { @@ -203,9 +203,9 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, } } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/renode/common-hal/busio/I2C.c b/ports/renode/common-hal/busio/I2C.c index 0b01b6e79369d..41649f180b3d9 100644 --- a/ports/renode/common-hal/busio/I2C.c +++ b/ports/renode/common-hal/busio/I2C.c @@ -44,17 +44,17 @@ bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return 0; } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { return -MP_EIO; } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { return -MP_EIO; } diff --git a/ports/silabs/common-hal/busio/I2C.c b/ports/silabs/common-hal/busio/I2C.c index 85f3e63a6dbe6..6c2b04d821d7c 100644 --- a/ports/silabs/common-hal/busio/I2C.c +++ b/ports/silabs/common-hal/busio/I2C.c @@ -145,7 +145,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } // Write data to the device selected by address -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { I2C_TransferSeq_TypeDef seq; @@ -165,7 +165,7 @@ mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, } // Read into buffer from the device selected by address -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { @@ -186,7 +186,7 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, } // Write the bytes from out_data to the device selected by address, -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { diff --git a/ports/stm/common-hal/busio/I2C.c b/ports/stm/common-hal/busio/I2C.c index 5277bffe1fcd2..7f7eb990e4d07 100644 --- a/ports/stm/common-hal/busio/I2C.c +++ b/ports/stm/common-hal/busio/I2C.c @@ -212,7 +212,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { HAL_StatusTypeDef result; if (!transmit_stop_bit) { @@ -237,12 +237,12 @@ static mp_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t ad return result == HAL_OK ? 0 : -MP_EIO; } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { if (!self->frame_in_prog) { return HAL_I2C_Master_Receive(&(self->handle), (uint16_t)(addr << 1), data, (uint16_t)len, 500) @@ -259,9 +259,9 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, } } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - mp_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/stm/common-hal/sdioio/SDCard.c b/ports/stm/common-hal/sdioio/SDCard.c index d8cfdb1131885..2d24f24d82504 100644 --- a/ports/stm/common-hal/sdioio/SDCard.c +++ b/ports/stm/common-hal/sdioio/SDCard.c @@ -242,7 +242,7 @@ static void check_for_deinit(sdioio_sdcard_obj_t *self) { } // Native function for VFS blockdev layer -mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); @@ -262,7 +262,7 @@ mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { check_for_deinit(self); check_whole_block(bufinfo); @@ -272,7 +272,7 @@ mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint3 } // Native function for VFS blockdev layer -mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t num_blocks) { sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); @@ -291,7 +291,7 @@ mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, return 0; } -mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { check_for_deinit(self); check_whole_block(bufinfo); diff --git a/ports/zephyr-cp/common-hal/busio/I2C.c b/ports/zephyr-cp/common-hal/busio/I2C.c index d26fc2828cdfd..84e95721b2736 100644 --- a/ports/zephyr-cp/common-hal/busio/I2C.c +++ b/ports/zephyr-cp/common-hal/busio/I2C.c @@ -73,7 +73,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { k_mutex_unlock(&self->mutex); } -mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { if (common_hal_busio_i2c_deinited(self)) { @@ -96,7 +96,7 @@ mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, return 0; } -mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { if (common_hal_busio_i2c_deinited(self)) { @@ -123,7 +123,7 @@ mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, return 0; } -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { if (common_hal_busio_i2c_deinited(self)) { diff --git a/py/mperrno.h b/py/mperrno.h index f1fee77b22e2c..6c675125f4e08 100644 --- a/py/mperrno.h +++ b/py/mperrno.h @@ -143,7 +143,8 @@ #endif -typedef int mp_errno_t; +// Type for return values where 0 indicates success and negative values are negated MP_E* error codes. +typedef int mp_negative_errno_t; #if MICROPY_PY_ERRNO diff --git a/py/mpz.c b/py/mpz.c index 046d4fcdd5a03..7d8bc03ca8610 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -782,15 +782,15 @@ void mpz_set_from_float(mpz_t *z, mp_float_t src) { // 2 <= value const int dig_cnt = (adj_exp + 1 + (DIG_SIZE - 1)) / DIG_SIZE; const unsigned int rem = adj_exp % DIG_SIZE; - int dig_ind, shift; + int dig_ind, shft; mp_float_uint_t frc = u.p.frc | ((mp_float_uint_t)1 << MP_FLOAT_FRAC_BITS); if (adj_exp < MP_FLOAT_FRAC_BITS) { - shift = 0; + shft = 0; dig_ind = 0; frc >>= MP_FLOAT_FRAC_BITS - adj_exp; } else { - shift = (rem - MP_FLOAT_FRAC_BITS) % DIG_SIZE; + shft = (rem - MP_FLOAT_FRAC_BITS) % DIG_SIZE; dig_ind = (adj_exp - MP_FLOAT_FRAC_BITS) / DIG_SIZE; } mpz_need_dig(z, dig_cnt); @@ -798,9 +798,9 @@ void mpz_set_from_float(mpz_t *z, mp_float_t src) { if (dig_ind != 0) { memset(z->dig, 0, dig_ind * sizeof(mpz_dig_t)); } - if (shift != 0) { - z->dig[dig_ind++] = (frc << shift) & DIG_MASK; - frc >>= DIG_SIZE - shift; + if (shft != 0) { + z->dig[dig_ind++] = (frc << shft) & DIG_MASK; + frc >>= DIG_SIZE - shft; } #if DIG_SIZE < (MP_FLOAT_FRAC_BITS + 1) while (dig_ind != dig_cnt) { diff --git a/pyproject.toml b/pyproject.toml index 0db7eb70a7875..7003a6af9aeae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ [tool.setuptools_scm] # can be empty if no extra settings are needed, presence enables setuptools-scm -# Ruff settings copied from MicroPython +# Codespell settings copied from MicroPython [tool.ruff] target-version = "py37" @@ -59,3 +59,27 @@ lint.per-file-ignores."ports/**/boards/**/manifest_*.py" = [ "F821" ] # Exclude all tests from linting (does not apply to formatting). lint.per-file-ignores."tests/**/*.py" = [ "ALL" ] lint.mccabe.max-complexity = 40 + +[tool.codespell] +count = "" +ignore-regex = '\b[A-Z]{3}\b' +ignore-words-list = "ans,asend,aranges,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" +quiet-level = 3 +skip = """ +*/build*,\ +./.git,\ +./drivers/cc3100,\ +./lib,\ +./ports/cc3200/FreeRTOS,\ +./ports/cc3200/bootmgr/sl,\ +./ports/cc3200/hal,\ +./ports/cc3200/simplelink,\ +./ports/cc3200/telnet,\ +./ports/esp32/managed_components,\ +./ports/nrf/drivers/bluetooth/s1*,\ +./ports/stm32/usbhost,\ +./tests,\ +ACKNOWLEDGEMENTS,\ +""" + +# Ruff settings copied from MicroPython diff --git a/shared-bindings/busio/I2C.c b/shared-bindings/busio/I2C.c index 34c02349deb3d..a2724e60c3422 100644 --- a/shared-bindings/busio/I2C.c +++ b/shared-bindings/busio/I2C.c @@ -228,7 +228,7 @@ static mp_obj_t busio_i2c_readfrom_into(size_t n_args, const mp_obj_t *pos_args, start *= stride_in_bytes; length *= stride_in_bytes; - mp_errno_t status = + mp_negative_errno_t status = common_hal_busio_i2c_read(self, args[ARG_address].u_int, ((uint8_t *)bufinfo.buf) + start, length); if (status != 0) { mp_raise_OSError(-status); @@ -290,7 +290,7 @@ static mp_obj_t busio_i2c_writeto(size_t n_args, const mp_obj_t *pos_args, mp_ma length *= stride_in_bytes; // do the transfer - mp_errno_t status = + mp_negative_errno_t status = common_hal_busio_i2c_write(self, args[ARG_address].u_int, ((uint8_t *)bufinfo.buf) + start, length); if (status != 0) { @@ -377,7 +377,7 @@ static mp_obj_t busio_i2c_writeto_then_readfrom(size_t n_args, const mp_obj_t *p in_start *= in_stride_in_bytes; in_length *= in_stride_in_bytes; - mp_errno_t status = common_hal_busio_i2c_write_read(self, args[ARG_address].u_int, + mp_negative_errno_t status = common_hal_busio_i2c_write_read(self, args[ARG_address].u_int, ((uint8_t *)out_bufinfo.buf) + out_start, out_length, ((uint8_t *)in_bufinfo.buf) + in_start, in_length); if (status != 0) { mp_raise_OSError(-status); diff --git a/shared-bindings/busio/I2C.h b/shared-bindings/busio/I2C.h index e7293ce861752..2978a90754490 100644 --- a/shared-bindings/busio/I2C.h +++ b/shared-bindings/busio/I2C.h @@ -37,16 +37,16 @@ extern void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self); extern bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr); // Write to the device and return 0 on success or a negative error code from mperrno.h -extern mp_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, +extern mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len); // Reads memory of the i2c device picking up where it left off and return 0 on // success or a negative error code from mperrno.h -extern mp_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, +extern mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len); // Do a write and then a read in the same I2C transaction. -mp_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t address, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len); // This is used by the supervisor to claim I2C devices indefinitely. diff --git a/shared-bindings/digitalio/DigitalInOut.c b/shared-bindings/digitalio/DigitalInOut.c index 50cbc3bc36d62..c868ca7d443b9 100644 --- a/shared-bindings/digitalio/DigitalInOut.c +++ b/shared-bindings/digitalio/DigitalInOut.c @@ -368,13 +368,13 @@ digitalio_direction_t digitalinout_get_direction(mp_obj_t self_in) { return common_hal_digitalio_digitalinout_get_direction(self); } -mp_errno_t digitalinout_set_value(mp_obj_t self_in, bool value) { +mp_negative_errno_t digitalinout_set_value(mp_obj_t self_in, bool value) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); common_hal_digitalio_digitalinout_set_value(self, value); return 0; } -mp_errno_t digitalinout_get_value(mp_obj_t self_in, bool *value) { +mp_negative_errno_t digitalinout_get_value(mp_obj_t self_in, bool *value) { digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); *value = common_hal_digitalio_digitalinout_get_value(self); return 0; diff --git a/shared-bindings/digitalio/DigitalInOut.h b/shared-bindings/digitalio/DigitalInOut.h index ad38cbec6a4d2..8d3fe8c00a2c5 100644 --- a/shared-bindings/digitalio/DigitalInOut.h +++ b/shared-bindings/digitalio/DigitalInOut.h @@ -64,8 +64,8 @@ bool digitalinout_deinited(mp_obj_t self_in); digitalinout_result_t digitalinout_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull); digitalinout_result_t digitalinout_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode); digitalio_direction_t digitalinout_get_direction(mp_obj_t self_in); -mp_errno_t digitalinout_set_value(mp_obj_t self_in, bool value); -mp_errno_t digitalinout_get_value(mp_obj_t self_in, bool *value); +mp_negative_errno_t digitalinout_set_value(mp_obj_t self_in, bool value); +mp_negative_errno_t digitalinout_get_value(mp_obj_t self_in, bool *value); digitalinout_result_t digitalinout_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode); digitalio_drive_mode_t digitalinout_get_drive_mode(mp_obj_t self_in); digitalinout_result_t digitalinout_set_pull(mp_obj_t self_in, digitalio_pull_t pull); diff --git a/shared-bindings/digitalio/DigitalInOutProtocol.c b/shared-bindings/digitalio/DigitalInOutProtocol.c index df64fd729c29b..48b428ad46336 100644 --- a/shared-bindings/digitalio/DigitalInOutProtocol.c +++ b/shared-bindings/digitalio/DigitalInOutProtocol.c @@ -311,7 +311,7 @@ digitalio_direction_t digitalinout_protocol_get_direction(mp_obj_t self) { return DIRECTION_OUTPUT; } -mp_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value) { +mp_negative_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value) { const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->set_value) { return proto->set_value(self, value); @@ -322,7 +322,7 @@ mp_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value) { return 0; } -mp_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value) { +mp_negative_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value) { const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); if (proto && proto->get_value) { return proto->get_value(self, value); diff --git a/shared-bindings/digitalio/DigitalInOutProtocol.h b/shared-bindings/digitalio/DigitalInOutProtocol.h index 798cb6a9de1eb..6cb92a3735f33 100644 --- a/shared-bindings/digitalio/DigitalInOutProtocol.h +++ b/shared-bindings/digitalio/DigitalInOutProtocol.h @@ -25,8 +25,8 @@ typedef struct _digitalinout_p_t { digitalinout_result_t (*switch_to_input)(mp_obj_t self, digitalio_pull_t pull); digitalinout_result_t (*switch_to_output)(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode); digitalio_direction_t (*get_direction)(mp_obj_t self); - mp_errno_t (*set_value)(mp_obj_t self, bool value); // Return 0 if ok - mp_errno_t (*get_value)(mp_obj_t self, bool *value); // Return 0 if ok + mp_negative_errno_t (*set_value)(mp_obj_t self, bool value); // Return 0 if ok + mp_negative_errno_t (*get_value)(mp_obj_t self, bool *value); // Return 0 if ok digitalinout_result_t (*set_drive_mode)(mp_obj_t self, digitalio_drive_mode_t drive_mode); digitalio_drive_mode_t (*get_drive_mode)(mp_obj_t self); digitalinout_result_t (*set_pull)(mp_obj_t self, digitalio_pull_t pull); @@ -61,8 +61,8 @@ bool digitalinout_protocol_deinited(mp_obj_t self); digitalinout_result_t digitalinout_protocol_switch_to_input(mp_obj_t self, digitalio_pull_t pull); digitalinout_result_t digitalinout_protocol_switch_to_output(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode); digitalio_direction_t digitalinout_protocol_get_direction(mp_obj_t self); -mp_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value); -mp_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value); +mp_negative_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value); +mp_negative_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value); digitalinout_result_t digitalinout_protocol_set_drive_mode(mp_obj_t self, digitalio_drive_mode_t drive_mode); digitalio_drive_mode_t digitalinout_protocol_get_drive_mode(mp_obj_t self); digitalinout_result_t digitalinout_protocol_set_pull(mp_obj_t self, digitalio_pull_t pull); diff --git a/shared-bindings/i2cioexpander/IOExpander.c b/shared-bindings/i2cioexpander/IOExpander.c index f2257b4b63253..c828c6dc5c7fd 100644 --- a/shared-bindings/i2cioexpander/IOExpander.c +++ b/shared-bindings/i2cioexpander/IOExpander.c @@ -169,7 +169,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_output_value_obj, i2cioex static mp_obj_t i2cioexpander_ioexpander_obj_set_output_value(mp_obj_t self_in, mp_obj_t value) { i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_errno_t result = common_hal_i2cioexpander_ioexpander_set_output_value(self, mp_obj_get_int(value)); + mp_negative_errno_t result = common_hal_i2cioexpander_ioexpander_set_output_value(self, mp_obj_get_int(value)); if (result != 0) { mp_raise_OSError(result); } @@ -198,7 +198,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_output_mask_obj, i2cioexp static mp_obj_t i2cioexpander_ioexpander_obj_set_output_mask(mp_obj_t self_in, mp_obj_t value) { i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_errno_t result = common_hal_i2cioexpander_ioexpander_set_output_mask(self, mp_obj_get_int(value)); + mp_negative_errno_t result = common_hal_i2cioexpander_ioexpander_set_output_mask(self, mp_obj_get_int(value)); if (result != 0) { mp_raise_OSError(result); } diff --git a/shared-bindings/i2cioexpander/IOExpander.h b/shared-bindings/i2cioexpander/IOExpander.h index 64e49b603648d..5c93c6250cc7d 100644 --- a/shared-bindings/i2cioexpander/IOExpander.h +++ b/shared-bindings/i2cioexpander/IOExpander.h @@ -23,12 +23,12 @@ void common_hal_i2cioexpander_ioexpander_construct( void common_hal_i2cioexpander_ioexpander_deinit(i2cioexpander_ioexpander_obj_t *self); bool common_hal_i2cioexpander_ioexpander_deinited(i2cioexpander_ioexpander_obj_t *self); -mp_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioexpander_obj_t *self, size_t *value); +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioexpander_obj_t *self, size_t *value); // No error return because this returns a cached value. void common_hal_i2cioexpander_ioexpander_get_output_value(i2cioexpander_ioexpander_obj_t *self, size_t *value); -mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_ioexpander_obj_t *self, size_t value); +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_ioexpander_obj_t *self, size_t value); void common_hal_i2cioexpander_ioexpander_get_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t *mask); -mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t mask); +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t mask); mp_obj_t common_hal_i2cioexpander_ioexpander_get_pins(i2cioexpander_ioexpander_obj_t *self); diff --git a/shared-bindings/i2cioexpander/IOPin.c b/shared-bindings/i2cioexpander/IOPin.c index e2957f9a220f9..bba229bd31e22 100644 --- a/shared-bindings/i2cioexpander/IOPin.c +++ b/shared-bindings/i2cioexpander/IOPin.c @@ -134,7 +134,7 @@ static mp_obj_t i2cioexpander_iopin_obj_get_value(mp_obj_t self_in) { i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); check_for_deinit(self); bool value; - mp_errno_t res = common_hal_i2cioexpander_iopin_get_value(self, &value); + mp_negative_errno_t res = common_hal_i2cioexpander_iopin_get_value(self, &value); if (res != 0) { mp_raise_OSError(-res); } @@ -149,7 +149,7 @@ static mp_obj_t i2cioexpander_iopin_obj_set_value(mp_obj_t self_in, mp_obj_t val mp_raise_AttributeError(MP_ERROR_TEXT("Cannot set value when direction is input.")); return mp_const_none; } - mp_errno_t res = common_hal_i2cioexpander_iopin_set_value(self, mp_obj_is_true(value)); + mp_negative_errno_t res = common_hal_i2cioexpander_iopin_set_value(self, mp_obj_is_true(value)); if (res != 0) { mp_raise_OSError(-res); } @@ -259,12 +259,12 @@ static digitalio_direction_t iopin_protocol_get_direction(mp_obj_t self_in) { return common_hal_i2cioexpander_iopin_get_direction(self); } -static mp_errno_t iopin_protocol_get_value(mp_obj_t self_in, bool *value) { +static mp_negative_errno_t iopin_protocol_get_value(mp_obj_t self_in, bool *value) { i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_i2cioexpander_iopin_get_value(self, value); } -static mp_errno_t iopin_protocol_set_value(mp_obj_t self_in, bool value) { +static mp_negative_errno_t iopin_protocol_set_value(mp_obj_t self_in, bool value) { i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); return common_hal_i2cioexpander_iopin_set_value(self, value); } diff --git a/shared-bindings/i2cioexpander/IOPin.h b/shared-bindings/i2cioexpander/IOPin.h index 6a7d96130fa62..c8155c71aa1f3 100644 --- a/shared-bindings/i2cioexpander/IOPin.h +++ b/shared-bindings/i2cioexpander/IOPin.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t i2cioexpander_iopin_type; -mp_errno_t i2cioexpander_iopin_construct( +mp_negative_errno_t i2cioexpander_iopin_construct( i2cioexpander_iopin_obj_t *self, i2cioexpander_ioexpander_obj_t *expander, uint8_t pin_number); @@ -30,8 +30,8 @@ digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_output( digitalio_direction_t common_hal_i2cioexpander_iopin_get_direction(i2cioexpander_iopin_obj_t *self); -mp_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *self, bool value); -mp_errno_t common_hal_i2cioexpander_iopin_get_value(i2cioexpander_iopin_obj_t *self, bool *value); +mp_negative_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *self, bool value); +mp_negative_errno_t common_hal_i2cioexpander_iopin_get_value(i2cioexpander_iopin_obj_t *self, bool *value); digitalinout_result_t common_hal_i2cioexpander_iopin_set_drive_mode( i2cioexpander_iopin_obj_t *self, diff --git a/shared-bindings/sdcardio/SDCard.h b/shared-bindings/sdcardio/SDCard.h index ae364357707fe..d0cb8206c349d 100644 --- a/shared-bindings/sdcardio/SDCard.h +++ b/shared-bindings/sdcardio/SDCard.h @@ -18,9 +18,9 @@ bool common_hal_sdcardio_sdcard_deinited(sdcardio_sdcard_obj_t *self); void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self); void common_hal_sdcardio_sdcard_mark_deinit(sdcardio_sdcard_obj_t *self); int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self); -mp_errno_t common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); -mp_errno_t common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self); -mp_errno_t common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); +mp_negative_errno_t common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); +mp_negative_errno_t common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self); +mp_negative_errno_t common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); // Used by native vfs blockdev. mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); diff --git a/shared-bindings/sdioio/SDCard.h b/shared-bindings/sdioio/SDCard.h index 446ff3dfd7618..042521aea50dd 100644 --- a/shared-bindings/sdioio/SDCard.h +++ b/shared-bindings/sdioio/SDCard.h @@ -37,12 +37,12 @@ uint8_t common_hal_sdioio_sdcard_get_width(sdioio_sdcard_obj_t *self); uint32_t common_hal_sdioio_sdcard_get_count(sdioio_sdcard_obj_t *self); // Read or write blocks - returns 0 on success or negative error code from mperrno.h -mp_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); -mp_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); // Used by native vfs blockdev. -mp_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); -mp_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value); // This is used by the supervisor to claim SDIO devices indefinitely. diff --git a/shared-module/i2cioexpander/IOExpander.c b/shared-module/i2cioexpander/IOExpander.c index 5146abc66e616..f4aac5c053245 100644 --- a/shared-module/i2cioexpander/IOExpander.c +++ b/shared-module/i2cioexpander/IOExpander.c @@ -81,7 +81,7 @@ bool common_hal_i2cioexpander_ioexpander_deinited(i2cioexpander_ioexpander_obj_t return self->i2c == NULL; } -mp_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioexpander_obj_t *self, size_t *value) { +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioexpander_obj_t *self, size_t *value) { uint8_t buffer[2]; uint8_t num_bytes = (self->num_pins > 8) ? 2 : 1; @@ -89,7 +89,7 @@ mp_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioe RUN_BACKGROUND_TASKS; } - mp_errno_t result; + mp_negative_errno_t result; if (self->has_get_value) { // Send register address then read result = common_hal_busio_i2c_write_read( @@ -116,7 +116,7 @@ void common_hal_i2cioexpander_ioexpander_get_output_value(i2cioexpander_ioexpand *value = self->output_value; } -mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_ioexpander_obj_t *self, size_t value) { +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_ioexpander_obj_t *self, size_t value) { uint8_t buffer[5]; uint8_t num_bytes = 0; @@ -147,7 +147,7 @@ mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_io return -MP_EBUSY; } - mp_errno_t result = common_hal_busio_i2c_write(self->i2c, self->address, buffer, num_bytes); + mp_negative_errno_t result = common_hal_busio_i2c_write(self->i2c, self->address, buffer, num_bytes); common_hal_busio_i2c_unlock(self->i2c); if (result == 0) { self->output_value = value; @@ -159,7 +159,7 @@ void common_hal_i2cioexpander_ioexpander_get_output_mask(i2cioexpander_ioexpande *mask = self->output_mask; } -mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t mask) { +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t mask) { self->output_mask = mask; // Only write to device if direction register is provided @@ -186,7 +186,7 @@ mp_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioe return -MP_EBUSY; } - mp_errno_t result = common_hal_busio_i2c_write(self->i2c, self->address, buffer, num_bytes); + mp_negative_errno_t result = common_hal_busio_i2c_write(self->i2c, self->address, buffer, num_bytes); common_hal_busio_i2c_unlock(self->i2c); return result; } diff --git a/shared-module/i2cioexpander/IOPin.c b/shared-module/i2cioexpander/IOPin.c index 9502d2814f3fb..04642623b3dc0 100644 --- a/shared-module/i2cioexpander/IOPin.c +++ b/shared-module/i2cioexpander/IOPin.c @@ -9,7 +9,7 @@ #include "py/runtime.h" -mp_errno_t i2cioexpander_iopin_construct( +mp_negative_errno_t i2cioexpander_iopin_construct( i2cioexpander_iopin_obj_t *self, i2cioexpander_ioexpander_obj_t *expander, uint8_t pin_number) { @@ -88,7 +88,7 @@ digitalio_direction_t common_hal_i2cioexpander_iopin_get_direction(i2cioexpander return self->direction; } -mp_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *self, bool value) { +mp_negative_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *self, bool value) { size_t current_value; common_hal_i2cioexpander_ioexpander_get_output_value(self->expander, ¤t_value); size_t new_value; @@ -103,9 +103,9 @@ mp_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *s return 0; } -mp_errno_t common_hal_i2cioexpander_iopin_get_value(i2cioexpander_iopin_obj_t *self, bool *value) { +mp_negative_errno_t common_hal_i2cioexpander_iopin_get_value(i2cioexpander_iopin_obj_t *self, bool *value) { size_t full_value; - mp_errno_t result = common_hal_i2cioexpander_ioexpander_get_input_value(self->expander, &full_value); + mp_negative_errno_t result = common_hal_i2cioexpander_ioexpander_get_input_value(self->expander, &full_value); if (result != 0) { return result; } diff --git a/tools/safe_mode_finder.py b/tools/safe_mode_finder.py index af3ff0d3c6d7b..fece85312788f 100644 --- a/tools/safe_mode_finder.py +++ b/tools/safe_mode_finder.py @@ -86,8 +86,8 @@ print(e.has_dwarf_info()) d = e.get_dwarf_info() # print(dir(d)) - arranges = d.get_aranges() - cu_offset = arranges.cu_offset_at_addr(pc) + aranges = d.get_aranges() + cu_offset = aranges.cu_offset_at_addr(pc) if not cu_offset: cu_offset = 0 main_cu = d.get_CU_at(cu_offset) From 4fdf0d10d83c672b2f493b7ef7c78c408a3fa596 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Feb 2026 16:53:47 -0800 Subject: [PATCH 293/517] Fix digitalio allocation location --- shared-module/bitbangio/I2C.c | 7 +++++-- shared-module/bitbangio/SPI.c | 9 ++++++--- shared-module/fourwire/FourWire.c | 9 ++++++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/shared-module/bitbangio/I2C.c b/shared-module/bitbangio/I2C.c index 8e27298af7c0a..d88455729e28b 100644 --- a/shared-module/bitbangio/I2C.c +++ b/shared-module/bitbangio/I2C.c @@ -167,11 +167,14 @@ void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self, self->us_delay = 1; } + // Allocate the pins in the same place as self. + bool use_port_allocation = !gc_alloc_possible() || !gc_ptr_on_heap(self); + // Convert scl from Pin to DigitalInOutProtocol - self->scl = digitalinout_protocol_from_pin(scl, MP_QSTR_scl, false, false, &self->own_scl); + self->scl = digitalinout_protocol_from_pin(scl, MP_QSTR_scl, false, use_port_allocation, &self->own_scl); // Convert sda from Pin to DigitalInOutProtocol - self->sda = digitalinout_protocol_from_pin(sda, MP_QSTR_sda, false, false, &self->own_sda); + self->sda = digitalinout_protocol_from_pin(sda, MP_QSTR_sda, false, use_port_allocation, &self->own_sda); digitalinout_protocol_switch_to_output(self->scl, true, DRIVE_MODE_OPEN_DRAIN); digitalinout_protocol_switch_to_output(self->sda, true, DRIVE_MODE_OPEN_DRAIN); diff --git a/shared-module/bitbangio/SPI.c b/shared-module/bitbangio/SPI.c index b891d22c9aaaa..9b89d1d4a1b05 100644 --- a/shared-module/bitbangio/SPI.c +++ b/shared-module/bitbangio/SPI.c @@ -23,19 +23,22 @@ void shared_module_bitbangio_spi_construct(bitbangio_spi_obj_t *self, mp_obj_t clock, mp_obj_t mosi, mp_obj_t miso) { + // Allocate the pins in the same place as self. + bool use_port_allocation = !gc_alloc_possible() || !gc_ptr_on_heap(self); + // Convert clock from Pin to DigitalInOutProtocol - self->clock = digitalinout_protocol_from_pin(clock, MP_QSTR_clock, false, false, &self->own_clock); + self->clock = digitalinout_protocol_from_pin(clock, MP_QSTR_clock, false, use_port_allocation, &self->own_clock); digitalinout_protocol_switch_to_output(self->clock, self->polarity == 1, DRIVE_MODE_PUSH_PULL); // Convert mosi from Pin to DigitalInOutProtocol (optional) - self->mosi = digitalinout_protocol_from_pin(mosi, MP_QSTR_mosi, true, false, &self->own_mosi); + self->mosi = digitalinout_protocol_from_pin(mosi, MP_QSTR_mosi, true, use_port_allocation, &self->own_mosi); self->has_mosi = (self->mosi != mp_const_none); if (self->has_mosi) { digitalinout_protocol_switch_to_output(self->mosi, false, DRIVE_MODE_PUSH_PULL); } // Convert miso from Pin to DigitalInOutProtocol (optional) - self->miso = digitalinout_protocol_from_pin(miso, MP_QSTR_miso, true, false, &self->own_miso); + self->miso = digitalinout_protocol_from_pin(miso, MP_QSTR_miso, true, use_port_allocation, &self->own_miso); self->has_miso = (self->miso != mp_const_none); // MISO starts out as input by default, no need to change diff --git a/shared-module/fourwire/FourWire.c b/shared-module/fourwire/FourWire.c index 0266401f9ce3a..d87cb0cec0f95 100644 --- a/shared-module/fourwire/FourWire.c +++ b/shared-module/fourwire/FourWire.c @@ -29,20 +29,23 @@ void common_hal_fourwire_fourwire_construct(fourwire_fourwire_obj_t *self, self->polarity = polarity; self->phase = phase; - self->command = digitalinout_protocol_from_pin(command, MP_QSTR_command, true, false, &self->own_command); + // Allocate the pins in the same place as self. + bool use_port_allocation = !gc_alloc_possible() || !gc_ptr_on_heap(self); + + self->command = digitalinout_protocol_from_pin(command, MP_QSTR_command, true, use_port_allocation, &self->own_command); if (self->command != mp_const_none) { digitalinout_protocol_switch_to_output(self->command, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(command); } - self->reset = digitalinout_protocol_from_pin(reset, MP_QSTR_reset, true, false, &self->own_reset); + self->reset = digitalinout_protocol_from_pin(reset, MP_QSTR_reset, true, use_port_allocation, &self->own_reset); if (self->reset != mp_const_none) { digitalinout_protocol_switch_to_output(self->reset, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(reset); common_hal_fourwire_fourwire_reset(self); } - self->chip_select = digitalinout_protocol_from_pin(chip_select, MP_QSTR_chip_select, true, false, &self->own_chip_select); + self->chip_select = digitalinout_protocol_from_pin(chip_select, MP_QSTR_chip_select, true, use_port_allocation, &self->own_chip_select); if (self->chip_select != mp_const_none) { digitalinout_protocol_switch_to_output(self->chip_select, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(chip_select); From e95921bc3a5ed3f314dab5bfcbd75108ade26efa Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Feb 2026 17:01:36 -0800 Subject: [PATCH 294/517] Fix samd pin tracking --- ports/atmel-samd/supervisor/port.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 9654a791dbb32..31bc5faf82703 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -341,6 +341,9 @@ safe_mode_t port_init(void) { // Reset everything into a known state before board_init. reset_port(); + // Reset the pins too. + reset_all_pins(); + #ifdef SAMD21 if (PM->RCAUSE.bit.BOD33 == 1 || PM->RCAUSE.bit.BOD12 == 1) { return SAFE_MODE_BROWNOUT; From b439846408b15f7acdb174015bdff235f581d3dd Mon Sep 17 00:00:00 2001 From: Sean Carolan Date: Tue, 3 Feb 2026 09:53:34 -0500 Subject: [PATCH 295/517] Use assigned Espressif USB PID 0x8348 PID allocated via https://github.com/espressif/usb-pids/pull/292 Co-Authored-By: Claude Opus 4.5 --- ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk index 6b43abb68396f..2413c43cebea1 100644 --- a/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk @@ -1,5 +1,5 @@ USB_VID = 0x303A -USB_PID = 0x811B +USB_PID = 0x8348 USB_PRODUCT = "M5Stack CoreS3 SE" USB_MANUFACTURER = "M5Stack" From 74e380bed0b44bb5081500c4ff9e130a313cc251 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 08:54:28 -0800 Subject: [PATCH 296/517] Fix last overflowing board --- ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk b/ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk index 0e3b1b9048950..fc59a101e454f 100644 --- a/ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk +++ b/ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk @@ -11,3 +11,4 @@ INTERNAL_FLASH_FILESYSTEM = 1 CIRCUITPY_AUDIOIO = 0 CIRCUITPY_DISPLAYIO = 1 CIRCUITPY_STAGE = 1 +CIRCUITPY_DIGITALINOUT_PROTOCOL = 0 From 1dc9c83b54a5ad61ca7cfaccb49efa9b2d2fd7d6 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 09:34:07 -0800 Subject: [PATCH 297/517] Fetch submodules --- .github/workflows/run-tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 4fded6bbbdf27..01172041b6205 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -86,6 +86,11 @@ jobs: python-version: 3.13 - name: Set up Zephyr uses: ./.github/actions/deps/ports/zephyr-cp + - name: Set up submodules + id: set-up-submodules + uses: ./.github/actions/deps/submodules + with: + target: zephyr-cp - name: Set up external uses: ./.github/actions/deps/external - name: Build native sim target From df8b3e530197bf3c7857506a04253ecfcd3be3f2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 09:36:24 -0800 Subject: [PATCH 298/517] Guard zephyr include. Add fetch-port-submodules make target --- ports/zephyr-cp/Makefile | 5 ++++- supervisor/shared/safe_mode.c | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index 15ae523fb72ca..622fe4901a99f 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -8,7 +8,7 @@ BUILD ?= build-$(BOARD) TRANSLATION ?= en_US -.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug run clean menuconfig all clean-all test +.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug run clean menuconfig all clean-all test fetch-port-submodules $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) @@ -38,6 +38,9 @@ menuconfig: clean: rm -rf $(BUILD) +fetch-port-submodules: + python ../../tools/ci_fetch_deps.py zephyr-cp + # Build all boards. The + prefix allows jobserver file descriptors to be passed through. # This enables parallel builds across all boards when invoked with `make -jN all`. all: diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index a3e4de235c7bd..36a0e99180b5c 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -18,7 +18,9 @@ #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" +#if __ZEPHYR__ #include +#endif #define SAFE_MODE_DATA_GUARD 0xad0000af #define SAFE_MODE_DATA_GUARD_MASK 0xff0000ff From 1ed4442e0533329f7d45310c7ccec9ba3d57fe28 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 11:00:25 -0800 Subject: [PATCH 299/517] Fix thunk issue --- ports/zephyr-cp/mpconfigport.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ports/zephyr-cp/mpconfigport.h b/ports/zephyr-cp/mpconfigport.h index 5b5b077a37152..491b5293e2ebc 100644 --- a/ports/zephyr-cp/mpconfigport.h +++ b/ports/zephyr-cp/mpconfigport.h @@ -17,6 +17,9 @@ #define CIRCUITPY_DEBUG_TINYUSB 0 +// Disable native _Float16 handling for host builds. +#define MICROPY_FLOAT_USE_NATIVE_FLT16 (0) + //////////////////////////////////////////////////////////////////////////////////////////////////// // This also includes mpconfigboard.h. From ac063d15fc1ae440fccd5834ec5918122ed2d0c0 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 11:44:48 -0800 Subject: [PATCH 300/517] Zephyr ifdef --- supervisor/shared/safe_mode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index 36a0e99180b5c..9650556546030 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -18,7 +18,7 @@ #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" -#if __ZEPHYR__ +#ifdef __ZEPHYR__ #include #endif @@ -104,7 +104,7 @@ void PLACE_IN_ITCM(safe_mode_on_next_reset)(safe_mode_t reason) { // Don't inline this so it's easy to break on it from GDB. void __attribute__((noinline, )) PLACE_IN_ITCM(reset_into_safe_mode)(safe_mode_t reason) { if (_safe_mode > SAFE_MODE_BROWNOUT && reason > SAFE_MODE_BROWNOUT) { - #if __ZEPHYR__ + #ifdef __ZEPHYR__ printk("Already in safe mode\n"); printk("Reason: %d\n", reason); printk("Current safe mode: %d\n", _safe_mode); From f7c66cc518a61c6bb23dc2c0a1891c3203198aa8 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 11:46:45 -0800 Subject: [PATCH 301/517] install mtools --- .github/actions/deps/ports/zephyr-cp/action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/deps/ports/zephyr-cp/action.yml b/.github/actions/deps/ports/zephyr-cp/action.yml index 75ff232562c4d..5f52cc7f0c259 100644 --- a/.github/actions/deps/ports/zephyr-cp/action.yml +++ b/.github/actions/deps/ports/zephyr-cp/action.yml @@ -3,11 +3,11 @@ name: Fetch Zephyr port deps runs: using: composite steps: - - name: Get libusb + - name: Get libusb and mtools if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y libusb-1.0-0-dev libudev-dev + sudo apt-get install -y libusb-1.0-0-dev libudev-dev mtools shell: bash - name: Setup Zephyr project uses: zephyrproject-rtos/action-zephyr-setup@v1 From 6ab3b0be848ef9ebe67a35399d64c7abf57f472c Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 14:58:31 -0800 Subject: [PATCH 302/517] Move other ports to gchelper --- ports/analog/Makefile | 3 +++ ports/atmel-samd/Makefile | 8 ++++++++ ports/broadcom/Makefile | 2 ++ ports/cxd56/Makefile | 4 ++++ ports/espressif/Makefile | 7 +++++++ ports/litex/Makefile | 5 +++++ ports/mimxrt10xx/Makefile | 6 +++++- ports/nordic/Makefile | 4 ++++ ports/raspberrypi/Makefile | 5 +++++ ports/renode/Makefile | 3 +++ ports/silabs/Makefile | 4 +++- ports/stm/Makefile | 3 +++ py/circuitpy_mpconfig.h | 4 ++++ 13 files changed, 56 insertions(+), 2 deletions(-) diff --git a/ports/analog/Makefile b/ports/analog/Makefile index 0ae276985f7f4..4dd174d2f6cf2 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -156,6 +156,9 @@ endif SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S += $(STARTUPFILE) +SRC_S += shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c # Needed to compile some MAX32 headers CFLAGS += -D$(MCU_VARIANT_UPPER) \ diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 9582eff212d60..ec414f3d6330e 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -284,6 +284,7 @@ SRC_C += \ lib/tinyusb/src/portable/microchip/samd/dcd_samd.c \ mphalport.c \ reset.c \ + shared/runtime/gchelper_native.c \ timer_handler.c \ $(SRC_PERIPHERALS) \ @@ -308,6 +309,12 @@ ifeq ($(CIRCUITPY_AUDIOBUSIO),1) SRC_C += peripherals/samd/i2s.c peripherals/samd/$(PERIPHERALS_CHIP_FAMILY)/i2s.c endif +ifeq ($(CHIP_FAMILY), samd21) +SRC_S += shared/runtime/gchelper_thumb1.s +else +SRC_S += shared/runtime/gchelper_thumb2.s +endif + SRC_S_UPPER = supervisor/shared/cpu_regs.S OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) @@ -317,6 +324,7 @@ ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) diff --git a/ports/broadcom/Makefile b/ports/broadcom/Makefile index f60ebbf045e1f..dda93c6fd7ce0 100644 --- a/ports/broadcom/Makefile +++ b/ports/broadcom/Makefile @@ -59,6 +59,8 @@ SRC_C += bindings/videocore/__init__.c \ SRC_S = peripherals/broadcom/boot$(SUFFIX).s +SRC_C += shared/runtime/gchelper_generic.c + OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) ifeq ($(INTERNAL_LIBM),1) diff --git a/ports/cxd56/Makefile b/ports/cxd56/Makefile index fdf3646c80d27..6305a5b1d4bc5 100644 --- a/ports/cxd56/Makefile +++ b/ports/cxd56/Makefile @@ -111,6 +111,9 @@ LDFLAGS = \ CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_CXD56 -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_CDC_TX_BUFSIZE=1024 -DCFG_TUD_MSC_BUFSIZE=512 $(CFLAGS_MOD) SRC_S_UPPER = supervisor/shared/cpu_regs.S +SRC_S = shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c SRC_C += \ background.c \ @@ -120,6 +123,7 @@ SRC_C += \ lib/tinyusb/src/portable/sony/cxd56/dcd_cxd56.c \ OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) ifeq ($(INTERNAL_LIBM),1) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 8302726aad7a4..f83c2446df1a0 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -216,9 +216,12 @@ ifeq ($(IDF_TARGET_ARCH),xtensa) # `#include "xtensa/xtensa_api.h"`. CFLAGS += -mlongcalls -isystem esp-idf/components/xtensa/deprecated_include/ -Wno-error=cpp + CFLAGS += -DMICROPY_GCREGS_SETJMP=1 # Wrap longjmp with a patched version that protects register window update with a critical section LDFLAGS += -Wl,--wrap=longjmp + + SRC_C += shared/runtime/gchelper_generic.c else ifeq ($(IDF_TARGET_ARCH),riscv) ifeq ($(IDF_TARGET),esp32p4) @@ -230,6 +233,9 @@ else ifeq ($(IDF_TARGET_ARCH),riscv) LDFLAGS += \ -Lesp-idf/components/riscv/ld \ -Trom.api.ld + + SRC_C += shared/runtime/gchelper_native.c + SRC_S = shared/runtime/gchelper_rv32i.s endif @@ -594,6 +600,7 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) $(BUILD)/$(FATFS_DIR)/ff.o: COPT += -Os diff --git a/ports/litex/Makefile b/ports/litex/Makefile index 98abe985699cf..9ef34cd8ff8c1 100644 --- a/ports/litex/Makefile +++ b/ports/litex/Makefile @@ -81,6 +81,10 @@ SRC_S_UPPER = \ crt0-vexriscv.S \ supervisor/shared/cpu_regs.S +SRC_S = shared/runtime/gchelper_rv32i.s + +SRC_C += shared/runtime/gchelper_native.c + $(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align ifneq ($(FROZEN_MPY_DIR),) @@ -94,6 +98,7 @@ ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) diff --git a/ports/mimxrt10xx/Makefile b/ports/mimxrt10xx/Makefile index e6928eead619d..9cb1df8014520 100644 --- a/ports/mimxrt10xx/Makefile +++ b/ports/mimxrt10xx/Makefile @@ -165,6 +165,10 @@ SRC_S_UPPER = \ sdk/devices/$(CHIP_FAMILY)/gcc/startup_$(CHIP_CORE).S \ supervisor/shared/cpu_regs.S +SRC_S = shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c + OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_SDK:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) @@ -172,7 +176,7 @@ ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S:.S=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) diff --git a/ports/nordic/Makefile b/ports/nordic/Makefile index aa614e097ba3c..cd1360b9fd425 100755 --- a/ports/nordic/Makefile +++ b/ports/nordic/Makefile @@ -147,6 +147,9 @@ $(patsubst %.c,$(BUILD)/%.o,$(SRC_DCD)): CFLAGS += -Wno-missing-prototypes endif # CIRCUITPY_USB_DEVICE SRC_S_UPPER = supervisor/shared/cpu_regs.S +SRC_S = shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c OBJ += $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_NRFX:.c=.o)) @@ -155,6 +158,7 @@ ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile index b75d542325166..097a4e7728f70 100644 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -422,6 +422,8 @@ SRC_S_UPPER_CHIP_VARIANT := \ sdk/src/rp2_common/pico_float/float_aeabi_rp2040.S \ sdk/src/rp2_common/pico_mem_ops/mem_ops_aeabi.S \ +SRC_S = shared/runtime/gchelper_thumb1.s + PICO_LDFLAGS += \ $(PICO_WRAP_FLOAT_AEABI_FLAGS) \ $(PICO_WRAP_FLOAT_SCI_FLAGS) \ @@ -461,6 +463,8 @@ SRC_S_UPPER_CHIP_VARIANT := \ sdk/src/rp2_common/pico_float/float_sci_m33_vfp.S \ sdk/src/rp2_common/pico_float/float_common_m33.S \ +SRC_S = shared/runtime/gchelper_thumb2.s + PICO_LDFLAGS += $(PICO_WRAP_FLOAT_SCI_FLAGS) $(PICO_WRAP_DOUBLE_FLAGS) ifeq ($(CHIP_PACKAGE),A) @@ -473,6 +477,7 @@ endif endif +SRC_C += shared/runtime/gchelper_native.c SRC_SDK := \ src/common/hardware_claim/claim.c \ diff --git a/ports/renode/Makefile b/ports/renode/Makefile index 92541c03e77b9..40013ac4f661b 100644 --- a/ports/renode/Makefile +++ b/ports/renode/Makefile @@ -46,6 +46,9 @@ SRC_C += \ mphalport.c \ SRC_S_UPPER = supervisor/shared/cpu_regs.S +SRC_S = shared/runtime/gchelper_thumb1.s + +SRC_C += shared/runtime/gchelper_native.c OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) diff --git a/ports/silabs/Makefile b/ports/silabs/Makefile index 58929dd498fed..97b0e4c597fde 100644 --- a/ports/silabs/Makefile +++ b/ports/silabs/Makefile @@ -90,7 +90,9 @@ ifneq (,$(wildcard boards/$(BOARD)/sensor.c)) SRC_C += boards/$(BOARD)/sensor.c endif -SRC_S = boards/mp_efr32xg24_gchelper.s +SRC_S = shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c OBJ += $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) diff --git a/ports/stm/Makefile b/ports/stm/Makefile index 9db283767e0ec..f85991cfedb9e 100755 --- a/ports/stm/Makefile +++ b/ports/stm/Makefile @@ -214,8 +214,11 @@ endif SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S = \ + shared/runtime/gchelper_thumb2.s \ st_driver/cmsis_device_$(MCU_SERIES_LOWER)/Source/Templates/gcc/startup_$(MCU_VARIANT_LOWER).s +SRC_C += shared/runtime/gchelper_native.c + ifneq ($(FROZEN_MPY_DIR),) FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 386d1ee3d1e12..7715e77eab59b 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -46,6 +46,10 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_VFS_LFS1 (0) #define MICROPY_VFS_LFS2 (0) +#ifndef MICROPY_GCREGS_SETJMP +#define MICROPY_GCREGS_SETJMP (0) +#endif + // Sorted alphabetically for easy finding. // // default is 128; consider raising to reduce fragmentation. From 38c118b071d966069c061201ddde1ed79b84e48d Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 4 Feb 2026 01:08:24 +0100 Subject: [PATCH 303/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 38 +++++++++++++++++++++++++++++++++----- locale/el.po | 38 +++++++++++++++++++++++++++++++++----- locale/hi.po | 38 +++++++++++++++++++++++++++++++++----- locale/ko.po | 38 +++++++++++++++++++++++++++++++++----- locale/ru.po | 38 +++++++++++++++++++++++++++++++++----- locale/tr.po | 38 +++++++++++++++++++++++++++++++++----- 6 files changed, 198 insertions(+), 30 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index b1e50a11e1271..b7964d4ff1383 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -130,7 +130,8 @@ msgstr "%q v %q musí být typu %q, ne %q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q se právě používá" @@ -143,7 +144,7 @@ msgid "%q indices must be integers, not %s" msgstr "Indexy %q musí být celá čísla, nikoli %s" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c msgid "%q init failed" msgstr "Inicializace %q selhala" @@ -246,6 +247,14 @@ msgstr "%q musí být typu %q, ne %q" msgid "%q must be power of 2" msgstr "%q musí být mocnina 2" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "%q je mimo hranice" @@ -315,7 +324,7 @@ msgstr "%s chyba 0x%x" msgid "'%q' argument required" msgstr "Je vyžadován argument '%q'" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "Objekt '%q' nepodporuje '%q'" @@ -782,6 +791,10 @@ msgstr "Nelze změnit USB zařízení" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "Není možné vytvořit nový adaptér; použití _bleio.adapter;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -816,6 +829,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "Nelze nastavit hodnotu, když směr je vstup." @@ -957,6 +971,7 @@ msgid "Done" msgstr "Hotovo" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "" @@ -1297,8 +1312,8 @@ msgstr "" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" @@ -1903,6 +1918,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -2568,6 +2584,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -3843,6 +3863,10 @@ msgstr "není implementováno pro komplexní dtype" msgid "not supported for input types" msgstr "není podporováno pro vstupní typy" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3856,6 +3880,10 @@ msgstr "objekt " msgid "object '%s' isn't a tuple or list" msgstr "objekt '%s' není tuple or nebo list" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" diff --git a/locale/el.po b/locale/el.po index 10d7d4d1f51bd..b40162b35b783 100644 --- a/locale/el.po +++ b/locale/el.po @@ -134,7 +134,8 @@ msgstr "%q στο %q πρέπει να είναι τύπου %q, όχι %q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q είναι σε χρήση" @@ -147,7 +148,7 @@ msgid "%q indices must be integers, not %s" msgstr "%q δείκτες πρέπει να είναι ακέραιοι, όχι %s" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c msgid "%q init failed" msgstr "%q εκκίνηση απέτυχε" @@ -250,6 +251,14 @@ msgstr "%q πρέπει να είναι τύπου %q, όχι %q" msgid "%q must be power of 2" msgstr "%q πρέπει να είναι δύναμη του 2" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "%q εκτός ορίων" @@ -319,7 +328,7 @@ msgstr "%s σφάλμα 0x%x" msgid "'%q' argument required" msgstr "'%q' όρισμα απαιτείται" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "'%q' αντικείμενο δεν υποστηρίζει '%q'" @@ -785,6 +794,10 @@ msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "" "Δεν μπορεί να δημιουργηθεί νέο Adapter; χρησιμοποιείστε _bleio.adapter;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -821,6 +834,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "Δεν μπορεί να οριστεί τιμή οταν η κατεύθυνση είναι input." @@ -964,6 +978,7 @@ msgid "Done" msgstr "Ολοκληρώθηκε" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "Ο τρόπος οδήγησης δεν χρησιμοποιείται όταν η κατεύθυνση είναι είσοδος." @@ -1303,8 +1318,8 @@ msgstr "" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" @@ -1908,6 +1923,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -2567,6 +2583,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -3842,6 +3862,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3855,6 +3879,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index ebbedfe7639cc..08b91c9bad6f4 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -121,7 +121,8 @@ msgstr "" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "" @@ -134,7 +135,7 @@ msgid "%q indices must be integers, not %s" msgstr "" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c msgid "%q init failed" msgstr "" @@ -237,6 +238,14 @@ msgstr "" msgid "%q must be power of 2" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "" @@ -306,7 +315,7 @@ msgstr "" msgid "'%q' argument required" msgstr "" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "" @@ -768,6 +777,10 @@ msgstr "" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -802,6 +815,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "" @@ -941,6 +955,7 @@ msgid "Done" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "" @@ -1279,8 +1294,8 @@ msgstr "" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" @@ -1882,6 +1897,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -2541,6 +2557,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -3816,6 +3836,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3829,6 +3853,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 182f4d08d6bb8..7963a7a9822d6 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -132,7 +132,8 @@ msgstr "%q의 %q는 %q가 아니라 %q 유형이어야 합니다" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q 사용 중입니다" @@ -145,7 +146,7 @@ msgid "%q indices must be integers, not %s" msgstr "%q 인덱스는 %s 가 아닌 정수 여야합니다" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c msgid "%q init failed" msgstr "%q 초기화 실패" @@ -253,6 +254,14 @@ msgstr "%q는 %q가 아니라 %q 유형이어야 합니다" msgid "%q must be power of 2" msgstr "%q는 2의 거듭제곱이어야 합니다" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c #, fuzzy msgid "%q out of bounds" @@ -328,7 +337,7 @@ msgstr "%s 오류 0x%x" msgid "'%q' argument required" msgstr "'%q' 인수가 필요합니다" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "'%q' 개체가 '%q'를 지원하지 않습니다" @@ -810,6 +819,10 @@ msgstr "현재 USB 디바이스를 변경할 수 없습니다" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "_bleio.adapter를 사용해서; 새로운 Adapter를 만들 수 없습니다;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -844,6 +857,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "방향이 입력되면 값을 설정할 수 없습니다." @@ -986,6 +1000,7 @@ msgid "Done" msgstr "완료" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "방향을 입력할 때 드라이브 모드는 사용되지 않습니다." @@ -1330,8 +1345,8 @@ msgstr "출력 함수로 인해 종료되었다" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" @@ -1955,6 +1970,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c #, fuzzy msgid "Pull not used when direction is output." msgstr "방향이 출력 될 때 풀은 사용되지 않습니다" @@ -2616,6 +2632,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -3891,6 +3911,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3904,6 +3928,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" diff --git a/locale/ru.po b/locale/ru.po index 653eed6c0bfb9..45ecfb19f0faf 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -134,7 +134,8 @@ msgstr "%q в %q должно быть типа %q, а не %q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q используется" @@ -147,7 +148,7 @@ msgid "%q indices must be integers, not %s" msgstr "Индексы %q должны быть целыми числами, а не %s" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c msgid "%q init failed" msgstr "Инициализация %q не удалась" @@ -250,6 +251,14 @@ msgstr "%q должно быть типа %q, а не %q" msgid "%q must be power of 2" msgstr "%q должен быть во 2-й степени" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "%q за пределом" @@ -319,7 +328,7 @@ msgstr "%s ошибка 0x%x" msgid "'%q' argument required" msgstr "Требуется аргумент '%q'" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "Объект '%q' не поддерживает '%q'" @@ -787,6 +796,10 @@ msgstr "Невозможно изменить USB устройство сейч msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "Невозможно создать новый Adapter; используйте _bleio.adapter;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -823,6 +836,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "Невозможно установить значение при вводе направления." @@ -967,6 +981,7 @@ msgid "Done" msgstr "Выполнено" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "Режим движения не используется при вводе направления." @@ -1318,8 +1333,8 @@ msgstr "Прерывается функцией выхода" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" @@ -1933,6 +1948,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "Тяга не используется, когда выводится направление." @@ -2607,6 +2623,10 @@ msgstr "__new__ arg должен быть пользовательского т msgid "a bytes-like object is required" msgstr "Требуется байтоподобный объект" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "адреса пусты" @@ -3905,6 +3925,10 @@ msgstr "не реализовано для сложного типа d" msgid "not supported for input types" msgstr "Не поддерживается для типов ввода" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "Количество баллов должно быть не менее 2" @@ -3918,6 +3942,10 @@ msgstr "объект " msgid "object '%s' isn't a tuple or list" msgstr "Объект \"%s\" не является кортежом или списком" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "Объект не поддерживает назначение элементов" diff --git a/locale/tr.po b/locale/tr.po index 511e9cb7f4654..7951c91c4bf58 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -132,7 +132,8 @@ msgstr "" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q kullanımda" @@ -145,7 +146,7 @@ msgid "%q indices must be integers, not %s" msgstr "%q indeksleri integer olmalı, %s değil" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c msgid "%q init failed" msgstr "%q init başarısız oldu" @@ -248,6 +249,14 @@ msgstr "" msgid "%q must be power of 2" msgstr "%q, 2'nin kuvveti olmalıdır" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "%q sınırların dışında" @@ -317,7 +326,7 @@ msgstr "%s hatası 0x%x" msgid "'%q' argument required" msgstr "'%q' argümanı gerekli" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "'%q' nesnesi '%q' öğesini desteklemiyor" @@ -781,6 +790,10 @@ msgstr "USB aygıtları şu an değiştirilemez" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "yeni Adaptör oluşturulamadı; _bleio.adapter kullanın;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -816,6 +829,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "Yön, giriş olduğunda değer ayarlanamıyor." @@ -955,6 +969,7 @@ msgid "Done" msgstr "Tamamlandı" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "Yön, giriş olduğunda sürüş modu kullanılmaz." @@ -1297,8 +1312,8 @@ msgstr "" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c #: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/mipidsi/Display.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" @@ -1904,6 +1919,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -2563,6 +2579,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -3838,6 +3858,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3851,6 +3875,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" From 57ce5dac643181e638f016c54d2238bc37b80b25 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 16:17:55 -0800 Subject: [PATCH 304/517] Fix AFLAGS issues --- ports/raspberrypi/Makefile | 12 +++++------- ports/silabs/Makefile | 3 ++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile index 097a4e7728f70..1784d325d89b7 100644 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -396,6 +396,8 @@ OTHER_PICO_FLAGS := \ -Wl,--wrap=__aeabi_uidivmod \ -Wl,--wrap=__aeabi_uldivmod +SRC_S = shared/runtime/gchelper_thumb1.s + ifeq ($(CHIP_VARIANT),RP2040) CFLAGS += \ -march=armv6-m \ @@ -422,8 +424,6 @@ SRC_S_UPPER_CHIP_VARIANT := \ sdk/src/rp2_common/pico_float/float_aeabi_rp2040.S \ sdk/src/rp2_common/pico_mem_ops/mem_ops_aeabi.S \ -SRC_S = shared/runtime/gchelper_thumb1.s - PICO_LDFLAGS += \ $(PICO_WRAP_FLOAT_AEABI_FLAGS) \ $(PICO_WRAP_FLOAT_SCI_FLAGS) \ @@ -436,12 +436,12 @@ UF2_ID = 0xE48BFF56 DOUBLE_EABI = rp2040 endif ifeq ($(CHIP_VARIANT),RP2350) -CFLAGS += \ - -march=armv8-m.main+fp+dsp \ +AFLAGS = -march=armv8-m.main+fp+dsp \ -mthumb \ - -mabi=aapcs-linux \ -mcpu=cortex-m33 \ -mfloat-abi=softfp +CFLAGS += $(AFLAGS) \ + -mabi=aapcs-linux # ARM Secure family id UF2_ID = 0xe48bff59 @@ -463,8 +463,6 @@ SRC_S_UPPER_CHIP_VARIANT := \ sdk/src/rp2_common/pico_float/float_sci_m33_vfp.S \ sdk/src/rp2_common/pico_float/float_common_m33.S \ -SRC_S = shared/runtime/gchelper_thumb2.s - PICO_LDFLAGS += $(PICO_WRAP_FLOAT_SCI_FLAGS) $(PICO_WRAP_DOUBLE_FLAGS) ifeq ($(CHIP_PACKAGE),A) diff --git a/ports/silabs/Makefile b/ports/silabs/Makefile index 97b0e4c597fde..34b6346d0d428 100644 --- a/ports/silabs/Makefile +++ b/ports/silabs/Makefile @@ -90,7 +90,8 @@ ifneq (,$(wildcard boards/$(BOARD)/sensor.c)) SRC_C += boards/$(BOARD)/sensor.c endif -SRC_S = shared/runtime/gchelper_thumb2.s +AFLAGS = -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard +SRC_S = shared/runtime/gchelper_thumb1.s SRC_C += shared/runtime/gchelper_native.c From 86bb6dbd101267dadcd10c92f0387046bc6d74b6 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 4 Feb 2026 09:38:11 -0600 Subject: [PATCH 305/517] raspberrypi: PDMIn: Fix sample rate calculation. The sample rate needs to be mutiplied by oversampling, not by 32. --- ports/raspberrypi/common-hal/audiobusio/PDMIn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c index 327bec8de7280..afe35cc19014c 100644 --- a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c +++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -43,7 +43,7 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, // Use the state machine to manage pins. common_hal_rp2pio_statemachine_construct(&self->state_machine, pdmin, MP_ARRAY_SIZE(pdmin), - sample_rate * 32 * 2, // Frequency based on sample rate + sample_rate * OVERSAMPLING * 2, // Frequency based on sample rate NULL, 0, NULL, 0, // may_exec NULL, 1, PIO_PINMASK32_NONE, PIO_PINMASK32_ALL, // out pin From c95cbea009cd8a53e66e01a748297c196637bf13 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 4 Feb 2026 09:38:53 -0600 Subject: [PATCH 306/517] raspberrypi: PDMIn: Fix sample rate check. The "actual frequency" is the PIO frequency, which is twice the audio clock. --- ports/raspberrypi/common-hal/audiobusio/PDMIn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c index afe35cc19014c..52fe03b700268 100644 --- a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c +++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -64,7 +64,7 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, PIO_FIFO_TYPE_DEFAULT, PIO_MOV_STATUS_DEFAULT, PIO_MOV_N_DEFAULT); uint32_t actual_frequency = common_hal_rp2pio_statemachine_get_frequency(&self->state_machine); - if (actual_frequency < MIN_MIC_CLOCK) { + if (actual_frequency < 2 * MIN_MIC_CLOCK) { // 2 PIO samples per audio clock mp_raise_ValueError(MP_ERROR_TEXT("sampling rate out of range")); } From 5cda32b6dc7938a1322d907c69db191fc6bfb265 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 4 Feb 2026 09:39:27 -0600 Subject: [PATCH 307/517] raspberrypi: PDMIn: Free resources when failing to construct. This ensures that after using an invalid sample rate, the pin objects and pio object are deinitialized. --- ports/raspberrypi/common-hal/audiobusio/PDMIn.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c index 52fe03b700268..b436469d09098 100644 --- a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c +++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -65,6 +65,7 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, PIO_MOV_STATUS_DEFAULT, PIO_MOV_N_DEFAULT); uint32_t actual_frequency = common_hal_rp2pio_statemachine_get_frequency(&self->state_machine); if (actual_frequency < 2 * MIN_MIC_CLOCK) { // 2 PIO samples per audio clock + common_hal_audiobusio_pdmin_deinit(self); mp_raise_ValueError(MP_ERROR_TEXT("sampling rate out of range")); } From 5c70dc03879db8d54fafde168eac3af0f7ec7a3f Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 4 Feb 2026 10:30:28 -0600 Subject: [PATCH 308/517] audio: Remove unused function prototypes. --- shared-bindings/audiodelays/Chorus.h | 4 ---- shared-bindings/audiofreeverb/Freeverb.h | 4 ---- 2 files changed, 8 deletions(-) diff --git a/shared-bindings/audiodelays/Chorus.h b/shared-bindings/audiodelays/Chorus.h index 10c6448df8955..63e9ba9c28d2c 100644 --- a/shared-bindings/audiodelays/Chorus.h +++ b/shared-bindings/audiodelays/Chorus.h @@ -18,10 +18,6 @@ void common_hal_audiodelays_chorus_construct(audiodelays_chorus_obj_t *self, uin void common_hal_audiodelays_chorus_deinit(audiodelays_chorus_obj_t *self); bool common_hal_audiodelays_chorus_deinited(audiodelays_chorus_obj_t *self); -uint32_t common_hal_audiodelays_chorus_get_sample_rate(audiodelays_chorus_obj_t *self); -uint8_t common_hal_audiodelays_chorus_get_channel_count(audiodelays_chorus_obj_t *self); -uint8_t common_hal_audiodelays_chorus_get_bits_per_sample(audiodelays_chorus_obj_t *self); - mp_obj_t common_hal_audiodelays_chorus_get_delay_ms(audiodelays_chorus_obj_t *self); void common_hal_audiodelays_chorus_set_delay_ms(audiodelays_chorus_obj_t *self, mp_obj_t delay_ms); diff --git a/shared-bindings/audiofreeverb/Freeverb.h b/shared-bindings/audiofreeverb/Freeverb.h index 913953ebecf62..bde6cfffda7d2 100644 --- a/shared-bindings/audiofreeverb/Freeverb.h +++ b/shared-bindings/audiofreeverb/Freeverb.h @@ -18,10 +18,6 @@ void common_hal_audiofreeverb_freeverb_construct(audiofreeverb_freeverb_obj_t *s void common_hal_audiofreeverb_freeverb_deinit(audiofreeverb_freeverb_obj_t *self); bool common_hal_audiofreeverb_freeverb_deinited(audiofreeverb_freeverb_obj_t *self); -uint32_t common_hal_audiofreeverb_freeverb_get_sample_rate(audiofreeverb_freeverb_obj_t *self); -uint8_t common_hal_audiofreeverb_freeverb_get_channel_count(audiofreeverb_freeverb_obj_t *self); -uint8_t common_hal_audiofreeverb_freeverb_get_bits_per_sample(audiofreeverb_freeverb_obj_t *self); - mp_obj_t common_hal_audiofreeverb_freeverb_get_roomsize(audiofreeverb_freeverb_obj_t *self); void common_hal_audiofreeverb_freeverb_set_roomsize(audiofreeverb_freeverb_obj_t *self, mp_obj_t feedback); From ed54e9d1a08bec463e487e2471eb2a258a0b5f64 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 4 Feb 2026 10:30:54 -0600 Subject: [PATCH 309/517] audio: Call audiosample_mark_deinit. Also remove the deinited check when it's safe to do all deinit actions again. --- shared-module/audiodelays/Chorus.c | 5 ++--- shared-module/audiofreeverb/Freeverb.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/shared-module/audiodelays/Chorus.c b/shared-module/audiodelays/Chorus.c index 9262c6b4de195..3c35b902189cd 100644 --- a/shared-module/audiodelays/Chorus.c +++ b/shared-module/audiodelays/Chorus.c @@ -4,6 +4,7 @@ // // SPDX-License-Identifier: MIT #include "shared-bindings/audiodelays/Chorus.h" +#include "shared-bindings/audiocore/__init__.h" #include #include @@ -104,9 +105,7 @@ bool common_hal_audiodelays_chorus_deinited(audiodelays_chorus_obj_t *self) { } void common_hal_audiodelays_chorus_deinit(audiodelays_chorus_obj_t *self) { - if (common_hal_audiodelays_chorus_deinited(self)) { - return; - } + audiosample_mark_deinit(&self->base); self->chorus_buffer = NULL; self->buffer[0] = NULL; self->buffer[1] = NULL; diff --git a/shared-module/audiofreeverb/Freeverb.c b/shared-module/audiofreeverb/Freeverb.c index 28779bdcc84b2..b57e82cdca2e5 100644 --- a/shared-module/audiofreeverb/Freeverb.c +++ b/shared-module/audiofreeverb/Freeverb.c @@ -8,6 +8,7 @@ // Fixed point ideas from - Paul Stoffregen in the Teensy audio library https://github.com/PaulStoffregen/Audio/blob/master/effect_freeverb.cpp // #include "shared-bindings/audiofreeverb/Freeverb.h" +#include "shared-bindings/audiocore/__init__.h" #include "shared-module/synthio/__init__.h" #include @@ -124,9 +125,7 @@ bool common_hal_audiofreeverb_freeverb_deinited(audiofreeverb_freeverb_obj_t *se } void common_hal_audiofreeverb_freeverb_deinit(audiofreeverb_freeverb_obj_t *self) { - if (common_hal_audiofreeverb_freeverb_deinited(self)) { - return; - } + audiosample_mark_deinit(&self->base); self->buffer[0] = NULL; self->buffer[1] = NULL; } From c5066abd787d7b4ad971f105fb16d6eca601181e Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 4 Feb 2026 09:21:37 -0800 Subject: [PATCH 310/517] Update autogen file after merge --- ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 8a4eb3e6fb581..2cf9d7127d57e 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false From f545763d79cb37950a0e39fb3b6f35fc9eae5818 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 4 Feb 2026 10:58:38 -0800 Subject: [PATCH 311/517] Remove cpu_regs. We're using the MP stuff now --- main.c | 1 - ports/analog/Makefile | 2 - ports/atmel-samd/Makefile | 3 - ports/cxd56/Makefile | 2 - ports/espressif/Makefile | 3 - ports/litex/Makefile | 3 +- ports/mimxrt10xx/Makefile | 3 +- ports/nordic/Makefile | 2 - ports/raspberrypi/Makefile | 1 - ports/renode/Makefile | 2 - ports/stm/Makefile | 4 +- .../zephyr-cp/cptools/build_circuitpython.py | 2 - supervisor/shared/cpu_regs.S | 102 ------------------ supervisor/shared/cpu_regs.h | 38 ------- 14 files changed, 3 insertions(+), 165 deletions(-) delete mode 100644 supervisor/shared/cpu_regs.S delete mode 100644 supervisor/shared/cpu_regs.h diff --git a/main.c b/main.c index d187c56cee46d..7b936488fb794 100644 --- a/main.c +++ b/main.c @@ -31,7 +31,6 @@ #include "supervisor/cpu.h" #include "supervisor/filesystem.h" #include "supervisor/port.h" -#include "supervisor/shared/cpu_regs.h" #include "supervisor/shared/reload.h" #include "supervisor/shared/safe_mode.h" #include "supervisor/shared/serial.h" diff --git a/ports/analog/Makefile b/ports/analog/Makefile index 4dd174d2f6cf2..6b86bd7039636 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -154,7 +154,6 @@ LINKERFILE = linking/$(MCU_VARIANT_LOWER)_cktpy.ld LDFLAGS += -nostartfiles -specs=nano.specs endif -SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S += $(STARTUPFILE) SRC_S += shared/runtime/gchelper_thumb2.s @@ -255,7 +254,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) # List of sources for qstr extraction diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index ac1d50123b941..0aaf6e3d849bf 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -315,8 +315,6 @@ else SRC_S += shared/runtime/gchelper_thumb2.s endif -SRC_S_UPPER = supervisor/shared/cpu_regs.S - OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_ASF:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) @@ -325,7 +323,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) QSTR_GLOBAL_REQUIREMENTS += $(HEADER_BUILD)/sdiodata.h diff --git a/ports/cxd56/Makefile b/ports/cxd56/Makefile index 6305a5b1d4bc5..c4526f31252ac 100644 --- a/ports/cxd56/Makefile +++ b/ports/cxd56/Makefile @@ -110,7 +110,6 @@ LDFLAGS = \ CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_CXD56 -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_CDC_TX_BUFSIZE=1024 -DCFG_TUD_MSC_BUFSIZE=512 $(CFLAGS_MOD) -SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S = shared/runtime/gchelper_thumb2.s SRC_C += shared/runtime/gchelper_native.c @@ -124,7 +123,6 @@ SRC_C += \ OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index f83c2446df1a0..ebc4fa3a4e1f3 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -591,8 +591,6 @@ FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) endif -SRC_S_UPPER = supervisor/shared/cpu_regs.S - OBJ += $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) ifeq ($(INTERNAL_LIBM),1) @@ -601,7 +599,6 @@ endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) $(BUILD)/$(FATFS_DIR)/ff.o: COPT += -Os $(filter $(PY_BUILD)/../extmod/vfs_fat_%.o, $(PY_O)): COPT += -Os diff --git a/ports/litex/Makefile b/ports/litex/Makefile index 9ef34cd8ff8c1..940d751e3d92f 100644 --- a/ports/litex/Makefile +++ b/ports/litex/Makefile @@ -78,8 +78,7 @@ SRC_C += lib/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.c endif SRC_S_UPPER = \ - crt0-vexriscv.S \ - supervisor/shared/cpu_regs.S + crt0-vexriscv.S SRC_S = shared/runtime/gchelper_rv32i.s diff --git a/ports/mimxrt10xx/Makefile b/ports/mimxrt10xx/Makefile index 9cb1df8014520..74667fd3d6446 100644 --- a/ports/mimxrt10xx/Makefile +++ b/ports/mimxrt10xx/Makefile @@ -162,8 +162,7 @@ SRC_C += \ endif SRC_S_UPPER = \ - sdk/devices/$(CHIP_FAMILY)/gcc/startup_$(CHIP_CORE).S \ - supervisor/shared/cpu_regs.S + sdk/devices/$(CHIP_FAMILY)/gcc/startup_$(CHIP_CORE).S SRC_S = shared/runtime/gchelper_thumb2.s diff --git a/ports/nordic/Makefile b/ports/nordic/Makefile index cd1360b9fd425..0d8d8878f4ca1 100755 --- a/ports/nordic/Makefile +++ b/ports/nordic/Makefile @@ -146,7 +146,6 @@ SRC_C += $(SRC_DCD) $(patsubst %.c,$(BUILD)/%.o,$(SRC_DCD)): CFLAGS += -Wno-missing-prototypes endif # CIRCUITPY_USB_DEVICE -SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S = shared/runtime/gchelper_thumb2.s SRC_C += shared/runtime/gchelper_native.c @@ -159,7 +158,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) # nrfx uses undefined preprocessor variables quite casually, so we can't do diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile index 1784d325d89b7..cef2806c87497 100644 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -675,7 +675,6 @@ SRC_S_UPPER = sdk/src/rp2_common/hardware_irq/irq_handler_chain.S \ sdk/src/rp2_common/pico_double/double_aeabi_$(DOUBLE_EABI).S \ sdk/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S \ sdk/src/rp2_common/pico_crt0/crt0.S \ - supervisor/shared/cpu_regs.S \ $(SRC_S_UPPER_CHIP_VARIANT) ifeq ($(CIRCUITPY_PICODVI),1) diff --git a/ports/renode/Makefile b/ports/renode/Makefile index 40013ac4f661b..c4ce66194e9d1 100644 --- a/ports/renode/Makefile +++ b/ports/renode/Makefile @@ -45,7 +45,6 @@ SRC_C += \ background.c \ mphalport.c \ -SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S = shared/runtime/gchelper_thumb1.s SRC_C += shared/runtime/gchelper_native.c @@ -57,7 +56,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) $(BUILD)/%.o: $(BUILD)/%.S diff --git a/ports/stm/Makefile b/ports/stm/Makefile index f85991cfedb9e..c0e64e2339de1 100755 --- a/ports/stm/Makefile +++ b/ports/stm/Makefile @@ -212,9 +212,8 @@ ifneq ($(CIRCUITPY_USB),0) endif endif -SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S = \ - shared/runtime/gchelper_thumb2.s \ + shared/runtime/gchelper_thumb1.s \ st_driver/cmsis_device_$(MCU_SERIES_LOWER)/Source/Templates/gcc/startup_$(MCU_VARIANT_LOWER).s SRC_C += shared/runtime/gchelper_native.c @@ -232,7 +231,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) $(BUILD)/$(FATFS_DIR)/ff.o: COPT += -Os diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 83f80945f3e00..5d3c7d7515da5 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -552,8 +552,6 @@ async def build_circuitpython(): source_files.append(portdir / "common-hal/zephyr_kernel/__init__.c") # source_files.append(srcdir / "ports" / port / "peripherals" / "nrf" / "nrf52840" / "pins.c") - assembly_files.append(srcdir / "supervisor/shared/cpu_regs.S") - source_files.extend(assembly_files) objects = [] diff --git a/supervisor/shared/cpu_regs.S b/supervisor/shared/cpu_regs.S deleted file mode 100644 index 90e5367ed5808..0000000000000 --- a/supervisor/shared/cpu_regs.S +++ /dev/null @@ -1,102 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "supervisor/shared/cpu_regs.h" - -#ifdef __arm__ -.syntax unified -.thumb -.text -.align 2 - -@ uint cpu_get_regs_and_sp(r0=uint regs[SAVED_REGISTER_COUNT]) -.global cpu_get_regs_and_sp -.thumb -.thumb_func -.type cpu_get_regs_and_sp, %function -cpu_get_regs_and_sp: -#if __ARM_ARCH_ISA_THUMB == 2 -@ store registers into given array -#ifdef __arm__ -stmia r0!, {r4-r11} -#endif -#if defined(__aarch64__) && __aarch64__ == 1 -#error "aarch64 not supported" -stmia r0!, {x19-x28} -#endif -#ifdef __ARM_FP -#ifdef __arm__ -vstmia r0!, {s16-s31} -#endif -#if defined(__aarch64__) && __aarch64__ == 1 -vst1.64 {d8-d15}, [r0], #16 -#endif -#endif -#endif -// Thumb 1 can only store directly from R0-R7. This is M0 and M23 mostly. -#if __ARM_ARCH_ISA_THUMB == 1 -str r4, [r0, #0] -str r5, [r0, #4] -str r6, [r0, #8] -str r7, [r0, #12] -push {r1} -mov r1, r8 -str r1, [r0, #16] -mov r1, r9 -str r1, [r0, #20] -mov r1, r10 -str r1, [r0, #24] -mov r1, r11 -str r1, [r0, #28] -mov r1, r12 -str r1, [r0, #32] -mov r1, r13 -str r1, [r0, #36] -pop {r1} -#endif - -@ return the sp -mov r0, sp -bx lr -#endif - -#ifdef __riscv -#if __riscv_xlen == 32 -.global cpu_get_regs_and_sp -.type cpu_get_regs_and_sp, %function -cpu_get_regs_and_sp: -sw s0, 0(a0) -sw s1, 4(a0) -sw s2, 8(a0) -sw s3, 12(a0) -sw s4, 16(a0) -sw s5, 20(a0) -sw s6, 24(a0) -sw s7, 28(a0) -sw s8, 32(a0) -sw s9, 36(a0) -sw s10, 40(a0) -sw s11, 44(a0) -#ifdef __riscv_vector -sw fs0, 48(a0) -sw fs1, 52(a0) -sw fs2, 56(a0) -sw fs3, 60(a0) -sw fs4, 64(a0) -sw fs5, 68(a0) -sw fs6, 72(a0) -sw fs7, 76(a0) -sw fs8, 80(a0) -sw fs9, 84(a0) -sw fs10, 88(a0) -sw fs11, 92(a0) -#endif -move a0, sp -ret -#else -#error "Unsupported RISC-V bit length" -#endif -#endif diff --git a/supervisor/shared/cpu_regs.h b/supervisor/shared/cpu_regs.h deleted file mode 100644 index 8243c2388cd9b..0000000000000 --- a/supervisor/shared/cpu_regs.h +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#ifdef __arm__ -#define INTEGER_REGS 10 -#ifdef __ARM_FP -#define FLOATING_POINT_REGS 16 -#endif -#endif - -#ifdef __aarch64__ -#define INTEGER_REGS 10 -#ifdef __ARM_FP -#define FLOATING_POINT_REGS 8 -#endif -#endif - -#ifdef __riscv -#define INTEGER_REGS 12 -#ifdef __riscv_vector -#define FLOATING_POINT_REGS 12 -#endif -#endif - -#ifndef INTEGER_REGS -#define INTEGER_REGS 0 -#endif - -#ifndef FLOATING_POINT_REGS -#define FLOATING_POINT_REGS 0 -#endif - -#define SAVED_REGISTER_COUNT (INTEGER_REGS + FLOATING_POINT_REGS) From f8c06e39ed97a32b67e0816f53d62762d3b096f2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 4 Feb 2026 10:58:53 -0800 Subject: [PATCH 312/517] Fix board builds --- ports/espressif/boards/m5stack_cores3_se/board.c | 4 ++-- ports/stm/boards/meowbit_v121/mpconfigboard.mk | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/m5stack_cores3_se/board.c b/ports/espressif/boards/m5stack_cores3_se/board.c index cf06bd8488d86..bf5ccd17f36fe 100644 --- a/ports/espressif/boards/m5stack_cores3_se/board.c +++ b/ports/espressif/boards/m5stack_cores3_se/board.c @@ -46,8 +46,8 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO35, // DC - &pin_GPIO3, // CS + MP_OBJ_FROM_PTR(&pin_GPIO35), // DC + MP_OBJ_FROM_PTR(&pin_GPIO3), // CS NULL, // RST 40000000, // baudrate 0, // polarity diff --git a/ports/stm/boards/meowbit_v121/mpconfigboard.mk b/ports/stm/boards/meowbit_v121/mpconfigboard.mk index 74759abed5483..419f073b5ea2c 100644 --- a/ports/stm/boards/meowbit_v121/mpconfigboard.mk +++ b/ports/stm/boards/meowbit_v121/mpconfigboard.mk @@ -23,6 +23,7 @@ LD_FILE = boards/STM32F401xe_boot.ld # LD_FILE = boards/STM32F401xe_fs.ld CIRCUITPY_AESIO = 0 +CIRCUITPY_CODEOP = 0 CIRCUITPY_BITMAPFILTER = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_BLEIO_HCI = 0 @@ -30,6 +31,7 @@ CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_I2CDISPLAYBUS = 0 CIRCUITPY_KEYPAD_DEMUX = 0 +CIRCUITPY_PIXELMAP = 0 CIRCUITPY_SHARPDISPLAY = 0 CIRCUITPY_TILEPALETTEMAPPER = 0 CIRCUITPY_ULAB = 0 From b975d1da814ef32d8ee9b89ef7015e82e403042b Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 5 Feb 2026 21:44:55 -0500 Subject: [PATCH 313/517] py/nlrthumb.c: restore r1,r2,r3 clobbers to nlr_push() --- ports/atmel-samd/peripherals | 2 +- py/nlrthumb.c | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ports/atmel-samd/peripherals b/ports/atmel-samd/peripherals index 863e615ff98c3..da0a1d7dccb34 160000 --- a/ports/atmel-samd/peripherals +++ b/ports/atmel-samd/peripherals @@ -1 +1 @@ -Subproject commit 863e615ff98c3a8aa904e87a157553559c933b81 +Subproject commit da0a1d7dccb34b7fa07738e7a8ce25118c88d1a3 diff --git a/py/nlrthumb.c b/py/nlrthumb.c index 0aa1f053a2397..265052568ffe4 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -36,8 +36,7 @@ // For reference, arm/thumb callee save regs are: // r4-r11, r13=sp -// CIRCUITPY-CHANGE: added returns_twice -__attribute__((naked, returns_twice)) unsigned int nlr_push(nlr_buf_t *nlr) { +__attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { // If you get a linker error here, indicating that a relocation doesn't // fit, try the following (in that order): @@ -92,6 +91,10 @@ __attribute__((naked, returns_twice)) unsigned int nlr_push(nlr_buf_t *nlr) { "b nlr_push_tail \n" // do the rest in C #endif #endif + // CIRCUITPY-CHANGE: add input and clobbers to prevent smashing registers. + : // output operands + : "r" (nlr) // input operands + : "r1", "r2", "r3" // clobbers ); #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) @@ -141,7 +144,8 @@ NORETURN void nlr_jump(void *val) { "bx lr \n" // return : // output operands : "r" (top) // input operands - : "memory" // clobbered registers + // CIRCUITPY-CHANGE: better comment + : "memory" // clobbers ); MP_UNREACHABLE From 8df888eb29adbc49ea0b37b4206603829d3446b6 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 7 Feb 2026 18:28:59 -0500 Subject: [PATCH 314/517] prevent recursive display refresh --- shared-module/busdisplay/BusDisplay.c | 5 ++++- shared-module/epaperdisplay/EPaperDisplay.c | 6 +++++- shared-module/framebufferio/FramebufferDisplay.c | 5 ++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index a4652324c1e4f..46f3aaeaa0abc 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -309,11 +309,14 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } static void _refresh_display(busdisplay_busdisplay_obj_t *self) { + if (!displayio_display_core_start_refresh(&self->core)) { + // Refresh for this display already in progress. + return; + } if (!displayio_display_bus_is_free(&self->bus)) { // A refresh on this bus is already in progress. Try next display. return; } - displayio_display_core_start_refresh(&self->core); const displayio_area_t *current_area = _get_refresh_areas(self); while (current_area != NULL) { _refresh_area(self, current_area); diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index 556db6ae22002..5d92862fced2a 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -187,6 +187,11 @@ void epaperdisplay_epaperdisplay_change_refresh_mode_parameters(epaperdisplay_ep } static void epaperdisplay_epaperdisplay_start_refresh(epaperdisplay_epaperdisplay_obj_t *self) { + if (!displayio_display_core_start_refresh(&self->core)) { + // Refresh on this display already in progress. + return; + } + if (!displayio_display_bus_is_free(&self->bus)) { // Can't acquire display bus; skip updating this display. Try next display. return; @@ -201,7 +206,6 @@ static void epaperdisplay_epaperdisplay_start_refresh(epaperdisplay_epaperdispla if (mp_hal_is_interrupted()) { return; } - displayio_display_core_start_refresh(&self->core); } uint32_t common_hal_epaperdisplay_epaperdisplay_get_time_to_refresh(epaperdisplay_epaperdisplay_obj_t *self) { diff --git a/shared-module/framebufferio/FramebufferDisplay.c b/shared-module/framebufferio/FramebufferDisplay.c index 4ba2b1325815c..8116f4b0347cb 100644 --- a/shared-module/framebufferio/FramebufferDisplay.c +++ b/shared-module/framebufferio/FramebufferDisplay.c @@ -217,7 +217,10 @@ static void _refresh_display(framebufferio_framebufferdisplay_obj_t *self) { if (!self->bufinfo.buf) { return; } - displayio_display_core_start_refresh(&self->core); + if (!displayio_display_core_start_refresh(&self->core)) { + // Refresh on this display already in progress. + return; + } const displayio_area_t *current_area = _get_refresh_areas(self); if (current_area) { bool transposed = (self->core.rotation == 90 || self->core.rotation == 270); From 65151c2ca182cc8c5a102df3619def5ee35cc5c3 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 8 Feb 2026 15:21:50 -0500 Subject: [PATCH 315/517] Apply suggestions from code review --- ports/espressif/common-hal/_bleio/PacketBuffer.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ports/espressif/common-hal/_bleio/PacketBuffer.c b/ports/espressif/common-hal/_bleio/PacketBuffer.c index 00aaae44e38ec..db035157ceb6b 100644 --- a/ports/espressif/common-hal/_bleio/PacketBuffer.c +++ b/ports/espressif/common-hal/_bleio/PacketBuffer.c @@ -159,13 +159,7 @@ static int packet_buffer_on_ble_client_evt(struct ble_gap_event *event, void *pa return false; } -#if CIRCUITPY_SERIAL_BLE || CIRCUITPY_BLE_FILE_SERVICE -// Exposed via shared-bindings header when enabled void _common_hal_bleio_packet_buffer_construct( -#else -// Internal static helper when disabled (avoids "missing prototype" error) -static void _common_hal_bleio_packet_buffer_construct( -#endif bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, uint32_t *incoming_buffer, size_t incoming_buffer_size, uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, From ad580f4d9a30963f5747f41873740737fcc7ec93 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Mon, 9 Feb 2026 13:22:13 +0100 Subject: [PATCH 316/517] Bump circuitpython-stage to 1.4.0 This fixes the FourWire and BusDisplay imports for pybadge and pygamer. --- frozen/circuitpython-stage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frozen/circuitpython-stage b/frozen/circuitpython-stage index ccac175f5e9c8..7b71363acb67c 160000 --- a/frozen/circuitpython-stage +++ b/frozen/circuitpython-stage @@ -1 +1 @@ -Subproject commit ccac175f5e9c8392d436058d4364a8ca5e58abc7 +Subproject commit 7b71363acb67cb1b254b12607f35280cfb4ad6c1 From bcb9cd1f78be1508ef46967dba55cbfe3d6e29fd Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 9 Feb 2026 15:09:06 -0500 Subject: [PATCH 317/517] don't start refresh until we know bus is available --- shared-module/busdisplay/BusDisplay.c | 9 +++++---- shared-module/epaperdisplay/EPaperDisplay.c | 9 ++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index 46f3aaeaa0abc..6ef1c4c7f66ce 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -309,14 +309,15 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } static void _refresh_display(busdisplay_busdisplay_obj_t *self) { - if (!displayio_display_core_start_refresh(&self->core)) { - // Refresh for this display already in progress. - return; - } if (!displayio_display_bus_is_free(&self->bus)) { // A refresh on this bus is already in progress. Try next display. return; } + if (!displayio_display_core_start_refresh(&self->core)) { + // Refresh for this display already in progress. + return; + } + const displayio_area_t *current_area = _get_refresh_areas(self); while (current_area != NULL) { _refresh_area(self, current_area); diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index 5d92862fced2a..655ae10307155 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -187,15 +187,14 @@ void epaperdisplay_epaperdisplay_change_refresh_mode_parameters(epaperdisplay_ep } static void epaperdisplay_epaperdisplay_start_refresh(epaperdisplay_epaperdisplay_obj_t *self) { - if (!displayio_display_core_start_refresh(&self->core)) { - // Refresh on this display already in progress. - return; - } - if (!displayio_display_bus_is_free(&self->bus)) { // Can't acquire display bus; skip updating this display. Try next display. return; } + if (!displayio_display_core_start_refresh(&self->core)) { + // Refresh on this display already in progress. + return; + } // run start sequence self->bus.bus_reset(self->bus.bus); From 4599aba309edc95ee7a618163e0a71cb3364e5f1 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 9 Feb 2026 16:52:12 -0500 Subject: [PATCH 318/517] update frozen libraries --- frozen/Adafruit_CircuitPython_Bitmap_Font | 2 +- frozen/Adafruit_CircuitPython_BusDevice | 2 +- frozen/Adafruit_CircuitPython_NeoPixel | 2 +- frozen/Adafruit_CircuitPython_PortalBase | 2 +- frozen/Adafruit_CircuitPython_Wiznet5k | 2 +- frozen/Adafruit_CircuitPython_seesaw | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_Bitmap_Font b/frozen/Adafruit_CircuitPython_Bitmap_Font index 13c6a39a58e28..cb6a08e2ca57d 160000 --- a/frozen/Adafruit_CircuitPython_Bitmap_Font +++ b/frozen/Adafruit_CircuitPython_Bitmap_Font @@ -1 +1 @@ -Subproject commit 13c6a39a58e28030f3651a90e116c1ab30f2035b +Subproject commit cb6a08e2ca57df75b512b844cce78558088979fe diff --git a/frozen/Adafruit_CircuitPython_BusDevice b/frozen/Adafruit_CircuitPython_BusDevice index baa6bcafa2251..69ebda79d40d0 160000 --- a/frozen/Adafruit_CircuitPython_BusDevice +++ b/frozen/Adafruit_CircuitPython_BusDevice @@ -1 +1 @@ -Subproject commit baa6bcafa22512ac56f343c7d124f3b029861c33 +Subproject commit 69ebda79d40d0e74c01e4f1aa293f219b8c7e086 diff --git a/frozen/Adafruit_CircuitPython_NeoPixel b/frozen/Adafruit_CircuitPython_NeoPixel index 1a9523574b68c..5b8fe0e70646d 160000 --- a/frozen/Adafruit_CircuitPython_NeoPixel +++ b/frozen/Adafruit_CircuitPython_NeoPixel @@ -1 +1 @@ -Subproject commit 1a9523574b68cc205c151aaf080909348df7417c +Subproject commit 5b8fe0e70646d55bfa75a14cbc5136b9328be850 diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase index 059c805bd1f91..067b1b80dfd42 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit 059c805bd1f9157495d17739c8082f9a2042bdbf +Subproject commit 067b1b80dfd42fe1b62416eef2790b7d99d46206 diff --git a/frozen/Adafruit_CircuitPython_Wiznet5k b/frozen/Adafruit_CircuitPython_Wiznet5k index ea7b97c5b1faa..fa2e95d61fb5a 160000 --- a/frozen/Adafruit_CircuitPython_Wiznet5k +++ b/frozen/Adafruit_CircuitPython_Wiznet5k @@ -1 +1 @@ -Subproject commit ea7b97c5b1faa2db4b54d7f6d9a61b545b49f383 +Subproject commit fa2e95d61fb5a90b8bd59234b56d1acbfe9fd995 diff --git a/frozen/Adafruit_CircuitPython_seesaw b/frozen/Adafruit_CircuitPython_seesaw index d01642926b80a..5062810482484 160000 --- a/frozen/Adafruit_CircuitPython_seesaw +++ b/frozen/Adafruit_CircuitPython_seesaw @@ -1 +1 @@ -Subproject commit d01642926b80a46762f1dd76dcdd5aefb0d95bc8 +Subproject commit 50628104824849fbdac093c8f18367623edff1c4 From ecdb5db1c968e72af50fab3636c1b8318310f388 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 10 Feb 2026 12:12:29 -0500 Subject: [PATCH 319/517] remove guard meant to be removed in #10755 --- shared-bindings/_bleio/PacketBuffer.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shared-bindings/_bleio/PacketBuffer.h b/shared-bindings/_bleio/PacketBuffer.h index 1a872512da27c..dd0c1ae9e2548 100644 --- a/shared-bindings/_bleio/PacketBuffer.h +++ b/shared-bindings/_bleio/PacketBuffer.h @@ -21,13 +21,11 @@ void common_hal_bleio_packet_buffer_construct( bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, size_t buffer_size, size_t max_packet_size); // Allocation free version for BLE workflow use. -#if CIRCUITPY_SERIAL_BLE || CIRCUITPY_BLE_FILE_SERVICE void _common_hal_bleio_packet_buffer_construct( bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, uint32_t *incoming_buffer, size_t incoming_buffer_size, - uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t outgoing_buffer_size, + uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, ble_event_handler_t *static_handler_entry); -#endif mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len); mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len); mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length(bleio_packet_buffer_obj_t *self); From f65029ed185a9037b46207abbd9a4a3c92852421 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 10 Feb 2026 19:09:20 +0100 Subject: [PATCH 320/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 8 ++++---- locale/el.po | 8 ++++---- locale/hi.po | 8 ++++---- locale/ko.po | 13 ++++++++----- locale/ru.po | 11 +++++++---- locale/tr.po | 8 ++++---- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index b7964d4ff1383..ec160f6bdd39e 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -1679,10 +1679,6 @@ msgstr "Nepřipojený" msgid "Not playing" msgstr "Nehraje" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -2412,6 +2408,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "Nepodporovaný barevný prostor" diff --git a/locale/el.po b/locale/el.po index b40162b35b783..7b1a182690ed9 100644 --- a/locale/el.po +++ b/locale/el.po @@ -1685,10 +1685,6 @@ msgstr "" msgid "Not playing" msgstr "" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -2416,6 +2412,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index 08b91c9bad6f4..c11116b34f690 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -1661,10 +1661,6 @@ msgstr "" msgid "Not playing" msgstr "" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -2390,6 +2386,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 7963a7a9822d6..99c62684dc603 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -1719,11 +1719,6 @@ msgstr "연결되지 않았습니다" msgid "Not playing" msgstr "재생되지 않았습니다" -#: shared-module/jpegio/JpegDecoder.c -#, fuzzy -msgid "Not supported JPEG standard" -msgstr "지원되지 않는 JPEG 표준" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -2465,6 +2460,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" @@ -4663,6 +4662,10 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, fuzzy +#~ msgid "Not supported JPEG standard" +#~ msgstr "지원되지 않는 JPEG 표준" + #, fuzzy #~ msgid "%q moved from %q to %q" #~ msgstr "%q가 %q에서 %q로 이동했습니다" diff --git a/locale/ru.po b/locale/ru.po index 45ecfb19f0faf..cf83523e82c71 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -1702,10 +1702,6 @@ msgstr "Не подключено" msgid "Not playing" msgstr "Не воспроизводится (Not playing)" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "Не поддерживается Стандарт JPEG" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -2449,6 +2445,10 @@ msgstr "" "Неуказанная проблема. Возможно, запрос на сопряжение на другом устройстве " "был отклонен или проигнорирован." +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "Неподдерживаемое цветовое пространство" @@ -4681,6 +4681,9 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" +#~ msgid "Not supported JPEG standard" +#~ msgstr "Не поддерживается Стандарт JPEG" + #~ msgid "%q moved from %q to %q" #~ msgstr "%q переместился из %q в %q" diff --git a/locale/tr.po b/locale/tr.po index 7951c91c4bf58..f8db30f3893cc 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -1680,10 +1680,6 @@ msgstr "" msgid "Not playing" msgstr "" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -2412,6 +2408,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" From 5a1b57cf62d59b3bbbe657f810d5042f3d1459ca Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 10 Feb 2026 13:45:09 -0500 Subject: [PATCH 321/517] fix silab compile issues --- ports/silabs/common-hal/_bleio/PacketBuffer.c | 2 +- ports/silabs/common-hal/microcontroller/__init__.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/silabs/common-hal/_bleio/PacketBuffer.c b/ports/silabs/common-hal/_bleio/PacketBuffer.c index dec833fe62f85..881cf1622eba7 100644 --- a/ports/silabs/common-hal/_bleio/PacketBuffer.c +++ b/ports/silabs/common-hal/_bleio/PacketBuffer.c @@ -145,7 +145,7 @@ void _common_hal_bleio_packet_buffer_construct( uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, - ble_event_handler_t static_handler_entry) { + ble_event_handler_t *static_handler_entry) { bleio_characteristic_properties_t temp_prop; self->characteristic = characteristic; diff --git a/ports/silabs/common-hal/microcontroller/__init__.c b/ports/silabs/common-hal/microcontroller/__init__.c index b1472fcf43725..7244af6619dd6 100644 --- a/ports/silabs/common-hal/microcontroller/__init__.c +++ b/ports/silabs/common-hal/microcontroller/__init__.c @@ -60,6 +60,9 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { } } +// CHIP_Reset does not return, but is not declared as such in the SDK. +void __attribute__ ((noreturn)) CHIP_Reset(void); + void common_hal_mcu_reset(void) { filesystem_flush(); // TODO: implement as part of flash improvements CHIP_Reset(); From 0b3b90ae16603c22b18e7d88fbe37fd8d2a687a8 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 10 Feb 2026 18:48:26 -0500 Subject: [PATCH 322/517] ble_hci PacketBuffer.h: need a def for ble_event_handler_t --- devices/ble_hci/common-hal/_bleio/PacketBuffer.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/devices/ble_hci/common-hal/_bleio/PacketBuffer.h b/devices/ble_hci/common-hal/_bleio/PacketBuffer.h index 563f365d21b01..4e001339fe323 100644 --- a/devices/ble_hci/common-hal/_bleio/PacketBuffer.h +++ b/devices/ble_hci/common-hal/_bleio/PacketBuffer.h @@ -28,4 +28,7 @@ typedef struct { bool packet_queued; } bleio_packet_buffer_obj_t; +// Unused, but needed for _common_hal_bleio_packet_buffer_construct() +typedef void *ble_event_handler_t; + void bleio_packet_buffer_update(bleio_packet_buffer_obj_t *self, mp_buffer_info_t *bufinfo); From 13de16c14ae0511bdad2617030184171930543dc Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Thu, 12 Feb 2026 11:53:40 -0600 Subject: [PATCH 323/517] Update USB vid/pid codes --- ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk | 4 ++-- .../boards/tinycircuits_thumby_color/mpconfigboard.mk | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk index 44dc0d671001b..d8140e6cb0a13 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk +++ b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk @@ -1,5 +1,5 @@ -USB_VID = 0x1D6B -USB_PID = 0x0003 +USB_VID = 0x1209 +USB_PID = 0x3500 USB_PRODUCT = "Thumby" USB_MANUFACTURER = "TinyCircuits" diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk index ed1255e2b96ed..828d4a638d5ab 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk @@ -1,5 +1,5 @@ -USB_VID = 0x1D6B -USB_PID = 0x0003 +USB_VID = 0x1209 +USB_PID = 0x3501 USB_PRODUCT = "Thumby Color" USB_MANUFACTURER = "TinyCircuits" From a2ab5536c857ea401058c609bf0f4d2f24f4fc86 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Thu, 12 Feb 2026 19:37:37 +0100 Subject: [PATCH 324/517] Add board definition for uGame S3 uGame S3 is a handheld game console with an ESP32-S3 chip. More information at https://deshipu.art/projects/project-178061/ --- .../espressif/boards/deshipu_ugame_s3/board.c | 123 ++++++++++++++++++ .../boards/deshipu_ugame_s3/mpconfigboard.h | 28 ++++ .../boards/deshipu_ugame_s3/mpconfigboard.mk | 27 ++++ .../espressif/boards/deshipu_ugame_s3/pins.c | 43 ++++++ .../boards/deshipu_ugame_s3/sdkconfig | 22 ++++ 5 files changed, 243 insertions(+) create mode 100644 ports/espressif/boards/deshipu_ugame_s3/board.c create mode 100644 ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.h create mode 100644 ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.mk create mode 100644 ports/espressif/boards/deshipu_ugame_s3/pins.c create mode 100644 ports/espressif/boards/deshipu_ugame_s3/sdkconfig diff --git a/ports/espressif/boards/deshipu_ugame_s3/board.c b/ports/espressif/boards/deshipu_ugame_s3/board.c new file mode 100644 index 0000000000000..677cab7f07655 --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/board.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * 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 "supervisor/board.h" +#include "mpconfigboard.h" + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + +#include "esp_log.h" +#include "esp_err.h" + +fourwire_fourwire_obj_t board_display_obj; + +#define DELAY 0x80 + +uint8_t display_init_sequence[] = { + 0x01, 0 | DELAY, 0x80, // Software reset then delay 0x80 (128ms) + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + 0xc0, 1, 0x23, // Power control VRH[5:0] + 0xc1, 1, 0x10, // Power control SAP[2:0];BT[3:0] + 0xc5, 2, 0x3e, 0x28, // VCM control + 0xc7, 1, 0x86, // VCM control2 + 0x37, 1, 0x00, // Vertical scroll zero + 0x3a, 1, 0x55, // COLMOD: Pixel Format Set + 0xb1, 2, 0x00, 0x18, // Frame Rate Control (In Normal Mode/Full Colors) + 0xb6, 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + 0x26, 1, 0x01, // Gamma curve selected + 0xe0, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma + 0xe1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma + 0x11, 0 | DELAY, 0x78, // Exit Sleep then delay 0x78 (120ms) + 0x29, 0 | DELAY, 0x78, // Display on then delay 0x78 (120ms) + 0x36, 1, 0x38, +}; + + +void board_init(void) { + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct(spi, &pin_GPIO12, &pin_GPIO11, NULL, false); + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + common_hal_fourwire_fourwire_construct(bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO10), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO13), // TFT_RESET Reset + 80000000L, // Baudrate + 0, // Polarity + 0); // Phase + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 320, // Width (after rotation) + 240, // Height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // Color depth + false, // Grayscale + false, // Pixels in a byte share a row. Only used for depth < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // Set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // Set row command + MIPI_COMMAND_WRITE_MEMORY_START, // Write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO21, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 20, // native_frames_per_second + true, // backlight_on_high + false, // not SH1107 + 50000); // backlight pwm frequency +} + +void board_deinit(void) { +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.h b/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.h new file mode 100644 index 0000000000000..da7c3b4e8a43c --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.h @@ -0,0 +1,28 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define MICROPY_HW_BOARD_NAME "uGame S3" +#define MICROPY_HW_MCU_NAME "ESP32S3" diff --git a/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.mk b/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.mk new file mode 100644 index 0000000000000..36a0ce040c509 --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.mk @@ -0,0 +1,27 @@ +USB_VID = 0x1209 +USB_PID = 0xD187 +USB_PRODUCT = "uGameS3" +USB_MANUFACTURER = "deshipu" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +CIRCUITPY_STAGE = 1 +CIRCUITPY_KEYPAD = 1 + +CIRCUITPY_CANIO = 0 +CIRCUITPY_DUALBANK = 0 +CIRCUITPY_ESPCAMERA = 0 +CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_PARALLELDISPLAYBUS = 0 +CIRCUITPY_RGBMATRIX = 0 +CIRCUITPY_ROTARYIO = 0 + +FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/ugame_s3 diff --git a/ports/espressif/boards/deshipu_ugame_s3/pins.c b/ports/espressif/boards/deshipu_ugame_s3/pins.c new file mode 100644 index 0000000000000..54bd5dcdb4f0d --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/pins.c @@ -0,0 +1,43 @@ +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_P1), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_P2), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_P3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_P4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_P5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_P6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_P7), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_BUTTON_LEFT), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_UP), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_RIGHT), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_DOWN), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_O), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_Z), MP_ROM_PTR(&pin_GPIO47) }, + + { MP_ROM_QSTR(MP_QSTR_LIGHT), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_BATTERY), MP_ROM_PTR(&pin_GPIO8) }, + + + { MP_ROM_QSTR(MP_QSTR_AUDIO_BCLK), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_AUDIO_LRCLK), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_AUDIO_DATA), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_AUDIO_GAIN), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_TFT_BACKLIGHT), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_TFT_SCK), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_TFT_MOSI), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/deshipu_ugame_s3/sdkconfig b/ports/espressif/boards/deshipu_ugame_s3/sdkconfig new file mode 100644 index 0000000000000..1bddb7a89fbb7 --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/sdkconfig @@ -0,0 +1,22 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +CONFIG_LWIP_LOCAL_HOSTNAME="espressif-esp32s3" +# end of LWIP + +# +# Camera configuration +# +# CONFIG_OV7725_SUPPORT is not set +# CONFIG_OV3660_SUPPORT is not set +# end of Camera configuration + +# end of Component config + +# end of Espressif IoT Development Framework Configuration From 8142afc6a2004c75478b0f6a100664633710878a Mon Sep 17 00:00:00 2001 From: Arif Budiman Date: Thu, 12 Feb 2026 15:15:35 +0100 Subject: [PATCH 325/517] Translated using Weblate (Indonesian) Currently translated at 100.0% (1012 of 1012 strings) Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/id/ --- locale/ID.po | 1113 +++++++++++++++++++++++++------------------------- 1 file changed, 563 insertions(+), 550 deletions(-) diff --git a/locale/ID.po b/locale/ID.po index ee7b8dfc4a3e1..ca862a9714768 100644 --- a/locale/ID.po +++ b/locale/ID.po @@ -6,15 +6,15 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-06-30 16:01+0000\n" -"Last-Translator: MAE \n" +"PO-Revision-Date: 2026-02-13 15:09+0000\n" +"Last-Translator: Arif Budiman \n" "Language-Team: LANGUAGE \n" "Language: ID\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.13-dev\n" +"X-Generator: Weblate 5.16-dev\n" #: main.c msgid "" @@ -116,7 +116,7 @@ msgstr "%q gagal: %d" #: shared-module/audiodelays/MultiTapDelay.c msgid "%q in %q must be of type %q or %q, not %q" -msgstr "" +msgstr "%q dalam %q harus bertipe %q atau %q, bukan %q" #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" @@ -289,7 +289,7 @@ msgstr "%q, %q, dan %q semuanya harus memiliki panjang yang sama" #: py/objint.c shared-bindings/_bleio/Connection.c #: shared-bindings/storage/__init__.c msgid "%q=%q" -msgstr "" +msgstr "%q=%q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "%q[%u] shifts in more bits than pin count" @@ -566,7 +566,7 @@ msgstr "Sudah disebarkan." #: ports/atmel-samd/common-hal/canio/Listener.c msgid "Already have all-matches listener" -msgstr "" +msgstr "Sudah memiliki listener semua-kecocokan" #: ports/espressif/common-hal/_bleio/__init__.c msgid "Already in progress" @@ -610,7 +610,7 @@ msgstr "Nilai array harus berupa byte tunggal." #: ports/atmel-samd/common-hal/spitarget/SPITarget.c msgid "Async SPI transfer in progress on this bus, keep awaiting." -msgstr "" +msgstr "Transfer SPI async sedang berlangsung pada bus ini, terus menunggu." #: shared-module/memorymonitor/AllocationAlarm.c #, c-format @@ -624,7 +624,7 @@ msgstr "Konversi audio belum diimplementasikan" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c msgid "Audio source error" -msgstr "" +msgstr "Kesalahan sumber audio" #: shared-bindings/wifi/Radio.c msgid "AuthMode.OPEN is not used with password" @@ -767,7 +767,7 @@ msgstr "" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Can't construct AudioOut because continuous channel already open" -msgstr "" +msgstr "Tidak dapat membuat AudioOut karena kanal berkelanjutan sudah terbuka" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c @@ -817,7 +817,7 @@ msgstr "Tidak dapat merekam ke file" #: shared-module/storage/__init__.c msgid "Cannot remount path when visible via USB." -msgstr "" +msgstr "Tidak dapat me-remount jalur ketika terlihat melalui USB." #: shared-bindings/digitalio/DigitalInOut.c msgid "Cannot set value when direction is input." @@ -834,7 +834,7 @@ msgstr "Tidak dapat membuat subkelas dari irisan" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Cannot use GPIO0..15 together with GPIO32..47" -msgstr "" +msgstr "Tidak dapat menggunakan GPIO0..15 bersamaan dengan GPIO32..47" #: ports/nordic/common-hal/alarm/pin/PinAlarm.c msgid "Cannot wake on pin edge, only level" @@ -873,7 +873,7 @@ msgstr "Tipe array koordinat memiliki ukuran yang berbeda" #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" -msgstr "" +msgstr "Tidak dapat mempublikasikan ke topik ROS" #: shared-bindings/_bleio/Adapter.c msgid "Could not set address" @@ -890,7 +890,7 @@ msgstr "Tidak dapat mengalokasikan dekoder" #: ports/espressif/common-hal/rclcpy/__init__.c #, c-format msgid "Critical ROS failure during soft reboot, reset required: %d" -msgstr "" +msgstr "Kegagalan ROS kritis selama soft reboot, reset diperlukan: %d" #: ports/stm/common-hal/analogio/AnalogOut.c msgid "DAC Channel Init Error" @@ -1009,28 +1009,30 @@ msgstr "Gagal memperoleh mutex, err 0x%04x" #: ports/raspberrypi/common-hal/mdns/Server.c msgid "Failed to add service TXT record" -msgstr "" +msgstr "Gagal menambahkan record TXT layanan" #: shared-bindings/mdns/Server.c msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" +"Gagal menambahkan record TXT layanan; ditemukan non-string atau bytes dalam " +"txt_records" #: shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" -msgstr "" +msgstr "Gagal mengalokasikan buffer %q" #: ports/espressif/common-hal/wifi/__init__.c msgid "Failed to allocate Wifi memory" -msgstr "" +msgstr "Gagal mengalokasikan memori Wifi" #: ports/espressif/common-hal/wifi/ScannedNetworks.c msgid "Failed to allocate wifi scan memory" -msgstr "" +msgstr "Gagal mengalokasikan memori pemindaian wifi" #: ports/stm/common-hal/audiopwmio/PWMAudioOut.c msgid "Failed to buffer the sample" -msgstr "" +msgstr "Gagal membuffer sampel" #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c @@ -1043,23 +1045,23 @@ msgstr "Gagal terhubung: habis waktu" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to create continuous channels: invalid arg" -msgstr "" +msgstr "Gagal membuat kanal berkelanjutan: argumen tidak valid" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to create continuous channels: invalid state" -msgstr "" +msgstr "Gagal membuat kanal berkelanjutan: status tidak valid" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to create continuous channels: no mem" -msgstr "" +msgstr "Gagal membuat kanal berkelanjutan: tidak ada memori" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to create continuous channels: not found" -msgstr "" +msgstr "Gagal membuat kanal berkelanjutan: tidak ditemukan" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to enable continuous" -msgstr "" +msgstr "Gagal mengaktifkan berkelanjutan" #: shared-module/audiomp3/MP3Decoder.c msgid "Failed to parse MP3 file" @@ -1067,7 +1069,7 @@ msgstr "Gagal mengurai file MP3" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to register continuous events callback" -msgstr "" +msgstr "Gagal mendaftarkan callback event berkelanjutan" #: ports/nordic/sd_mutex.c #, c-format @@ -1076,11 +1078,11 @@ msgstr "Gagal melepaskan mutex, err 0x%04x" #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" -msgstr "" +msgstr "Gagal menetapkan hostname" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to start async audio" -msgstr "" +msgstr "Gagal memulai audio async" #: supervisor/shared/safe_mode.c msgid "Failed to write internal flash." @@ -1093,43 +1095,44 @@ msgstr "File sudah ada" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c #: shared-module/os/getenv.c msgid "File not found" -msgstr "" +msgstr "File tidak ditemukan" #: ports/atmel-samd/common-hal/canio/Listener.c #: ports/espressif/common-hal/canio/Listener.c #: ports/mimxrt10xx/common-hal/canio/Listener.c #: ports/stm/common-hal/canio/Listener.c msgid "Filters too complex" -msgstr "" +msgstr "Filter terlalu kompleks" #: ports/espressif/common-hal/dualbank/__init__.c msgid "Firmware is duplicate" -msgstr "" +msgstr "Firmware adalah duplikat" #: ports/espressif/common-hal/dualbank/__init__.c msgid "Firmware is invalid" -msgstr "" +msgstr "Firmware tidak valid" #: ports/espressif/common-hal/dualbank/__init__.c msgid "Firmware is too big" -msgstr "" +msgstr "Firmware terlalu besar" #: shared-bindings/bitmaptools/__init__.c msgid "For L8 colorspace, input bitmap must have 8 bits per pixel" -msgstr "" +msgstr "Untuk colorspace L8, bitmap input harus memiliki 8 bit per piksel" #: shared-bindings/bitmaptools/__init__.c msgid "For RGB colorspaces, input bitmap must have 16 bits per pixel" -msgstr "" +msgstr "Untuk colorspace RGB, bitmap input harus memiliki 16 bit per piksel" #: ports/cxd56/common-hal/camera/Camera.c shared-module/audiocore/WaveFile.c msgid "Format not supported" -msgstr "" +msgstr "Format tidak didukung" #: ports/mimxrt10xx/common-hal/microcontroller/Processor.c msgid "" "Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" msgstr "" +"Frekuensi harus 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 atau 1008 Mhz" #: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c #: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c @@ -1138,11 +1141,11 @@ msgstr "Fungsinya membutuhkan kunci" #: ports/cxd56/common-hal/gnss/GNSS.c msgid "GNSS init" -msgstr "" +msgstr "Inisialisasi GNSS" #: ports/espressif/common-hal/espidf/__init__.c msgid "Generic Failure" -msgstr "" +msgstr "Kegagalan Umum" #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c @@ -1152,7 +1155,7 @@ msgstr "Grup sudah digunakan" #: supervisor/shared/safe_mode.c msgid "Hard fault: memory access or instruction error." -msgstr "" +msgstr "Hard fault: kesalahan akses memori atau instruksi." #: ports/mimxrt10xx/common-hal/busio/SPI.c #: ports/mimxrt10xx/common-hal/busio/UART.c ports/stm/common-hal/busio/I2C.c @@ -1163,7 +1166,7 @@ msgstr "Perangkat keras sedang digunakan, coba pin alternatif" #: supervisor/shared/safe_mode.c msgid "Heap allocation when VM not running." -msgstr "" +msgstr "Alokasi heap ketika VM tidak berjalan." #: extmod/vfs_posix_file.c py/objstringio.c msgid "I/O operation on closed file" @@ -1171,16 +1174,16 @@ msgstr "operasi I/O pada file tertutup" #: ports/stm/common-hal/busio/I2C.c msgid "I2C init error" -msgstr "" +msgstr "Kesalahan inisialisasi I2C" #: ports/raspberrypi/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c msgid "I2C peripheral in use" -msgstr "" +msgstr "Periferal I2C sedang digunakan" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "In-buffer elements must be <= 4 bytes long" -msgstr "" +msgstr "Elemen in-buffer harus <= 4 byte panjangnya" #: shared-bindings/_pew/PewPew.c msgid "Incorrect buffer size" @@ -1188,24 +1191,25 @@ msgstr "Ukuran penyangga salah" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Init program size invalid" -msgstr "" +msgstr "Ukuran program inisialisasi tidak valid" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Initial set pin direction conflicts with initial out pin direction" -msgstr "" +msgstr "Arah pin set awal berkonflik dengan arah pin out awal" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Initial set pin state conflicts with initial out pin state" -msgstr "" +msgstr "Status pin set awal berkonflik dengan status pin out awal" #: shared-bindings/bitops/__init__.c #, c-format msgid "Input buffer length (%d) must be a multiple of the strand count (%d)" msgstr "" +"Panjang buffer input (%d) harus merupakan kelipatan dari jumlah strand (%d)" #: ports/atmel-samd/common-hal/pulseio/PulseIn.c msgid "Input taking too long" -msgstr "" +msgstr "Input memakan waktu terlalu lama" #: py/moderrno.c msgid "Input/output error" @@ -1223,20 +1227,20 @@ msgstr "Enkripsi tidak cukup" #: shared-module/jpegio/JpegDecoder.c msgid "Insufficient memory pool for the image" -msgstr "" +msgstr "Pool memori tidak mencukupi untuk gambar" #: shared-module/jpegio/JpegDecoder.c msgid "Insufficient stream input buffer" -msgstr "" +msgstr "Buffer input stream tidak mencukupi" #: ports/espressif/common-hal/wifi/Radio.c #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Interface must be started" -msgstr "" +msgstr "Interface harus dimulai" #: ports/atmel-samd/audio_dma.c ports/raspberrypi/audio_dma.c msgid "Internal audio buffer too small" -msgstr "" +msgstr "Buffer audio internal terlalu kecil" #: ports/stm/common-hal/busio/UART.c msgid "Internal define error" @@ -1244,7 +1248,7 @@ msgstr "Kesalahan definisi internal" #: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c msgid "Internal error" -msgstr "" +msgstr "Kesalahan internal" #: shared-module/rgbmatrix/RGBMatrix.c #, c-format @@ -1262,19 +1266,19 @@ msgstr "Kesalahan internal #%d" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-bindings/pwmio/PWMOut.c msgid "Internal resource(s) in use" -msgstr "" +msgstr "Sumber daya internal sedang digunakan" #: supervisor/shared/safe_mode.c msgid "Internal watchdog timer expired." -msgstr "" +msgstr "Timer watchdog internal kedaluwarsa." #: supervisor/shared/safe_mode.c msgid "Interrupt error." -msgstr "" +msgstr "Kesalahan interrupt." #: shared-module/jpegio/JpegDecoder.c msgid "Interrupted by output function" -msgstr "" +msgstr "Diinterupsi oleh fungsi output" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c @@ -1290,12 +1294,12 @@ msgstr "" #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" -msgstr "" +msgstr "%q tidak valid" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" -msgstr "" +msgstr "%q dan %q tidak valid" #: ports/atmel-samd/common-hal/microcontroller/Pin.c #: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c @@ -1311,19 +1315,19 @@ msgstr "Nilai Unit ADC tidak valid" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid BLE parameter" -msgstr "" +msgstr "Parameter BLE tidak valid" #: shared-bindings/wifi/Radio.c msgid "Invalid BSSID" -msgstr "" +msgstr "BSSID tidak valid" #: shared-bindings/wifi/Radio.c msgid "Invalid MAC address" -msgstr "" +msgstr "Alamat MAC tidak valid" #: ports/espressif/common-hal/rclcpy/__init__.c msgid "Invalid ROS domain ID" -msgstr "" +msgstr "ID domain ROS tidak valid" #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" @@ -1336,16 +1340,16 @@ msgstr "Bit per nilai tidak valid" #: shared-module/os/getenv.c #, c-format msgid "Invalid byte %.*s" -msgstr "" +msgstr "Byte tidak valid %.*s" #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" -msgstr "" +msgstr "data_pins[%d] tidak valid" #: shared-module/msgpack/__init__.c msgid "Invalid format" -msgstr "" +msgstr "Format tidak valid" #: shared-module/audiocore/WaveFile.c msgid "Invalid format chunk size" @@ -1353,29 +1357,29 @@ msgstr "Ukuran potongan format tidak valid" #: shared-bindings/wifi/Radio.c msgid "Invalid hex password" -msgstr "" +msgstr "Password hex tidak valid" #: ports/espressif/common-hal/wifi/Radio.c #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Invalid multicast MAC address" -msgstr "" +msgstr "Alamat MAC multicast tidak valid" #: ports/espressif/common-hal/espidf/__init__.c msgid "Invalid size" -msgstr "" +msgstr "Ukuran tidak valid" #: shared-module/ssl/SSLSocket.c msgid "Invalid socket for TLS" -msgstr "" +msgstr "Socket tidak valid untuk TLS" #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" -msgstr "" +msgstr "Status tidak valid" #: shared-module/os/getenv.c msgid "Invalid unicode escape" -msgstr "" +msgstr "Escape unicode tidak valid" #: shared-bindings/aesio/aes.c msgid "Key must be 16, 24, or 32 bytes long" @@ -1383,11 +1387,11 @@ msgstr "Panjang kunci harus 16, 24, atau 32 byte" #: shared-module/os/getenv.c msgid "Key not found" -msgstr "" +msgstr "Kunci tidak ditemukan" #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" -msgstr "" +msgstr "Pemetaan LED harus sesuai dengan ukuran display" #: py/compile.c msgid "LHS of keyword arg must be an id" @@ -1395,74 +1399,75 @@ msgstr "LHS dari keyword arg harus menjadi sebuah id" #: shared-module/displayio/Group.c msgid "Layer already in a group" -msgstr "" +msgstr "Layer sudah ada dalam grup" #: shared-module/displayio/Group.c msgid "Layer must be a Group or TileGrid subclass" -msgstr "" +msgstr "Layer harus merupakan subclass Group atau TileGrid" #: shared-bindings/audiocore/RawSample.c msgid "Length of %q must be an even multiple of channel_count * type_size" msgstr "" +"Panjang %q harus merupakan kelipatan genap dari channel_count * type_size" #: ports/espressif/common-hal/espidf/__init__.c msgid "MAC address was invalid" -msgstr "" +msgstr "Alamat MAC tidak valid" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/espressif/common-hal/_bleio/Descriptor.c msgid "MITM security not supported" -msgstr "" +msgstr "Keamanan MITM tidak didukung" #: ports/stm/common-hal/sdioio/SDCard.c #, c-format msgid "MMC/SDIO Clock Error %x" -msgstr "" +msgstr "Kesalahan Clock MMC/SDIO %x" #: shared-bindings/is31fl3741/IS31FL3741.c msgid "Mapping must be a tuple" -msgstr "" +msgstr "Mapping harus berupa tuple" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" -msgstr "" +msgstr "Ukuran data tidak cocok" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched swap flag" -msgstr "" +msgstr "Flag swap tidak cocok" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_in_pin. %q[%u] reads pin(s)" -msgstr "" +msgstr "first_in_pin tidak ada. %q[%u] membaca pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_in_pin. %q[%u] shifts in from pin(s)" -msgstr "" +msgstr "first_in_pin tidak ada. %q[%u] melakukan shift in dari pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_in_pin. %q[%u] waits based on pin" -msgstr "" +msgstr "first_in_pin tidak ada. %q[%u] menunggu berdasarkan pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_out_pin. %q[%u] shifts out to pin(s)" -msgstr "" +msgstr "first_out_pin tidak ada. %q[%u] melakukan shift out ke pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_out_pin. %q[%u] writes pin(s)" -msgstr "" +msgstr "first_out_pin tidak ada. %q[%u] menulis pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_set_pin. %q[%u] sets pin(s)" -msgstr "" +msgstr "first_set_pin tidak ada. %q[%u] menetapkan pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing jmp_pin. %q[%u] jumps on pin" -msgstr "" +msgstr "jmp_pin tidak ada. %q[%u] melompat pada pin" #: shared-module/storage/__init__.c msgid "Mount point directory missing" -msgstr "" +msgstr "Direktori titik mount tidak ada" #: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c #: shared-bindings/displayio/Group.c @@ -1471,7 +1476,7 @@ msgstr "Harus berupa subclass %q." #: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c msgid "Must provide 5/6/5 RGB pins" -msgstr "" +msgstr "Harus menyediakan pin RGB 5/6/5" #: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" @@ -1484,23 +1489,23 @@ msgstr "Harus menggunakan kelipatan 6 pin rgb, bukan %d" #: supervisor/shared/safe_mode.c msgid "NLR jump failed. Likely memory corruption." -msgstr "" +msgstr "Lompatan NLR gagal. Kemungkinan korupsi memori." #: ports/espressif/common-hal/nvm/ByteArray.c msgid "NVS Error" -msgstr "" +msgstr "Kesalahan NVS" #: shared-bindings/socketpool/SocketPool.c msgid "Name or service not known" -msgstr "" +msgstr "Nama atau layanan tidak diketahui" #: shared-bindings/displayio/TileGrid.c msgid "New bitmap must be same size as old bitmap" -msgstr "" +msgstr "Bitmap baru harus berukuran sama dengan bitmap lama" #: ports/espressif/common-hal/_bleio/__init__.c msgid "Nimble out of memory" -msgstr "" +msgstr "Nimble kehabisan memori" #: ports/atmel-samd/common-hal/busio/UART.c #: ports/espressif/common-hal/busio/SPI.c @@ -1535,26 +1540,26 @@ msgstr "tidak ada channel DMA ditemukan" #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c msgid "No DMA pacing timer found" -msgstr "" +msgstr "Tidak ditemukan timer pacing DMA" #: shared-module/adafruit_bus_device/i2c_device/I2CDevice.c #, c-format msgid "No I2C device at address: 0x%x" -msgstr "" +msgstr "Tidak ada perangkat I2C pada alamat: 0x%x" #: supervisor/shared/web_workflow/web_workflow.c msgid "No IP" -msgstr "" +msgstr "Tidak ada IP" #: ports/atmel-samd/common-hal/microcontroller/__init__.c #: ports/cxd56/common-hal/microcontroller/__init__.c #: ports/mimxrt10xx/common-hal/microcontroller/__init__.c msgid "No bootloader present" -msgstr "" +msgstr "Tidak ada bootloader" #: shared-module/usb/core/Device.c msgid "No configuration set" -msgstr "" +msgstr "Tidak ada konfigurasi yang ditetapkan" #: shared-bindings/_bleio/PacketBuffer.c msgid "No connection: length cannot be determined" @@ -1576,11 +1581,11 @@ msgstr "Tidak ada perangkat keras acak yang tersedia" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "No in in program" -msgstr "" +msgstr "Tidak ada in dalam program" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "No in or out in program" -msgstr "" +msgstr "Tidak ada in atau out dalam program" #: py/objint.c shared-bindings/time/__init__.c msgid "No long integer support" @@ -1588,18 +1593,18 @@ msgstr "Tidak ada dukungan bilangan bulat yang panjang" #: shared-bindings/wifi/Radio.c msgid "No network with that ssid" -msgstr "" +msgstr "Tidak ada jaringan dengan ssid tersebut" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "No out in program" -msgstr "" +msgstr "Tidak ada out dalam program" #: ports/atmel-samd/common-hal/busio/I2C.c #: ports/espressif/common-hal/busio/I2C.c #: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nordic/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/busio/I2C.c msgid "No pull up found on SDA or SCL; check your wiring" -msgstr "" +msgstr "Tidak ditemukan pull up pada SDA atau SCL; periksa pengkabelan Anda" #: shared-module/touchio/TouchIn.c msgid "No pulldown on pin; 1Mohm recommended" @@ -1607,7 +1612,7 @@ msgstr "Tidak ada pull-down pada pin; 1Mohm direkomendasikan" #: shared-module/touchio/TouchIn.c msgid "No pullup on pin; 1Mohm recommended" -msgstr "" +msgstr "Tidak ada pullup pada pin; 1Mohm direkomendasikan" #: py/moderrno.c msgid "No space left on device" @@ -1615,7 +1620,7 @@ msgstr "Tidak ada ruang yang tersisa di perangkat" #: py/moderrno.c msgid "No such device" -msgstr "" +msgstr "Perangkat tidak ada" #: py/moderrno.c msgid "No such file/directory" @@ -1627,15 +1632,15 @@ msgstr "Penghitung waktu tidak tersedia" #: shared-module/usb/core/Device.c msgid "No usb host port initialized" -msgstr "" +msgstr "Tidak ada port host usb yang diinisialisasi" #: ports/nordic/common-hal/_bleio/__init__.c msgid "Nordic system firmware out of memory" -msgstr "" +msgstr "Firmware sistem Nordic kehabisan memori" #: shared-bindings/ipaddress/IPv4Address.c shared-bindings/ipaddress/__init__.c msgid "Not a valid IP string" -msgstr "" +msgstr "Bukan string IP yang valid" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c @@ -1656,7 +1661,7 @@ msgstr "" #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format msgid "Number of data_pins must be %d or %d, not %d" -msgstr "" +msgstr "Jumlah data_pins harus %d atau %d, bukan %d" #: shared-bindings/util.c msgid "" @@ -1670,17 +1675,17 @@ msgstr "Parity ganjil tidak didukung" #: supervisor/shared/bluetooth/bluetooth.c msgid "Off" -msgstr "" +msgstr "Mati" #: supervisor/shared/bluetooth/bluetooth.c msgid "Ok" -msgstr "" +msgstr "Ok" #: ports/atmel-samd/common-hal/audiobusio/PDMIn.c #: ports/raspberrypi/common-hal/audiobusio/PDMIn.c #, c-format msgid "Only 8 or 16 bit mono with %dx oversampling supported." -msgstr "" +msgstr "Hanya mono 8 atau 16 bit dengan oversampling %dx yang didukung." #: ports/espressif/common-hal/wifi/__init__.c #: ports/raspberrypi/common-hal/wifi/__init__.c @@ -1701,51 +1706,51 @@ msgstr "" #: shared-bindings/_bleio/Adapter.c msgid "Only connectable advertisements can be directed" -msgstr "" +msgstr "Hanya advertisement yang dapat terhubung yang dapat diarahkan" #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" -msgstr "" +msgstr "Hanya deteksi tepi yang tersedia pada perangkat keras ini" #: shared-bindings/ipaddress/__init__.c msgid "Only int or string supported for ip" -msgstr "" +msgstr "Hanya int atau string yang didukung untuk ip" #: ports/espressif/common-hal/alarm/touch/TouchAlarm.c msgid "Only one %q can be set in deep sleep." -msgstr "" +msgstr "Hanya satu %q yang dapat ditetapkan dalam deep sleep." #: ports/espressif/common-hal/espulp/ULPAlarm.c msgid "Only one %q can be set." -msgstr "" +msgstr "Hanya satu %q yang dapat ditetapkan." #: ports/espressif/common-hal/i2ctarget/I2CTarget.c #: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c msgid "Only one address is allowed" -msgstr "" +msgstr "Hanya satu alamat yang diperbolehkan" #: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c #: ports/nordic/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set" -msgstr "" +msgstr "Hanya satu alarm alarm.time yang dapat ditetapkan" #: ports/espressif/common-hal/alarm/time/TimeAlarm.c #: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." -msgstr "" +msgstr "Hanya satu alarm alarm.time yang dapat ditetapkan." #: shared-module/displayio/ColorConverter.c msgid "Only one color can be transparent at a time" -msgstr "" +msgstr "Hanya satu warna yang dapat transparan pada satu waktu" #: py/moderrno.c msgid "Operation not permitted" -msgstr "" +msgstr "Operasi tidak diizinkan" #: ports/espressif/common-hal/espidf/__init__.c msgid "Operation or feature not supported" -msgstr "" +msgstr "Operasi atau fitur tidak didukung" #: ports/espressif/common-hal/espidf/__init__.c msgid "Operation timed out" @@ -1753,7 +1758,7 @@ msgstr "Waktu habis" #: ports/raspberrypi/common-hal/mdns/Server.c msgid "Out of MDNS service slots" -msgstr "" +msgstr "Kehabisan slot layanan MDNS" #: ports/espressif/common-hal/espidf/__init__.c msgid "Out of memory" @@ -1767,27 +1772,27 @@ msgstr "Kehabisan socket" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Out-buffer elements must be <= 4 bytes long" -msgstr "" +msgstr "Elemen out-buffer harus <= 4 byte panjangnya" #: ports/stm/common-hal/pwmio/PWMOut.c msgid "PWM restart" -msgstr "" +msgstr "Restart PWM" #: ports/raspberrypi/common-hal/countio/Counter.c msgid "PWM slice already in use" -msgstr "" +msgstr "Slice PWM sudah digunakan" #: ports/raspberrypi/common-hal/countio/Counter.c msgid "PWM slice channel A already in use" -msgstr "" +msgstr "Channel A slice PWM sudah digunakan" #: shared-bindings/spitarget/SPITarget.c msgid "Packet buffers for an SPI transfer must have the same length." -msgstr "" +msgstr "Buffer paket untuk transfer SPI harus memiliki panjang yang sama." #: shared-module/jpegio/JpegDecoder.c msgid "Parameter error" -msgstr "" +msgstr "Kesalahan parameter" #: ports/espressif/common-hal/audiobusio/__init__.c msgid "Peripheral in use" @@ -1799,7 +1804,7 @@ msgstr "Izin ditolak" #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Pin cannot wake from Deep Sleep" -msgstr "" +msgstr "Pin tidak dapat membangunkan dari Deep Sleep" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Pin count too large" @@ -1843,7 +1848,7 @@ msgstr "Pin harus berbagi potongan PWM" #: shared-module/usb/core/Device.c msgid "Pipe error" -msgstr "" +msgstr "Kesalahan pipe" #: py/builtinhelp.c msgid "Plus any modules on the filesystem\n" @@ -1869,7 +1874,7 @@ msgstr "" #: main.c msgid "Pretending to deep sleep until alarm, CTRL-C or file write.\n" -msgstr "" +msgstr "Berpura-pura deep sleep sampai alarm, CTRL-C atau penulisan file.\n" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Program does IN without loading ISR" @@ -1889,7 +1894,7 @@ msgstr "Program terlalu lama" #: shared-bindings/rclcpy/Publisher.c msgid "Publishers can only be created from a parent node" -msgstr "" +msgstr "Publisher hanya dapat dibuat dari node induk" #: shared-bindings/digitalio/DigitalInOut.c msgid "Pull not used when direction is output." @@ -1913,23 +1918,23 @@ msgstr "Kesalahan Init RNG" #: ports/espressif/common-hal/rclcpy/__init__.c msgid "ROS failed to initialize. Is agent connected?" -msgstr "" +msgstr "ROS gagal diinisialisasi. Apakah agen terhubung?" #: ports/espressif/common-hal/rclcpy/__init__.c msgid "ROS internal setup failure" -msgstr "" +msgstr "Kegagalan pengaturan internal ROS" #: ports/espressif/common-hal/rclcpy/__init__.c msgid "ROS memory allocator failure" -msgstr "" +msgstr "Kegagalan alokator memori ROS" #: ports/espressif/common-hal/rclcpy/Node.c msgid "ROS node failed to initialize" -msgstr "" +msgstr "Node ROS gagal diinisialisasi" #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "ROS topic failed to initialize" -msgstr "" +msgstr "Topik ROS gagal diinisialisasi" #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c @@ -2003,18 +2008,18 @@ msgstr "Kartu SD Format CSD tidak didukung" #: ports/cxd56/common-hal/sdioio/SDCard.c msgid "SDCard init" -msgstr "" +msgstr "Inisialisasi SDCard" #: ports/stm/common-hal/sdioio/SDCard.c #, c-format msgid "SDIO GetCardInfo Error %d" -msgstr "" +msgstr "Kesalahan GetCardInfo SDIO %d" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c #, c-format msgid "SDIO Init Error %x" -msgstr "" +msgstr "Kesalahan Init SDIO %x" #: ports/espressif/common-hal/busio/SPI.c msgid "SPI configuration failed" @@ -2022,7 +2027,7 @@ msgstr "Konfigurasi SPI gagal" #: ports/stm/common-hal/busio/SPI.c msgid "SPI init error" -msgstr "" +msgstr "Kesalahan inisialisasi SPI" #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" @@ -2030,7 +2035,7 @@ msgstr "Periferal SPI sedang digunakan" #: ports/stm/common-hal/busio/SPI.c msgid "SPI re-init" -msgstr "" +msgstr "Inisialisasi ulang SPI" #: shared-bindings/is31fl3741/FrameBuffer.c msgid "Scale dimensions must divide by 3" @@ -2146,7 +2151,7 @@ msgstr "Lebar ubin harus persis membagi lebar bitmap" #: shared-module/tilepalettemapper/TilePaletteMapper.c msgid "TilePaletteMapper may only be bound to a TileGrid once" -msgstr "" +msgstr "TilePaletteMapper hanya dapat diikat ke TileGrid sekali" #: shared-bindings/alarm/time/TimeAlarm.c msgid "Time is in the past." @@ -2197,12 +2202,12 @@ msgstr "Traceback (bagian terakhir dari panggilan terkini):\n" #: ports/stm/common-hal/busio/UART.c msgid "UART de-init" -msgstr "" +msgstr "De-inisialisasi UART" #: ports/cxd56/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c #: ports/stm/common-hal/busio/UART.c msgid "UART init" -msgstr "" +msgstr "Inisialisasi UART" #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" @@ -2210,11 +2215,11 @@ msgstr "Periferal UART sedang digunakan" #: ports/stm/common-hal/busio/UART.c msgid "UART re-init" -msgstr "" +msgstr "Inisialisasi ulang UART" #: ports/stm/common-hal/busio/UART.c msgid "UART write" -msgstr "" +msgstr "Penulisan UART" #: main.c msgid "UID:" @@ -2222,7 +2227,7 @@ msgstr "UID:" #: shared-module/usb_hid/Device.c msgid "USB busy" -msgstr "" +msgstr "USB sibuk" #: supervisor/shared/safe_mode.c msgid "USB devices need more endpoints than are available." @@ -2234,7 +2239,7 @@ msgstr "Perangkat USB menggunakan terlalu banyak nama antarmuka." #: shared-module/usb_hid/Device.c msgid "USB error" -msgstr "" +msgstr "Kesalahan USB" #: shared-bindings/_bleio/UUID.c msgid "UUID integer value must be 0-0xffff" @@ -2283,7 +2288,7 @@ msgstr "Tidak dapat membaca data palet warna" #: ports/mimxrt10xx/common-hal/canio/CAN.c msgid "Unable to send CAN Message: all Tx message buffers are busy" -msgstr "" +msgstr "Tidak dapat mengirim Pesan CAN: semua buffer pesan Tx sibuk" #: ports/espressif/common-hal/mdns/Server.c #: ports/raspberrypi/common-hal/mdns/Server.c @@ -2373,7 +2378,7 @@ msgstr "" #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" -msgstr "" +msgstr "Colorspace tidak didukung" #: shared-module/displayio/bus_core.c msgid "Unsupported display bus type" @@ -2439,17 +2444,17 @@ msgstr "" #: supervisor/shared/web_workflow/web_workflow.c msgid "Wi-Fi: " -msgstr "" +msgstr "Wi-Fi: " #: ports/espressif/common-hal/wifi/Radio.c #: ports/raspberrypi/common-hal/wifi/Radio.c #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "WiFi is not enabled" -msgstr "" +msgstr "WiFi tidak diaktifkan" #: main.c msgid "Woken up by alarm.\n" -msgstr "" +msgstr "Dibangunkan oleh alarm.\n" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c @@ -2528,7 +2533,7 @@ msgstr "__init __() harus mengembalikan None" #: py/objtype.c #, c-format msgid "__init__() should return None, not '%s'" -msgstr "" +msgstr "__init__() harus mengembalikan None, bukan '%s'" #: py/objobject.c msgid "__new__ arg must be a user-type" @@ -2544,7 +2549,7 @@ msgstr "alamatnya kosong" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "already playing" -msgstr "" +msgstr "sudah diputar" #: py/compile.c msgid "annotation must be an identifier" @@ -2568,15 +2573,15 @@ msgstr "Argumen argsort harus berupa ndarray" #: extmod/ulab/code/numpy/numerical.c msgid "argsort is not implemented for flattened arrays" -msgstr "" +msgstr "argsort tidak diimplementasikan untuk array yang diratakan" #: extmod/ulab/code/numpy/random/random.c msgid "argument must be None, an integer or a tuple of integers" -msgstr "" +msgstr "argumen harus None, integer atau tuple integer" #: py/compile.c msgid "argument name reused" -msgstr "" +msgstr "nama argumen digunakan kembali" #: py/argcheck.c shared-bindings/_stage/__init__.c #: shared-bindings/digitalio/DigitalInOut.c @@ -2589,15 +2594,15 @@ msgstr "argumen harus berupa ndarrays" #: extmod/ulab/code/ndarray.c msgid "array and index length must be equal" -msgstr "" +msgstr "panjang array dan indeks harus sama" #: extmod/ulab/code/numpy/io/io.c msgid "array has too many dimensions" -msgstr "" +msgstr "array memiliki terlalu banyak dimensi" #: extmod/ulab/code/ndarray.c msgid "array is too big" -msgstr "" +msgstr "array terlalu besar" #: py/objarray.c shared-bindings/alarm/SleepMemory.c #: shared-bindings/memorymap/AddressRange.c shared-bindings/nvm/ByteArray.c @@ -2606,15 +2611,15 @@ msgstr "diperlukan array/byte di sisi kanan" #: py/asmxtensa.c msgid "asm overflow" -msgstr "" +msgstr "asm overflow" #: py/compile.c msgid "async for/with outside async function" -msgstr "" +msgstr "async for/with di luar fungsi async" #: extmod/ulab/code/numpy/numerical.c msgid "attempt to get (arg)min/(arg)max of empty sequence" -msgstr "" +msgstr "percobaan untuk mendapatkan (arg)min/(arg)max dari urutan kosong" #: extmod/ulab/code/numpy/numerical.c msgid "attempt to get argmin/argmax of an empty sequence" @@ -2622,27 +2627,27 @@ msgstr "berusaha mendapatkan argmin/argmax dari urutan kosong" #: py/objstr.c msgid "attributes not supported" -msgstr "" +msgstr "atribut tidak didukung" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "audio format not supported" -msgstr "" +msgstr "format audio tidak didukung" #: extmod/ulab/code/ulab_tools.c msgid "axis is out of bounds" -msgstr "" +msgstr "axis berada di luar batas" #: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c msgid "axis must be None, or an integer" -msgstr "" +msgstr "axis harus None, atau integer" #: extmod/ulab/code/numpy/numerical.c msgid "axis too long" -msgstr "" +msgstr "axis terlalu panjang" #: shared-bindings/bitmaptools/__init__.c msgid "background value out of range of target" -msgstr "" +msgstr "nilai latar belakang di luar jangkauan target" #: py/builtinevex.c msgid "bad compile mode" @@ -2654,7 +2659,7 @@ msgstr "specifier salah konversi" #: py/objstr.c msgid "bad format string" -msgstr "" +msgstr "string format salah" #: py/binary.c py/objarray.c msgid "bad typecode" @@ -2662,27 +2667,27 @@ msgstr "typecode buruk" #: py/emitnative.c msgid "binary op %q not implemented" -msgstr "" +msgstr "operasi biner %q tidak diimplementasikan" #: ports/espressif/common-hal/audiobusio/PDMIn.c msgid "bit_depth must be 8, 16, 24, or 32." -msgstr "" +msgstr "bit_depth harus 8, 16, 24, atau 32." #: shared-module/bitmapfilter/__init__.c msgid "bitmap size and depth must match" -msgstr "" +msgstr "ukuran dan kedalaman bitmap harus cocok" #: shared-bindings/bitmaptools/__init__.c msgid "bitmap sizes must match" -msgstr "" +msgstr "ukuran bitmap harus cocok" #: extmod/modrandom.c msgid "bits must be 32 or less" -msgstr "" +msgstr "bit harus 32 atau kurang" #: shared-bindings/audiofreeverb/Freeverb.c msgid "bits_per_sample must be 16" -msgstr "" +msgstr "bits_per_sample harus 16" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c #: shared-bindings/audiodelays/MultiTapDelay.c @@ -2691,19 +2696,19 @@ msgstr "" #: shared-bindings/audiofilters/Filter.c shared-bindings/audiofilters/Phaser.c #: shared-bindings/audiomixer/Mixer.c msgid "bits_per_sample must be 8 or 16" -msgstr "" +msgstr "bits_per_sample harus 8 atau 16" #: py/emitinlinethumb.c msgid "branch not in range" -msgstr "" +msgstr "branch tidak dalam jangkauan" #: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c msgid "buffer is smaller than requested size" -msgstr "" +msgstr "buffer lebih kecil dari ukuran yang diminta" #: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c msgid "buffer size must be a multiple of element size" -msgstr "" +msgstr "ukuran buffer harus kelipatan dari ukuran elemen" #: shared-module/struct/__init__.c msgid "buffer size must match format" @@ -2711,27 +2716,27 @@ msgstr "ukuran buffer harus sesuai dengan format" #: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c msgid "buffer slices must be of equal length" -msgstr "" +msgstr "potongan buffer harus memiliki panjang yang sama" #: py/modstruct.c shared-module/struct/__init__.c msgid "buffer too small" -msgstr "" +msgstr "buffer terlalu kecil" #: shared-bindings/socketpool/Socket.c shared-bindings/ssl/SSLSocket.c msgid "buffer too small for requested bytes" -msgstr "" +msgstr "buffer terlalu kecil untuk byte yang diminta" #: py/emitbc.c msgid "bytecode overflow" -msgstr "" +msgstr "bytecode overflow" #: py/objarray.c msgid "bytes length not a multiple of item size" -msgstr "" +msgstr "panjang bytes bukan kelipatan dari ukuran item" #: py/objstr.c msgid "bytes value out of range" -msgstr "" +msgstr "nilai bytes di luar jangkauan" #: ports/atmel-samd/bindings/samd/Clock.c msgid "calibration is out of range" @@ -2744,7 +2749,7 @@ msgstr "kalibrasi adalah read only" #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" -msgstr "" +msgstr "hanya dapat memiliki satu induk" #: py/emitinlinethumb.c msgid "can only have up to 4 parameters to Thumb assembly" @@ -2752,15 +2757,15 @@ msgstr "hanya mampu memiliki hingga 4 parameter untuk Thumb assembly" #: py/emitinlinextensa.c msgid "can only have up to 4 parameters to Xtensa assembly" -msgstr "" +msgstr "hanya dapat memiliki hingga 4 parameter untuk assembly Xtensa" #: extmod/ulab/code/ndarray.c msgid "can only specify one unknown dimension" -msgstr "" +msgstr "hanya dapat menentukan satu dimensi yang tidak diketahui" #: py/objtype.c msgid "can't add special method to already-subclassed class" -msgstr "" +msgstr "tidak dapat menambahkan metode khusus ke kelas yang sudah di-subclass" #: py/compile.c msgid "can't assign to expression" @@ -2768,50 +2773,50 @@ msgstr "tidak dapat menetapkan ke ekspresi" #: extmod/modasyncio.c msgid "can't cancel self" -msgstr "" +msgstr "tidak dapat membatalkan diri sendiri" #: py/obj.c shared-module/adafruit_pixelbuf/PixelBuf.c msgid "can't convert %q to %q" -msgstr "" +msgstr "tidak dapat mengonversi %q ke %q" #: py/obj.c #, c-format msgid "can't convert %s to complex" -msgstr "" +msgstr "tidak dapat mengonversi %s ke complex" #: py/obj.c #, c-format msgid "can't convert %s to float" -msgstr "" +msgstr "tidak dapat mengonversi %s ke float" #: py/objint.c py/runtime.c #, c-format msgid "can't convert %s to int" -msgstr "" +msgstr "tidak dapat mengonversi %s ke int" #: py/objstr.c msgid "can't convert '%q' object to %q implicitly" -msgstr "" +msgstr "tidak dapat mengonversi objek '%q' ke %q secara implisit" #: extmod/ulab/code/numpy/vector.c msgid "can't convert complex to float" -msgstr "" +msgstr "tidak dapat mengonversi complex ke float" #: py/obj.c msgid "can't convert to complex" -msgstr "" +msgstr "tidak dapat mengonversi ke complex" #: py/obj.c msgid "can't convert to float" -msgstr "" +msgstr "tidak dapat mengonversi ke float" #: py/runtime.c msgid "can't convert to int" -msgstr "" +msgstr "tidak dapat mengonversi ke int" #: py/objstr.c msgid "can't convert to str implicitly" -msgstr "" +msgstr "tidak dapat mengonversi ke str secara implisit" #: py/compile.c msgid "can't declare nonlocal in outer code" @@ -2823,93 +2828,95 @@ msgstr "tidak bisa menghapus ekspresi" #: py/emitnative.c msgid "can't do binary op between '%q' and '%q'" -msgstr "" +msgstr "tidak dapat melakukan operasi biner antara '%q' dan '%q'" #: py/emitnative.c msgid "can't do unary op of '%q'" -msgstr "" +msgstr "tidak dapat melakukan operasi unary dari '%q'" #: py/emitnative.c msgid "can't implicitly convert '%q' to 'bool'" -msgstr "" +msgstr "tidak dapat mengonversi '%q' ke 'bool' secara implisit" #: py/runtime.c msgid "can't import name %q" -msgstr "" +msgstr "tidak dapat mengimpor nama %q" #: py/emitnative.c msgid "can't load from '%q'" -msgstr "" +msgstr "tidak dapat memuat dari '%q'" #: py/emitnative.c msgid "can't load with '%q' index" -msgstr "" +msgstr "tidak dapat memuat dengan indeks '%q'" #: py/builtinimport.c msgid "can't perform relative import" -msgstr "" +msgstr "tidak dapat melakukan impor relatif" #: py/objgenerator.c msgid "can't send non-None value to a just-started generator" -msgstr "" +msgstr "tidak dapat mengirim nilai non-None ke generator yang baru dimulai" #: shared-module/sdcardio/SDCard.c msgid "can't set 512 block size" -msgstr "" +msgstr "tidak dapat mengatur ukuran blok 512" #: py/objexcept.c py/objnamedtuple.c msgid "can't set attribute" -msgstr "" +msgstr "tidak dapat mengatur atribut" #: py/runtime.c msgid "can't set attribute '%q'" -msgstr "" +msgstr "tidak dapat mengatur atribut '%q'" #: py/emitnative.c msgid "can't store '%q'" -msgstr "" +msgstr "tidak dapat menyimpan '%q'" #: py/emitnative.c msgid "can't store to '%q'" -msgstr "" +msgstr "tidak dapat menyimpan ke '%q'" #: py/emitnative.c msgid "can't store with '%q' index" -msgstr "" +msgstr "tidak dapat menyimpan dengan indeks '%q'" #: py/objstr.c msgid "" "can't switch from automatic field numbering to manual field specification" msgstr "" +"tidak dapat beralih dari penomoran field otomatis ke spesifikasi field manual" #: py/objstr.c msgid "" "can't switch from manual field specification to automatic field numbering" msgstr "" +"tidak dapat beralih dari spesifikasi field manual ke penomoran field otomatis" #: py/objcomplex.c msgid "can't truncate-divide a complex number" -msgstr "" +msgstr "tidak dapat truncate-divide bilangan complex" #: extmod/modasyncio.c msgid "can't wait" -msgstr "" +msgstr "tidak dapat menunggu" #: extmod/ulab/code/ndarray.c msgid "cannot assign new shape" -msgstr "" +msgstr "tidak dapat menetapkan bentuk baru" #: extmod/ulab/code/ndarray_operators.c msgid "cannot cast output with casting rule" -msgstr "" +msgstr "tidak dapat melakukan cast output dengan aturan casting" #: extmod/ulab/code/ndarray.c msgid "cannot convert complex to dtype" -msgstr "" +msgstr "tidak dapat mengonversi complex ke dtype" #: extmod/ulab/code/ndarray.c msgid "cannot convert complex type" -msgstr "" +msgstr "tidak dapat mengonversi tipe complex" #: py/objtype.c msgid "cannot create '%q' instances" @@ -2917,71 +2924,71 @@ msgstr "" #: py/objtype.c msgid "cannot create instance" -msgstr "" +msgstr "tidak dapat membuat instance" #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" -msgstr "" +msgstr "tidak dapat menghapus elemen array" #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" -msgstr "" +msgstr "tidak dapat membentuk ulang array" #: py/emitnative.c msgid "casting" -msgstr "" +msgstr "casting" #: ports/stm/common-hal/pwmio/PWMOut.c msgid "channel re-init" -msgstr "" +msgstr "inisialisasi ulang channel" #: shared-bindings/_stage/Text.c msgid "chars buffer too small" -msgstr "" +msgstr "buffer chars terlalu kecil" #: py/modbuiltins.c msgid "chr() arg not in range(0x110000)" -msgstr "" +msgstr "argumen chr() tidak dalam rentang(0x110000)" #: py/modbuiltins.c msgid "chr() arg not in range(256)" -msgstr "" +msgstr "argumen chr() tidak dalam rentang(256)" #: shared-bindings/bitmaptools/__init__.c msgid "clip point must be (x,y) tuple" -msgstr "" +msgstr "clip point harus berupa tuple (x,y)" #: shared-bindings/msgpack/ExtType.c msgid "code outside range 0~127" -msgstr "" +msgstr "kode di luar rentang 0~127" #: shared-bindings/displayio/Palette.c msgid "color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)" -msgstr "" +msgstr "buffer warna harus 3 byte (RGB) atau 4 byte (RGB + pad byte)" #: shared-bindings/displayio/Palette.c msgid "color buffer must be a buffer, tuple, list, or int" -msgstr "" +msgstr "buffer warna harus berupa buffer, tuple, list, atau int" #: shared-bindings/displayio/Palette.c msgid "color buffer must be a bytearray or array of type 'b' or 'B'" -msgstr "" +msgstr "buffer warna harus berupa bytearray atau array bertipe 'b' atau 'B'" #: shared-bindings/displayio/Palette.c msgid "color must be between 0x000000 and 0xffffff" -msgstr "" +msgstr "warna harus antara 0x000000 dan 0xffffff" #: py/emitnative.c msgid "comparison of int and uint" -msgstr "" +msgstr "perbandingan int dan uint" #: py/objcomplex.c msgid "complex divide by zero" -msgstr "" +msgstr "pembagian complex dengan nol" #: py/objfloat.c py/parsenum.c msgid "complex values not supported" -msgstr "" +msgstr "nilai complex tidak didukung" #: extmod/modzlib.c msgid "compression header" @@ -2989,56 +2996,56 @@ msgstr "kompresi header" #: py/emitnative.c msgid "conversion to object" -msgstr "" +msgstr "konversi ke objek" #: extmod/ulab/code/numpy/filter.c msgid "convolve arguments must be linear arrays" -msgstr "" +msgstr "argumen convolve harus berupa array linear" #: extmod/ulab/code/numpy/filter.c msgid "convolve arguments must be ndarrays" -msgstr "" +msgstr "argumen convolve harus berupa ndarray" #: extmod/ulab/code/numpy/filter.c msgid "convolve arguments must not be empty" -msgstr "" +msgstr "argumen convolve tidak boleh kosong" #: extmod/ulab/code/numpy/io/io.c msgid "corrupted file" -msgstr "" +msgstr "file rusak" #: extmod/ulab/code/numpy/poly.c msgid "could not invert Vandermonde matrix" -msgstr "" +msgstr "tidak dapat membalik matriks Vandermonde" #: shared-module/sdcardio/SDCard.c msgid "couldn't determine SD card version" -msgstr "" +msgstr "tidak dapat menentukan versi kartu SD" #: extmod/ulab/code/numpy/numerical.c msgid "cross is defined for 1D arrays of length 3" -msgstr "" +msgstr "cross didefinisikan untuk array 1D dengan panjang 3" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "data must be iterable" -msgstr "" +msgstr "data harus dapat diiterasi" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "data must be of equal length" -msgstr "" +msgstr "data harus memiliki panjang yang sama" #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "data pin #%d in use" -msgstr "" +msgstr "pin data #%d sedang digunakan" #: extmod/ulab/code/ndarray.c msgid "data type not understood" -msgstr "" +msgstr "tipe data tidak dipahami" #: py/parsenum.c msgid "decimal numbers not supported" -msgstr "" +msgstr "angka desimal tidak didukung" #: py/compile.c msgid "default 'except' must be last" @@ -3046,60 +3053,62 @@ msgstr "'except' standar harus terakhir" #: shared-bindings/msgpack/__init__.c msgid "default is not a function" -msgstr "" +msgstr "default bukan fungsi" #: shared-bindings/audiobusio/PDMIn.c msgid "" "destination buffer must be a bytearray or array of type 'B' for bit_depth = 8" msgstr "" +"buffer tujuan harus berupa bytearray atau array bertipe 'B' untuk bit_depth " +"= 8" #: shared-bindings/audiobusio/PDMIn.c msgid "destination buffer must be an array of type 'H' for bit_depth = 16" -msgstr "" +msgstr "buffer tujuan harus berupa array bertipe 'H' untuk bit_depth = 16" #: py/objdict.c msgid "dict update sequence has wrong length" -msgstr "" +msgstr "urutan pembaruan dict memiliki panjang yang salah" #: extmod/ulab/code/numpy/numerical.c msgid "diff argument must be an ndarray" -msgstr "" +msgstr "argumen diff harus berupa ndarray" #: extmod/ulab/code/numpy/numerical.c msgid "differentiation order out of range" -msgstr "" +msgstr "urutan diferensiasi di luar jangkauan" #: extmod/ulab/code/numpy/transform.c msgid "dimensions do not match" -msgstr "" +msgstr "dimensi tidak cocok" #: py/emitnative.c msgid "div/mod not implemented for uint" -msgstr "" +msgstr "div/mod tidak diimplementasikan untuk uint" #: extmod/ulab/code/numpy/create.c msgid "divide by zero" -msgstr "" +msgstr "pembagian dengan nol" #: py/runtime.c msgid "division by zero" -msgstr "" +msgstr "pembagian dengan nol" #: extmod/ulab/code/numpy/vector.c msgid "dtype must be float, or complex" -msgstr "" +msgstr "dtype harus float, atau complex" #: extmod/ulab/code/ndarray_operators.c msgid "dtype of int32 is not supported" -msgstr "" +msgstr "dtype int32 tidak didukung" #: py/objdeque.c msgid "empty" -msgstr "" +msgstr "kosong" #: extmod/ulab/code/numpy/io/io.c msgid "empty file" -msgstr "" +msgstr "file kosong" #: extmod/modasyncio.c extmod/modheapq.c msgid "empty heap" @@ -3107,19 +3116,19 @@ msgstr "heap kosong" #: py/objstr.c msgid "empty separator" -msgstr "" +msgstr "pemisah kosong" #: shared-bindings/random/__init__.c msgid "empty sequence" -msgstr "" +msgstr "urutan kosong" #: py/objstr.c msgid "end of format while looking for conversion specifier" -msgstr "" +msgstr "akhir format saat mencari spesifier konversi" #: shared-bindings/alarm/time/TimeAlarm.c msgid "epoch_time not supported on this board" -msgstr "" +msgstr "epoch_time tidak didukung pada board ini" #: ports/nordic/common-hal/busio/UART.c #, c-format @@ -3128,19 +3137,19 @@ msgstr "error = 0x%08lX" #: py/runtime.c msgid "exceptions must derive from BaseException" -msgstr "" +msgstr "exception harus diturunkan dari BaseException" #: py/objstr.c msgid "expected ':' after format specifier" -msgstr "" +msgstr "mengharapkan ':' setelah spesifier format" #: py/obj.c msgid "expected tuple/list" -msgstr "" +msgstr "mengharapkan tuple/list" #: py/modthread.c msgid "expecting a dict for keyword args" -msgstr "" +msgstr "mengharapkan dict untuk argumen keyword" #: py/compile.c msgid "expecting an assembler instruction" @@ -3156,7 +3165,7 @@ msgstr "key:value diharapkan untuk dict" #: shared-bindings/msgpack/__init__.c msgid "ext_hook is not a function" -msgstr "" +msgstr "ext_hook bukan fungsi" #: py/argcheck.c msgid "extra keyword arguments given" @@ -3170,11 +3179,11 @@ msgstr "argumen posisi ekstra telah diberikan" #: shared-bindings/displayio/OnDiskBitmap.c shared-bindings/gifio/OnDiskGif.c #: shared-bindings/synthio/__init__.c shared-module/gifio/GifWriter.c msgid "file must be a file opened in byte mode" -msgstr "" +msgstr "file harus berupa file yang dibuka dalam mode byte" #: shared-bindings/traceback/__init__.c msgid "file write is not available" -msgstr "" +msgstr "penulisan file tidak tersedia" #: shared-bindings/storage/__init__.c msgid "filesystem must provide mount method" @@ -3182,63 +3191,63 @@ msgstr "" #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" -msgstr "" +msgstr "argumen pertama harus dapat dipanggil" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "first argument must be a function" -msgstr "" +msgstr "argumen pertama harus berupa fungsi" #: extmod/ulab/code/numpy/create.c msgid "first argument must be a tuple of ndarrays" -msgstr "" +msgstr "argumen pertama harus berupa tuple ndarray" #: extmod/ulab/code/numpy/transform.c extmod/ulab/code/numpy/vector.c msgid "first argument must be an ndarray" -msgstr "" +msgstr "argumen pertama harus berupa ndarray" #: py/objtype.c msgid "first argument to super() must be type" -msgstr "" +msgstr "argumen pertama untuk super() harus bertipe type" #: extmod/ulab/code/scipy/linalg/linalg.c msgid "first two arguments must be ndarrays" -msgstr "" +msgstr "dua argumen pertama harus berupa ndarray" #: extmod/ulab/code/ndarray.c msgid "flattening order must be either 'C', or 'F'" -msgstr "" +msgstr "urutan perataan harus 'C', atau 'F'" #: extmod/ulab/code/numpy/numerical.c msgid "flip argument must be an ndarray" -msgstr "" +msgstr "argumen flip harus berupa ndarray" #: py/objint.c msgid "float too big" -msgstr "" +msgstr "float terlalu besar" #: py/nativeglue.c msgid "float unsupported" -msgstr "" +msgstr "float tidak didukung" #: shared-bindings/_stage/Text.c msgid "font must be 2048 bytes long" -msgstr "" +msgstr "font harus sepanjang 2048 byte" #: extmod/moddeflate.c msgid "format" -msgstr "" +msgstr "format" #: py/objstr.c msgid "format needs a dict" -msgstr "" +msgstr "format memerlukan dict" #: py/objdeque.c msgid "full" -msgstr "" +msgstr "penuh" #: py/argcheck.c msgid "function doesn't take keyword arguments" -msgstr "" +msgstr "fungsi tidak menerima argumen keyword" #: py/argcheck.c #, c-format @@ -3251,15 +3260,15 @@ msgstr "fungsi mendapatkan nilai ganda untuk argumen '%q'" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "function has the same sign at the ends of interval" -msgstr "" +msgstr "fungsi memiliki tanda yang sama pada ujung interval" #: extmod/ulab/code/ndarray.c msgid "function is defined for ndarrays only" -msgstr "" +msgstr "fungsi didefinisikan hanya untuk ndarray" #: extmod/ulab/code/numpy/carray/carray.c msgid "function is implemented for ndarrays only" -msgstr "" +msgstr "fungsi diimplementasikan hanya untuk ndarray" #: py/argcheck.c #, c-format @@ -3287,23 +3296,23 @@ msgstr "fungsi mengambil posisi argumen %d tapi %d yang diberikan" #: py/objgenerator.c msgid "generator already executing" -msgstr "" +msgstr "generator sudah berjalan" #: py/objgenerator.c msgid "generator ignored GeneratorExit" -msgstr "" +msgstr "generator mengabaikan GeneratorExit" #: py/objgenerator.c py/runtime.c msgid "generator raised StopIteration" -msgstr "" +msgstr "generator memunculkan StopIteration" #: shared-bindings/_stage/Layer.c msgid "graphic must be 2048 bytes long" -msgstr "" +msgstr "grafik harus sepanjang 2048 byte" #: extmod/modhashlib.c msgid "hash is final" -msgstr "" +msgstr "hash bersifat final" #: extmod/modheapq.c msgid "heap must be a list" @@ -3319,23 +3328,23 @@ msgstr "identifier didefinisi ulang sebagai nonlocal" #: py/compile.c msgid "import * not at module level" -msgstr "" +msgstr "import * tidak pada tingkat modul" #: py/persistentcode.c msgid "incompatible .mpy arch" -msgstr "" +msgstr "arsitektur .mpy tidak kompatibel" #: py/persistentcode.c msgid "incompatible .mpy file" -msgstr "" +msgstr "file .mpy tidak kompatibel" #: py/objstr.c msgid "incomplete format" -msgstr "" +msgstr "format tidak lengkap" #: py/objstr.c msgid "incomplete format key" -msgstr "" +msgstr "kunci format tidak lengkap" #: extmod/modbinascii.c msgid "incorrect padding" @@ -3343,11 +3352,11 @@ msgstr "lapisan (padding) tidak benar" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/transform.c msgid "index is out of bounds" -msgstr "" +msgstr "indeks berada di luar batas" #: shared-bindings/_pixelmap/PixelMap.c msgid "index must be tuple or int" -msgstr "" +msgstr "indeks harus berupa tuple atau int" #: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c #: ports/espressif/common-hal/pulseio/PulseIn.c @@ -3357,19 +3366,19 @@ msgstr "index keluar dari jangkauan" #: py/obj.c msgid "indices must be integers" -msgstr "" +msgstr "indeks harus berupa integer" #: extmod/ulab/code/ndarray.c msgid "indices must be integers, slices, or Boolean lists" -msgstr "" +msgstr "indeks harus berupa integer, slice, atau list Boolean" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "initial values must be iterable" -msgstr "" +msgstr "nilai awal harus dapat diiterasi" #: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c msgid "initial_value length is wrong" -msgstr "" +msgstr "panjang initial_value salah" #: py/compile.c msgid "inline assembler must be a function" @@ -3377,98 +3386,98 @@ msgstr "inline assembler harus sebuah fungsi" #: extmod/ulab/code/numpy/vector.c msgid "input and output dimensions differ" -msgstr "" +msgstr "dimensi input dan output berbeda" #: extmod/ulab/code/numpy/vector.c msgid "input and output shapes differ" -msgstr "" +msgstr "bentuk input dan output berbeda" #: extmod/ulab/code/numpy/create.c msgid "input argument must be an integer, a tuple, or a list" -msgstr "" +msgstr "argumen input harus berupa integer, tuple, atau list" #: extmod/ulab/code/numpy/fft/fft_tools.c msgid "input array length must be power of 2" -msgstr "" +msgstr "panjang array input harus pangkat 2" #: extmod/ulab/code/numpy/create.c msgid "input arrays are not compatible" -msgstr "" +msgstr "array input tidak kompatibel" #: extmod/ulab/code/numpy/poly.c msgid "input data must be an iterable" -msgstr "" +msgstr "data input harus dapat diiterasi" #: extmod/ulab/code/numpy/vector.c msgid "input dtype must be float or complex" -msgstr "" +msgstr "dtype input harus float atau complex" #: extmod/ulab/code/numpy/poly.c msgid "input is not iterable" -msgstr "" +msgstr "input tidak dapat diiterasi" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "input matrix is asymmetric" -msgstr "" +msgstr "matriks input asimetris" #: extmod/ulab/code/numpy/linalg/linalg.c #: extmod/ulab/code/scipy/linalg/linalg.c msgid "input matrix is singular" -msgstr "" +msgstr "matriks input singular" #: extmod/ulab/code/numpy/create.c msgid "input must be 1- or 2-d" -msgstr "" +msgstr "input harus 1- atau 2-d" #: extmod/ulab/code/numpy/carray/carray.c msgid "input must be a 1D ndarray" -msgstr "" +msgstr "input harus berupa ndarray 1D" #: extmod/ulab/code/scipy/linalg/linalg.c extmod/ulab/code/user/user.c msgid "input must be a dense ndarray" -msgstr "" +msgstr "input harus berupa ndarray padat" #: extmod/ulab/code/user/user.c shared-bindings/_eve/__init__.c msgid "input must be an ndarray" -msgstr "" +msgstr "input harus berupa ndarray" #: extmod/ulab/code/numpy/carray/carray.c msgid "input must be an ndarray, or a scalar" -msgstr "" +msgstr "input harus berupa ndarray, atau skalar" #: extmod/ulab/code/scipy/signal/signal.c msgid "input must be one-dimensional" -msgstr "" +msgstr "input harus satu dimensi" #: extmod/ulab/code/ulab_tools.c msgid "input must be square matrix" -msgstr "" +msgstr "input harus berupa matriks persegi" #: extmod/ulab/code/numpy/numerical.c msgid "input must be tuple, list, range, or ndarray" -msgstr "" +msgstr "input harus berupa tuple, list, range, atau ndarray" #: extmod/ulab/code/numpy/poly.c msgid "input vectors must be of equal length" -msgstr "" +msgstr "vektor input harus memiliki panjang yang sama" #: extmod/ulab/code/numpy/approx.c msgid "interp is defined for 1D iterables of equal length" -msgstr "" +msgstr "interp didefinisikan untuk iterable 1D dengan panjang yang sama" #: shared-bindings/_bleio/Adapter.c #, c-format msgid "interval must be in range %s-%s" -msgstr "" +msgstr "interval harus dalam rentang %s-%s" #: py/compile.c msgid "invalid arch" -msgstr "" +msgstr "arsitektur tidak valid" #: shared-bindings/bitmaptools/__init__.c #, c-format msgid "invalid bits_per_pixel %d, must be, 1, 2, 4, 8, 16, 24, or 32" -msgstr "" +msgstr "bits_per_pixel %d tidak valid, harus 1, 2, 4, 8, 16, 24, atau 32" #: shared-module/ssl/SSLSocket.c msgid "invalid cert" @@ -3477,24 +3486,24 @@ msgstr "cert tidak valid" #: shared-bindings/bitmaptools/__init__.c #, c-format msgid "invalid element size %d for bits_per_pixel %d\n" -msgstr "" +msgstr "element size %d tidak valid untuk bits_per_pixel %d\n" #: shared-bindings/bitmaptools/__init__.c #, c-format msgid "invalid element_size %d, must be, 1, 2, or 4" -msgstr "" +msgstr "element_size %d tidak valid, harus 1, 2, atau 4" #: shared-bindings/traceback/__init__.c msgid "invalid exception" -msgstr "" +msgstr "exception tidak valid" #: py/objstr.c msgid "invalid format specifier" -msgstr "" +msgstr "spesifier format tidak valid" #: shared-bindings/wifi/Radio.c msgid "invalid hostname" -msgstr "" +msgstr "hostname tidak valid" #: shared-module/ssl/SSLSocket.c msgid "invalid key" @@ -3506,11 +3515,11 @@ msgstr "micropython decorator tidak valid" #: ports/espressif/common-hal/espcamera/Camera.c msgid "invalid setting" -msgstr "" +msgstr "pengaturan tidak valid" #: shared-bindings/random/__init__.c msgid "invalid step" -msgstr "" +msgstr "step tidak valid" #: py/compile.c py/parse.c msgid "invalid syntax" @@ -3518,40 +3527,42 @@ msgstr "syntax tidak valid" #: py/parsenum.c msgid "invalid syntax for integer" -msgstr "" +msgstr "sintaks tidak valid untuk integer" #: py/parsenum.c #, c-format msgid "invalid syntax for integer with base %d" -msgstr "" +msgstr "sintaks tidak valid untuk integer dengan basis %d" #: py/parsenum.c msgid "invalid syntax for number" -msgstr "" +msgstr "sintaks tidak valid untuk angka" #: py/objtype.c msgid "issubclass() arg 1 must be a class" -msgstr "" +msgstr "argumen 1 issubclass() harus berupa class" #: py/objtype.c msgid "issubclass() arg 2 must be a class or a tuple of classes" -msgstr "" +msgstr "argumen 2 issubclass() harus berupa class atau tuple class" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "iterations did not converge" -msgstr "" +msgstr "iterasi tidak konvergen" #: py/objstr.c msgid "join expects a list of str/bytes objects consistent with self object" -msgstr "" +msgstr "join mengharapkan list objek str/bytes yang konsisten dengan objek self" #: py/argcheck.c msgid "keyword argument(s) not implemented - use normal args instead" msgstr "" +"argumen keyword tidak diimplementasikan - gunakan argumen normal sebagai " +"gantinya" #: py/emitinlinethumb.c py/emitinlinextensa.c msgid "label '%q' not defined" -msgstr "" +msgstr "label '%q' tidak didefinisikan" #: py/compile.c msgid "label redefined" @@ -3559,101 +3570,101 @@ msgstr "label didefinis ulang" #: py/objarray.c msgid "lhs and rhs should be compatible" -msgstr "" +msgstr "lhs dan rhs harus kompatibel" #: py/emitnative.c msgid "local '%q' has type '%q' but source is '%q'" -msgstr "" +msgstr "lokal '%q' memiliki tipe '%q' tetapi sumber adalah '%q'" #: py/emitnative.c msgid "local '%q' used before type known" -msgstr "" +msgstr "lokal '%q' digunakan sebelum tipe diketahui" #: py/vm.c msgid "local variable referenced before assignment" -msgstr "" +msgstr "variabel lokal direferensikan sebelum penetapan" #: ports/espressif/common-hal/canio/CAN.c msgid "loopback + silent mode not supported by peripheral" -msgstr "" +msgstr "mode loopback + silent tidak didukung oleh periferal" #: ports/espressif/common-hal/mdns/Server.c #: ports/raspberrypi/common-hal/mdns/Server.c msgid "mDNS already initialized" -msgstr "" +msgstr "mDNS sudah diinisialisasi" #: ports/espressif/common-hal/mdns/Server.c #: ports/raspberrypi/common-hal/mdns/Server.c msgid "mDNS only works with built-in WiFi" -msgstr "" +msgstr "mDNS hanya bekerja dengan WiFi bawaan" #: py/parse.c msgid "malformed f-string" -msgstr "" +msgstr "f-string salah bentuk" #: shared-bindings/_stage/Layer.c msgid "map buffer too small" -msgstr "" +msgstr "buffer peta terlalu kecil" #: py/modmath.c shared-bindings/math/__init__.c msgid "math domain error" -msgstr "" +msgstr "kesalahan domain matematika" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "matrix is not positive definite" -msgstr "" +msgstr "matriks tidak definit positif" #: ports/espressif/common-hal/_bleio/Descriptor.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c #, c-format msgid "max_length must be 0-%d when fixed_length is %s" -msgstr "" +msgstr "max_length harus 0-%d ketika fixed_length adalah %s" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/random/random.c msgid "maximum number of dimensions is " -msgstr "" +msgstr "jumlah maksimum dimensi adalah " #: py/runtime.c msgid "maximum recursion depth exceeded" -msgstr "" +msgstr "kedalaman rekursi maksimum terlampaui" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "maxiter must be > 0" -msgstr "" +msgstr "maxiter harus > 0" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "maxiter should be > 0" -msgstr "" +msgstr "maxiter harus > 0" #: extmod/ulab/code/numpy/numerical.c msgid "median argument must be an ndarray" -msgstr "" +msgstr "argumen median harus berupa ndarray" #: py/runtime.c #, c-format msgid "memory allocation failed, allocating %u bytes" -msgstr "" +msgstr "alokasi memori gagal, mengalokasikan %u byte" #: py/runtime.c msgid "memory allocation failed, heap is locked" -msgstr "" +msgstr "alokasi memori gagal, heap terkunci" #: py/objarray.c msgid "memoryview offset too large" -msgstr "" +msgstr "offset memoryview terlalu besar" #: py/objarray.c msgid "memoryview: length is not a multiple of itemsize" -msgstr "" +msgstr "memoryview: panjang bukan kelipatan dari itemsize" #: extmod/modtime.c msgid "mktime needs a tuple of length 8 or 9" -msgstr "" +msgstr "mktime memerlukan tuple dengan panjang 8 atau 9" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "mode must be complete, or reduced" -msgstr "" +msgstr "mode harus complete, atau reduced" #: py/builtinimport.c msgid "module not found" @@ -3661,11 +3672,11 @@ msgstr "modul tidak ditemukan" #: ports/espressif/common-hal/wifi/Monitor.c msgid "monitor init failed" -msgstr "" +msgstr "inisialisasi monitor gagal" #: extmod/ulab/code/numpy/poly.c msgid "more degrees of freedom than data points" -msgstr "" +msgstr "lebih banyak derajat kebebasan daripada titik data" #: py/compile.c msgid "multiple *x in assignment" @@ -3673,76 +3684,76 @@ msgstr "perkalian *x dalam assignment" #: py/objtype.c msgid "multiple bases have instance lay-out conflict" -msgstr "" +msgstr "beberapa basis memiliki konflik tata letak instance" #: py/objtype.c msgid "multiple inheritance not supported" -msgstr "" +msgstr "pewarisan berganda tidak didukung" #: py/emitnative.c msgid "must raise an object" -msgstr "" +msgstr "harus memunculkan objek" #: py/modbuiltins.c msgid "must use keyword argument for key function" -msgstr "" +msgstr "harus menggunakan argumen keyword untuk fungsi kunci" #: py/runtime.c msgid "name '%q' isn't defined" -msgstr "" +msgstr "nama '%q' tidak didefinisikan" #: py/runtime.c msgid "name not defined" -msgstr "" +msgstr "nama tidak didefinisikan" #: py/qstr.c msgid "name too long" -msgstr "" +msgstr "nama terlalu panjang" #: py/persistentcode.c msgid "native code in .mpy unsupported" -msgstr "" +msgstr "kode native dalam .mpy tidak didukung" #: py/asmthumb.c msgid "native method too big" -msgstr "" +msgstr "metode native terlalu besar" #: py/emitnative.c msgid "native yield" -msgstr "" +msgstr "yield native" #: extmod/ulab/code/ndarray.c msgid "ndarray length overflows" -msgstr "" +msgstr "panjang ndarray meluap" #: py/runtime.c #, c-format msgid "need more than %d values to unpack" -msgstr "" +msgstr "memerlukan lebih dari %d nilai untuk membongkar" #: py/modmath.c msgid "negative factorial" -msgstr "" +msgstr "faktorial negatif" #: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative power with no float support" -msgstr "" +msgstr "pangkat negatif tanpa dukungan float" #: py/objint_mpz.c py/runtime.c msgid "negative shift count" -msgstr "" +msgstr "jumlah shift negatif" #: shared-bindings/_pixelmap/PixelMap.c msgid "nested index must be int" -msgstr "" +msgstr "indeks bersarang harus int" #: shared-module/sdcardio/SDCard.c msgid "no SD card" -msgstr "" +msgstr "tidak ada kartu SD" #: py/vm.c msgid "no active exception to reraise" -msgstr "" +msgstr "tidak ada exception aktif untuk dimunculkan kembali" #: py/compile.c msgid "no binding for nonlocal found" @@ -3750,11 +3761,11 @@ msgstr "tidak ada ikatan/bind pada temuan nonlocal" #: shared-module/msgpack/__init__.c msgid "no default packer" -msgstr "" +msgstr "tidak ada packer default" #: extmod/modrandom.c extmod/ulab/code/numpy/random/random.c msgid "no default seed" -msgstr "" +msgstr "tidak ada seed default" #: py/builtinimport.c msgid "no module named '%q'" @@ -3762,16 +3773,16 @@ msgstr "tidak ada modul yang bernama '%q'" #: shared-module/sdcardio/SDCard.c msgid "no response from SD card" -msgstr "" +msgstr "tidak ada respons dari kartu SD" #: ports/espressif/common-hal/espcamera/Camera.c py/objobject.c py/runtime.c msgid "no such attribute" -msgstr "" +msgstr "atribut tidak ada" #: ports/espressif/common-hal/_bleio/Connection.c #: ports/nordic/common-hal/_bleio/Connection.c msgid "non-UUID found in service_uuids_whitelist" -msgstr "" +msgstr "non-UUID ditemukan dalam service_uuids_whitelist" #: py/compile.c msgid "non-default argument follows default argument" @@ -3783,19 +3794,19 @@ msgstr "digit non-hex ditemukan" #: ports/nordic/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" -msgstr "" +msgstr "timeout non-nol harus > 0.01" #: shared-bindings/_bleio/Adapter.c msgid "non-zero timeout must be >= interval" -msgstr "" +msgstr "timeout non-nol harus >= interval" #: shared-bindings/_bleio/UUID.c msgid "not a 128-bit UUID" -msgstr "" +msgstr "bukan UUID 128-bit" #: py/parse.c msgid "not a constant" -msgstr "" +msgstr "bukan konstanta" #: py/objstr.c msgid "not all arguments converted during string formatting" @@ -3807,65 +3818,65 @@ msgstr "" #: extmod/ulab/code/numpy/carray/carray_tools.c msgid "not implemented for complex dtype" -msgstr "" +msgstr "tidak diimplementasikan untuk dtype complex" #: extmod/ulab/code/numpy/bitwise.c msgid "not supported for input types" -msgstr "" +msgstr "tidak didukung untuk tipe input" #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" -msgstr "" +msgstr "jumlah titik harus setidaknya 2" #: py/builtinhelp.c msgid "object " -msgstr "" +msgstr "objek " #: py/obj.c #, c-format msgid "object '%s' isn't a tuple or list" -msgstr "" +msgstr "objek '%s' bukan tuple atau list" #: py/obj.c msgid "object doesn't support item assignment" -msgstr "" +msgstr "objek tidak mendukung penetapan item" #: py/obj.c msgid "object doesn't support item deletion" -msgstr "" +msgstr "objek tidak mendukung penghapusan item" #: py/obj.c msgid "object has no len" -msgstr "" +msgstr "objek tidak memiliki len" #: py/obj.c msgid "object isn't subscriptable" -msgstr "" +msgstr "objek tidak dapat disubskripsikan" #: py/runtime.c msgid "object not an iterator" -msgstr "" +msgstr "objek bukan iterator" #: py/objtype.c py/runtime.c msgid "object not callable" -msgstr "" +msgstr "objek tidak dapat dipanggil" #: py/sequence.c shared-bindings/displayio/Group.c msgid "object not in sequence" -msgstr "" +msgstr "objek tidak dalam urutan" #: py/runtime.c msgid "object not iterable" -msgstr "" +msgstr "objek tidak dapat diiterasi" #: py/obj.c #, c-format msgid "object of type '%s' has no len()" -msgstr "" +msgstr "objek bertipe '%s' tidak memiliki len()" #: py/obj.c msgid "object with buffer protocol required" -msgstr "" +msgstr "objek dengan protokol buffer diperlukan" #: py/objstr.c msgid "odd-length string" @@ -3873,146 +3884,146 @@ msgstr "panjang data string memiliki keganjilan (odd-length)" #: supervisor/shared/web_workflow/web_workflow.c msgid "off" -msgstr "" +msgstr "mati" #: extmod/ulab/code/utils/utils.c msgid "offset is too large" -msgstr "" +msgstr "offset terlalu besar" #: shared-bindings/dualbank/__init__.c msgid "offset must be >= 0" -msgstr "" +msgstr "offset harus >= 0" #: extmod/ulab/code/numpy/create.c msgid "offset must be non-negative and no greater than buffer length" -msgstr "" +msgstr "offset harus non-negatif dan tidak lebih besar dari panjang buffer" #: ports/nordic/common-hal/audiobusio/PDMIn.c #: ports/stm/common-hal/audiobusio/PDMIn.c msgid "only bit_depth=16 is supported" -msgstr "" +msgstr "hanya bit_depth=16 yang didukung" #: ports/stm/common-hal/audiobusio/PDMIn.c msgid "only mono is supported" -msgstr "" +msgstr "hanya mono yang didukung" #: extmod/ulab/code/numpy/create.c msgid "only ndarrays can be concatenated" -msgstr "" +msgstr "hanya ndarray yang dapat digabungkan" #: ports/stm/common-hal/audiobusio/PDMIn.c msgid "only oversample=64 is supported" -msgstr "" +msgstr "hanya oversample=64 yang didukung" #: ports/nordic/common-hal/audiobusio/PDMIn.c #: ports/stm/common-hal/audiobusio/PDMIn.c msgid "only sample_rate=16000 is supported" -msgstr "" +msgstr "hanya sample_rate=16000 yang didukung" #: py/objarray.c py/objstr.c py/objstrunicode.c py/objtuple.c #: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c #: shared-bindings/nvm/ByteArray.c msgid "only slices with step=1 (aka None) are supported" -msgstr "" +msgstr "hanya slice dengan step=1 (alias None) yang didukung" #: py/vm.c msgid "opcode" -msgstr "" +msgstr "opcode" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/bitwise.c #: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/vector.c msgid "operands could not be broadcast together" -msgstr "" +msgstr "operan tidak dapat di-broadcast bersama" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "operation is defined for 2D arrays only" -msgstr "" +msgstr "operasi didefinisikan hanya untuk array 2D" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "operation is defined for ndarrays only" -msgstr "" +msgstr "operasi didefinisikan hanya untuk ndarray" #: extmod/ulab/code/ndarray.c msgid "operation is implemented for 1D Boolean arrays only" -msgstr "" +msgstr "operasi diimplementasikan hanya untuk array Boolean 1D" #: extmod/ulab/code/numpy/numerical.c msgid "operation is not implemented on ndarrays" -msgstr "" +msgstr "operasi tidak diimplementasikan pada ndarray" #: extmod/ulab/code/ndarray.c msgid "operation is not supported for given type" -msgstr "" +msgstr "operasi tidak didukung untuk tipe yang diberikan" #: extmod/ulab/code/ndarray_operators.c msgid "operation not supported for the input types" -msgstr "" +msgstr "operasi tidak didukung untuk tipe input" #: py/modbuiltins.c msgid "ord expects a character" -msgstr "" +msgstr "ord mengharapkan karakter" #: py/modbuiltins.c #, c-format msgid "ord() expected a character, but string of length %d found" -msgstr "" +msgstr "ord() mengharapkan karakter, tetapi string dengan panjang %d ditemukan" #: extmod/ulab/code/utils/utils.c msgid "out array is too small" -msgstr "" +msgstr "array out terlalu kecil" #: extmod/ulab/code/numpy/random/random.c msgid "out has wrong type" -msgstr "" +msgstr "out memiliki tipe yang salah" #: extmod/ulab/code/numpy/vector.c msgid "out keyword is not supported for complex dtype" -msgstr "" +msgstr "keyword out tidak didukung untuk dtype complex" #: extmod/ulab/code/numpy/vector.c msgid "out keyword is not supported for function" -msgstr "" +msgstr "keyword out tidak didukung untuk fungsi" #: extmod/ulab/code/utils/utils.c msgid "out must be a float dense array" -msgstr "" +msgstr "out harus berupa array padat float" #: extmod/ulab/code/numpy/vector.c msgid "out must be an ndarray" -msgstr "" +msgstr "out harus berupa ndarray" #: extmod/ulab/code/numpy/vector.c msgid "out must be of float dtype" -msgstr "" +msgstr "out harus bertipe dtype float" #: shared-bindings/bitmaptools/__init__.c msgid "out of range of target" -msgstr "" +msgstr "di luar jangkauan target" #: extmod/ulab/code/numpy/random/random.c msgid "output array has wrong type" -msgstr "" +msgstr "array output memiliki tipe yang salah" #: extmod/ulab/code/numpy/random/random.c msgid "output array must be contiguous" -msgstr "" +msgstr "array output harus berdekatan" #: py/objint_mpz.c msgid "overflow converting long int to machine word" -msgstr "" +msgstr "overflow saat mengonversi long int ke machine word" #: py/modstruct.c #, c-format msgid "pack expected %d items for packing (got %d)" -msgstr "" +msgstr "pack mengharapkan %d item untuk packing (mendapat %d)" #: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c msgid "palette must be 32 bytes long" -msgstr "" +msgstr "palet harus sepanjang 32 byte" #: py/emitinlinextensa.c msgid "parameters must be registers in sequence a2 to a5" -msgstr "" +msgstr "parameter harus berupa register dalam urutan a2 hingga a5" #: py/emitinlinethumb.c msgid "parameters must be registers in sequence r0 to r3" @@ -4020,7 +4031,7 @@ msgstr "parameter harus menjadi register dalam urutan r0 sampai r3" #: extmod/vfs_posix_file.c msgid "poll on file not available on win32" -msgstr "" +msgstr "poll pada file tidak tersedia di win32" #: ports/espressif/common-hal/pulseio/PulseIn.c msgid "pop from an empty PulseIn" @@ -4033,31 +4044,31 @@ msgstr "Muncul dari PulseIn yang kosong" #: ports/stm/common-hal/pulseio/PulseIn.c py/objdict.c py/objlist.c py/objset.c #: shared-bindings/ps2io/Ps2.c msgid "pop from empty %q" -msgstr "" +msgstr "pop dari %q kosong" #: shared-bindings/socketpool/Socket.c msgid "port must be >= 0" -msgstr "" +msgstr "port harus >= 0" #: py/compile.c msgid "positional arg after **" -msgstr "" +msgstr "argumen posisional setelah **" #: py/compile.c msgid "positional arg after keyword arg" -msgstr "" +msgstr "argumen posisional setelah argumen keyword" #: py/objint_mpz.c msgid "pow() 3rd argument cannot be 0" -msgstr "" +msgstr "argumen ketiga pow() tidak boleh 0" #: py/objint_mpz.c msgid "pow() with 3 arguments requires integers" -msgstr "" +msgstr "pow() dengan 3 argumen memerlukan integer" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "pull masks conflict with direction masks" -msgstr "" +msgstr "mask pull berkonflik dengan mask arah" #: py/parse.c msgid "raw f-strings are not supported" @@ -4065,7 +4076,7 @@ msgstr "" #: extmod/ulab/code/numpy/fft/fft_tools.c msgid "real and imaginary parts must be of equal length" -msgstr "" +msgstr "bagian real dan imajiner harus memiliki panjang yang sama" #: py/builtinimport.c msgid "relative import" @@ -4074,11 +4085,11 @@ msgstr "impor relatif" #: py/obj.c #, c-format msgid "requested length %d but object has length %d" -msgstr "" +msgstr "panjang yang diminta %d tetapi objek memiliki panjang %d" #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" -msgstr "" +msgstr "hasil tidak dapat di-cast ke tipe yang ditentukan" #: py/compile.c msgid "return annotation must be an identifier" @@ -4086,29 +4097,29 @@ msgstr "anotasi return harus sebuah identifier" #: py/emitnative.c msgid "return expected '%q' but got '%q'" -msgstr "" +msgstr "return mengharapkan '%q' tetapi mendapat '%q'" #: shared-bindings/rgbmatrix/RGBMatrix.c #, c-format msgid "rgb_pins[%d] duplicates another pin assignment" -msgstr "" +msgstr "rgb_pins[%d] menduplikasi penetapan pin lain" #: shared-bindings/rgbmatrix/RGBMatrix.c #, c-format msgid "rgb_pins[%d] is not on the same port as clock" -msgstr "" +msgstr "rgb_pins[%d] tidak pada port yang sama dengan clock" #: extmod/ulab/code/numpy/numerical.c msgid "roll argument must be an ndarray" -msgstr "" +msgstr "argumen roll harus berupa ndarray" #: py/objstr.c msgid "rsplit(None,n)" -msgstr "" +msgstr "rsplit(None,n)" #: shared-bindings/audiofreeverb/Freeverb.c msgid "samples_signed must be true" -msgstr "" +msgstr "samples_signed harus true" #: ports/atmel-samd/common-hal/audiobusio/PDMIn.c #: ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -4117,7 +4128,7 @@ msgstr "nilai sampling keluar dari jangkauan" #: py/modmicropython.c msgid "schedule queue full" -msgstr "" +msgstr "antrian jadwal penuh" #: py/builtinimport.c msgid "script compilation not supported" @@ -4125,43 +4136,43 @@ msgstr "kompilasi script tidak didukung" #: py/nativeglue.c msgid "set unsupported" -msgstr "" +msgstr "set tidak didukung" #: extmod/ulab/code/numpy/random/random.c msgid "shape must be None, and integer or a tuple of integers" -msgstr "" +msgstr "shape harus None, dan integer atau tuple integer" #: extmod/ulab/code/ndarray.c msgid "shape must be integer or tuple of integers" -msgstr "" +msgstr "shape harus integer atau tuple integer" #: shared-module/msgpack/__init__.c msgid "short read" -msgstr "" +msgstr "pembacaan pendek" #: py/objstr.c msgid "sign not allowed in string format specifier" -msgstr "" +msgstr "tanda tidak diperbolehkan dalam spesifier format string" #: py/objstr.c msgid "sign not allowed with integer format specifier 'c'" -msgstr "" +msgstr "tanda tidak diperbolehkan dengan spesifier format integer 'c'" #: extmod/ulab/code/ulab_tools.c msgid "size is defined for ndarrays only" -msgstr "" +msgstr "size didefinisikan hanya untuk ndarray" #: extmod/ulab/code/numpy/random/random.c msgid "size must match out.shape when used together" -msgstr "" +msgstr "size harus cocok dengan out.shape ketika digunakan bersama" #: py/nativeglue.c msgid "slice unsupported" -msgstr "" +msgstr "slice tidak didukung" #: py/objint.c py/sequence.c msgid "small int overflow" -msgstr "" +msgstr "overflow int kecil" #: main.c msgid "soft reboot\n" @@ -4169,39 +4180,39 @@ msgstr "memulai ulang software(soft reboot)\n" #: extmod/ulab/code/numpy/numerical.c msgid "sort argument must be an ndarray" -msgstr "" +msgstr "argumen sort harus berupa ndarray" #: extmod/ulab/code/scipy/signal/signal.c msgid "sos array must be of shape (n_section, 6)" -msgstr "" +msgstr "array sos harus berbentuk (n_section, 6)" #: extmod/ulab/code/scipy/signal/signal.c msgid "sos[:, 3] should be all ones" -msgstr "" +msgstr "sos[:, 3] harus semuanya satu" #: extmod/ulab/code/scipy/signal/signal.c msgid "sosfilt requires iterable arguments" -msgstr "" +msgstr "sosfilt memerlukan argumen yang dapat diiterasi" #: shared-bindings/bitmaptools/__init__.c msgid "source palette too large" -msgstr "" +msgstr "palet sumber terlalu besar" #: shared-bindings/bitmaptools/__init__.c msgid "source_bitmap must have value_count of 2 or 65536" -msgstr "" +msgstr "source_bitmap harus memiliki value_count 2 atau 65536" #: shared-bindings/bitmaptools/__init__.c msgid "source_bitmap must have value_count of 65536" -msgstr "" +msgstr "source_bitmap harus memiliki value_count 65536" #: shared-bindings/bitmaptools/__init__.c msgid "source_bitmap must have value_count of 8" -msgstr "" +msgstr "source_bitmap harus memiliki value_count 8" #: extmod/modre.c msgid "splitting with sub-captures" -msgstr "" +msgstr "pemisahan dengan sub-capture" #: py/objstr.c msgid "start/end indices" @@ -4209,28 +4220,28 @@ msgstr "" #: shared-bindings/random/__init__.c msgid "stop not reachable from start" -msgstr "" +msgstr "stop tidak dapat dijangkau dari start" #: py/stream.c shared-bindings/getpass/__init__.c msgid "stream operation not supported" -msgstr "" +msgstr "operasi stream tidak didukung" #: py/objarray.c py/objstr.c msgid "string argument without an encoding" -msgstr "" +msgstr "argumen string tanpa encoding" #: py/objstrunicode.c msgid "string index out of range" -msgstr "" +msgstr "indeks string di luar jangkauan" #: py/objstrunicode.c #, c-format msgid "string indices must be integers, not %s" -msgstr "" +msgstr "indeks string harus integer, bukan %s" #: py/objarray.c py/objstr.c msgid "substring not found" -msgstr "" +msgstr "substring tidak ditemukan" #: py/compile.c msgid "super() can't find self" @@ -4242,87 +4253,87 @@ msgstr "sintaksis error pada JSON" #: extmod/modtime.c msgid "ticks interval overflow" -msgstr "" +msgstr "overflow interval tick" #: ports/nordic/common-hal/watchdog/WatchDogTimer.c msgid "timeout duration exceeded the maximum supported value" -msgstr "" +msgstr "durasi timeout melebihi nilai maksimum yang didukung" #: ports/nordic/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" -msgstr "" +msgstr "timeout harus < 655.35 detik" #: ports/raspberrypi/common-hal/floppyio/__init__.c msgid "timeout waiting for flux" -msgstr "" +msgstr "timeout menunggu flux" #: ports/raspberrypi/common-hal/floppyio/__init__.c #: shared-module/floppyio/__init__.c msgid "timeout waiting for index pulse" -msgstr "" +msgstr "timeout menunggu pulsa indeks" #: shared-module/sdcardio/SDCard.c msgid "timeout waiting for v1 card" -msgstr "" +msgstr "timeout menunggu kartu v1" #: shared-module/sdcardio/SDCard.c msgid "timeout waiting for v2 card" -msgstr "" +msgstr "timeout menunggu kartu v2" #: ports/stm/common-hal/pwmio/PWMOut.c msgid "timer re-init" -msgstr "" +msgstr "inisialisasi ulang timer" #: shared-bindings/time/__init__.c msgid "timestamp out of range for platform time_t" -msgstr "" +msgstr "timestamp di luar jangkauan untuk time_t platform" #: extmod/ulab/code/ndarray.c msgid "tobytes can be invoked for dense arrays only" -msgstr "" +msgstr "tobytes hanya dapat dipanggil untuk array padat" #: py/compile.c msgid "too many args" -msgstr "" +msgstr "terlalu banyak argumen" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/create.c msgid "too many dimensions" -msgstr "" +msgstr "terlalu banyak dimensi" #: extmod/ulab/code/ndarray.c msgid "too many indices" -msgstr "" +msgstr "terlalu banyak indeks" #: py/asmthumb.c msgid "too many locals for native method" -msgstr "" +msgstr "terlalu banyak lokal untuk metode native" #: py/runtime.c #, c-format msgid "too many values to unpack (expected %d)" -msgstr "" +msgstr "terlalu banyak nilai untuk dibongkar (diharapkan %d)" #: extmod/ulab/code/numpy/approx.c msgid "trapz is defined for 1D arrays of equal length" -msgstr "" +msgstr "trapz didefinisikan untuk array 1D dengan panjang yang sama" #: extmod/ulab/code/numpy/approx.c msgid "trapz is defined for 1D iterables" -msgstr "" +msgstr "trapz didefinisikan untuk iterable 1D" #: py/obj.c msgid "tuple/list has wrong length" -msgstr "" +msgstr "tuple/list memiliki panjang yang salah" #: ports/espressif/common-hal/canio/CAN.c #, c-format msgid "twai_driver_install returned esp-idf error #%d" -msgstr "" +msgstr "twai_driver_install mengembalikan kesalahan esp-idf #%d" #: ports/espressif/common-hal/canio/CAN.c #, c-format msgid "twai_start returned esp-idf error #%d" -msgstr "" +msgstr "twai_start mengembalikan kesalahan esp-idf #%d" #: shared-bindings/busio/UART.c shared-bindings/canio/CAN.c msgid "tx and rx cannot both be None" @@ -4330,27 +4341,27 @@ msgstr "tx dan rx keduanya tidak boleh kosong" #: py/objtype.c msgid "type '%q' isn't an acceptable base type" -msgstr "" +msgstr "tipe '%q' bukan tipe basis yang dapat diterima" #: py/objtype.c msgid "type isn't an acceptable base type" -msgstr "" +msgstr "tipe bukan tipe basis yang dapat diterima" #: py/runtime.c msgid "type object '%q' has no attribute '%q'" -msgstr "" +msgstr "objek tipe '%q' tidak memiliki atribut '%q'" #: py/objtype.c msgid "type takes 1 or 3 arguments" -msgstr "" +msgstr "type memerlukan 1 atau 3 argumen" #: py/objint_longlong.c msgid "ulonglong too large" -msgstr "" +msgstr "ulonglong terlalu besar" #: py/parse.c msgid "unexpected indent" -msgstr "" +msgstr "indentasi tak terduga" #: py/bc.c msgid "unexpected keyword argument" @@ -4363,20 +4374,20 @@ msgstr "keyword argumen '%q' tidak diharapkan" #: py/lexer.c msgid "unicode name escapes" -msgstr "" +msgstr "escape nama unicode" #: py/parse.c msgid "unindent doesn't match any outer indent level" -msgstr "" +msgstr "unindent tidak cocok dengan tingkat indentasi luar mana pun" #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" -msgstr "" +msgstr "spesifier konversi tidak dikenal %c" #: py/objstr.c msgid "unknown format code '%c' for object of type '%q'" -msgstr "" +msgstr "kode format tidak dikenal '%c' untuk objek bertipe '%q'" #: py/compile.c msgid "unknown type" @@ -4384,100 +4395,102 @@ msgstr "tipe tidak diketahui" #: py/compile.c msgid "unknown type '%q'" -msgstr "" +msgstr "tipe tidak dikenal '%q'" #: py/objstr.c #, c-format msgid "unmatched '%c' in format" -msgstr "" +msgstr "'%c' tidak cocok dalam format" #: py/objtype.c py/runtime.c msgid "unreadable attribute" -msgstr "" +msgstr "atribut tidak dapat dibaca" #: shared-bindings/displayio/TileGrid.c shared-bindings/terminalio/Terminal.c #: shared-bindings/tilepalettemapper/TilePaletteMapper.c #: shared-bindings/vectorio/VectorShape.c msgid "unsupported %q type" -msgstr "" +msgstr "tipe %q tidak didukung" #: py/emitinlinethumb.c #, c-format msgid "unsupported Thumb instruction '%s' with %d arguments" -msgstr "" +msgstr "instruksi Thumb tidak didukung '%s' dengan %d argumen" #: py/emitinlinextensa.c #, c-format msgid "unsupported Xtensa instruction '%s' with %d arguments" -msgstr "" +msgstr "instruksi Xtensa tidak didukung '%s' dengan %d argumen" #: shared-module/bitmapfilter/__init__.c msgid "unsupported bitmap depth" -msgstr "" +msgstr "kedalaman bitmap tidak didukung" #: shared-module/gifio/GifWriter.c msgid "unsupported colorspace for GifWriter" -msgstr "" +msgstr "colorspace tidak didukung untuk GifWriter" #: shared-bindings/bitmaptools/__init__.c msgid "unsupported colorspace for dither" -msgstr "" +msgstr "colorspace tidak didukung untuk dither" #: py/objstr.c #, c-format msgid "unsupported format character '%c' (0x%x) at index %d" -msgstr "" +msgstr "karakter format tidak didukung '%c' (0x%x) pada indeks %d" #: py/runtime.c msgid "unsupported type for %q: '%s'" -msgstr "" +msgstr "tipe tidak didukung untuk %q: '%s'" #: py/runtime.c msgid "unsupported type for operator" -msgstr "" +msgstr "tipe tidak didukung untuk operator" #: py/runtime.c msgid "unsupported types for %q: '%q', '%q'" -msgstr "" +msgstr "tipe tidak didukung untuk %q: '%q', '%q'" #: extmod/ulab/code/numpy/io/io.c msgid "usecols is too high" -msgstr "" +msgstr "usecols terlalu tinggi" #: extmod/ulab/code/numpy/io/io.c msgid "usecols keyword must be specified" -msgstr "" +msgstr "keyword usecols harus ditentukan" #: py/objint.c #, c-format msgid "value must fit in %d byte(s)" -msgstr "" +msgstr "nilai harus muat dalam %d byte" #: shared-bindings/bitmaptools/__init__.c msgid "value out of range of target" -msgstr "" +msgstr "nilai di luar jangkauan target" #: extmod/moddeflate.c msgid "wbits" -msgstr "" +msgstr "wbit" #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " "or 25)" msgstr "" +"weights harus berupa urutan dengan jumlah persegi ganjil elemen (biasanya 9 " +"atau 25)" #: shared-bindings/bitmapfilter/__init__.c msgid "weights must be an object of type %q, %q, %q, or %q, not %q " -msgstr "" +msgstr "weights harus berupa objek bertipe %q, %q, %q, atau %q, bukan %q " #: shared-bindings/is31fl3741/FrameBuffer.c msgid "width must be greater than zero" -msgstr "" +msgstr "lebar harus lebih besar dari nol" #: ports/raspberrypi/common-hal/wifi/Monitor.c msgid "wifi.Monitor not available" -msgstr "" +msgstr "wifi.Monitor tidak tersedia" #: shared-bindings/_bleio/Adapter.c msgid "window must be <= interval" @@ -4493,11 +4506,11 @@ msgstr "sumbu yang ditentukan salah" #: extmod/ulab/code/numpy/io/io.c msgid "wrong dtype" -msgstr "" +msgstr "dtype salah" #: extmod/ulab/code/numpy/transform.c msgid "wrong index type" -msgstr "" +msgstr "tipe indeks salah" #: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/create.c #: extmod/ulab/code/numpy/io/io.c extmod/ulab/code/numpy/transform.c @@ -4507,11 +4520,11 @@ msgstr "tipe input salah" #: extmod/ulab/code/numpy/transform.c msgid "wrong length of condition array" -msgstr "" +msgstr "panjang array kondisi salah" #: extmod/ulab/code/numpy/transform.c msgid "wrong length of index array" -msgstr "" +msgstr "panjang array indeks salah" #: extmod/ulab/code/numpy/create.c py/objarray.c py/objstr.c msgid "wrong number of arguments" From e26fb4cb9dd83df22a91cb844a637b204987718a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 13 Feb 2026 15:58:36 -0500 Subject: [PATCH 326/517] shrink pygamer board --- ports/atmel-samd/boards/pygamer/mpconfigboard.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/atmel-samd/boards/pygamer/mpconfigboard.mk b/ports/atmel-samd/boards/pygamer/mpconfigboard.mk index 19fa4f19b9de6..0c6f16f8b6381 100644 --- a/ports/atmel-samd/boards/pygamer/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pygamer/mpconfigboard.mk @@ -12,6 +12,7 @@ LONGINT_IMPL = MPZ CIRCUITPY_AESIO = 0 CIRCUITPY_FLOPPYIO = 0 +CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_GIFIO = 0 CIRCUITPY_I2CDISPLAYBUS = 0 From 4b2111e10b0f7c04bb98de16bfa96cac082c14c7 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sat, 14 Feb 2026 20:10:00 -0500 Subject: [PATCH 327/517] os.getenv() now always returns str; add supervisor.get_setting() to fetch typed objects --- devices/ble_hci/common-hal/_bleio/Adapter.c | 4 +- docs/environment.rst | 4 +- docs/shared_bindings_matrix.py | 3 +- locale/circuitpython.pot | 18 +- main.c | 10 +- ports/atmel-samd/mpconfigport.mk | 2 +- ports/espressif/boards/makerfabs_tft7/board.c | 11 +- .../boards/sunton_esp32_2432S024C/board.c | 11 +- .../boards/sunton_esp32_2432S028/board.c | 11 +- .../boards/sunton_esp32_8048S050/board.c | 7 +- ports/espressif/common-hal/_bleio/Adapter.c | 10 +- ports/nordic/common-hal/_bleio/Adapter.c | 11 +- .../raspberrypi/common-hal/picodvi/__init__.c | 12 +- ports/unix/Makefile | 2 + ports/unix/main.c | 31 -- ports/unix/modos.c | 32 +- .../unix/variants/coverage/mpconfigvariant.mk | 3 +- py/circuitpy_defns.mk | 1 - py/circuitpy_mpconfig.mk | 6 +- shared-bindings/os/__init__.c | 17 +- shared-bindings/supervisor/__init__.c | 67 +++ shared-module/os/__init__.c | 17 + shared-module/os/__init__.h | 21 - shared-module/os/getenv.c | 417 ---------------- supervisor/shared/bluetooth/bluetooth.c | 10 +- supervisor/shared/display.c | 14 +- supervisor/shared/filesystem.c | 2 +- supervisor/shared/settings.c | 463 ++++++++++++++++++ supervisor/shared/settings.h | 53 ++ supervisor/shared/web_workflow/web_workflow.c | 26 +- supervisor/supervisor.mk | 4 + tests/circuitpython/getenv.py | 7 +- tests/circuitpython/getenv.py.exp | 26 +- 33 files changed, 723 insertions(+), 610 deletions(-) delete mode 100644 shared-module/os/getenv.c create mode 100644 supervisor/shared/settings.c create mode 100644 supervisor/shared/settings.h diff --git a/devices/ble_hci/common-hal/_bleio/Adapter.c b/devices/ble_hci/common-hal/_bleio/Adapter.c index fac7eb9f5fddf..03ec7a5588f11 100644 --- a/devices/ble_hci/common-hal/_bleio/Adapter.c +++ b/devices/ble_hci/common-hal/_bleio/Adapter.c @@ -29,7 +29,7 @@ #include "shared-bindings/_bleio/ScanEntry.h" #include "shared-bindings/time/__init__.h" -#if CIRCUITPY_OS_GETENV +#if CIRCUITPY_SETTINGS_TOML #include "shared-bindings/os/__init__.h" #endif @@ -261,7 +261,7 @@ static void _adapter_set_name(bleio_adapter_obj_t *self, mp_obj_str_t *name_obj) static void bleio_adapter_hci_init(bleio_adapter_obj_t *self) { mp_int_t name_len = 0; - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML mp_obj_t name = common_hal_os_getenv("CIRCUITPY_BLE_NAME", mp_const_none); if (name != mp_const_none) { mp_arg_validate_type_string(name, MP_QSTR_CIRCUITPY_BLE_NAME); diff --git a/docs/environment.rst b/docs/environment.rst index b97820b3caf74..36404eb81f928 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -187,7 +187,7 @@ Allows the entry of a display scaling factor used during the terminalio console The entered scaling factor only affects the terminalio console and has no impact on the UART, Web Workflow, BLE Workflow, etc consoles. -This feature is not enabled on boards that the CIRCUITPY_OS_GETENV (os CIRCUIPTY_FULL_BUILD) +This feature is not enabled on boards that the CIRCUITPY_SETTINGS_TOML (or CIRCUITPY_FULL_BUILD) flag has been set to 0. Currently this is primarily boards with limited flash including some of the Atmel_samd boards based on the SAMD21/M0 microprocessor. @@ -197,7 +197,7 @@ Specifies a custom font file path to use for the terminalio console instead of t ``/fonts/terminal.lvfontbin``. This allows users to create and use custom fonts for the CircuitPython console. -This feature requires both CIRCUITPY_OS_GETENV and CIRCUITPY_LVFONTIO to be enabled. +This feature requires both CIRCUITPY_SETTINGS_TOML and CIRCUITPY_LVFONTIO to be enabled. Example: diff --git a/docs/shared_bindings_matrix.py b/docs/shared_bindings_matrix.py index b4fe4ff7e2464..8fbee4beea5b5 100644 --- a/docs/shared_bindings_matrix.py +++ b/docs/shared_bindings_matrix.py @@ -84,8 +84,9 @@ "keypad.Keys": "CIRCUITPY_KEYPAD_KEYS", "keypad.ShiftRegisterKeys": "CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS", "keypad_demux.DemuxKeyMatrix": "CIRCUITPY_KEYPAD_DEMUX", - "os.getenv": "CIRCUITPY_OS_GETENV", + "os.getenv": "CIRCUITPY_SETTINGS_TOML", "select": "MICROPY_PY_SELECT_SELECT", + "supervisor.get_setting": "CIRCUITPY_SETTINGS_TOML", "sys": "CIRCUITPY_SYS", "terminalio": "CIRCUITPY_DISPLAYIO", "usb": "CIRCUITPY_PYUSB", diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index c135442f6001f..0a776db93db7f 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -583,7 +583,7 @@ msgstr "" msgid "Already scanning for wifi networks" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "" @@ -1092,7 +1092,6 @@ msgid "File exists" msgstr "" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "" @@ -1243,7 +1242,7 @@ msgstr "" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1340,17 +1339,12 @@ msgstr "" msgid "Invalid bits per value" msgstr "" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "" @@ -1381,7 +1375,7 @@ msgstr "" msgid "Invalid state" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "" @@ -1389,10 +1383,6 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "" diff --git a/main.c b/main.c index 7b936488fb794..46b54f522b503 100644 --- a/main.c +++ b/main.c @@ -113,8 +113,8 @@ uint8_t value_out = 0; #endif -#if MICROPY_ENABLE_PYSTACK && CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif static void reset_devices(void) { @@ -132,17 +132,17 @@ static const char line_clear[] = "\x1b[2K\x1b[0G"; #if MICROPY_ENABLE_PYSTACK || MICROPY_ENABLE_GC static uint8_t *_allocate_memory(safe_mode_t safe_mode, const char *env_key, size_t default_size, size_t *final_size) { *final_size = default_size; - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML if (safe_mode == SAFE_MODE_NONE) { mp_int_t size; - if (common_hal_os_getenv_int(env_key, &size) == GETENV_OK && size > 0) { + if (settings_get_int(env_key, &size) == SETTINGS_OK && size > 0) { *final_size = size; } } #endif uint8_t *ptr = port_malloc(*final_size, false); - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML if (ptr == NULL) { // Fallback to the build size. ptr = port_malloc(default_size, false); diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index c095220d0a3f2..b0947ea6f2529 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -48,10 +48,10 @@ CIRCUITPY_I2CTARGET ?= 0 CIRCUITPY_JSON ?= 0 CIRCUITPY_KEYPAD ?= 0 CIRCUITPY_MSGPACK ?= 0 -CIRCUITPY_OS_GETENV ?= 0 CIRCUITPY_PIXELMAP ?= 0 CIRCUITPY_RE ?= 0 CIRCUITPY_SDCARDIO ?= 0 +CIRCUITPY_SETTINGS_TOML ?= 0 CIRCUITPY_SPITARGET ?= 0 CIRCUITPY_SYNTHIO ?= 0 CIRCUITPY_TOUCHIO_USE_NATIVE ?= 1 diff --git a/ports/espressif/boards/makerfabs_tft7/board.c b/ports/espressif/boards/makerfabs_tft7/board.c index dd0ffa2652836..a994cfef38118 100644 --- a/ports/espressif/boards/makerfabs_tft7/board.c +++ b/ports/espressif/boards/makerfabs_tft7/board.c @@ -6,13 +6,14 @@ #include "supervisor/board.h" #include "mpconfigboard.h" + #include "shared-bindings/board/__init__.h" #include "shared-bindings/dotclockframebuffer/DotClockFramebuffer.h" #include "shared-bindings/dotclockframebuffer/__init__.h" #include "shared-bindings/framebufferio/FramebufferDisplay.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/displayio/__init__.h" -#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" static const mcu_pin_obj_t *blue_pins[] = { &pin_GPIO8, @@ -40,14 +41,14 @@ static const mcu_pin_obj_t *red_pins[] = { static void display_init(void) { mp_int_t height = 0, width = 0, frequency = 0; - os_getenv_err_t result; + settings_err_t result; - result = common_hal_os_getenv_int("CIRCUITPY_DISPLAY_WIDTH", &width); - if (result == GETENV_OK && width == 800) { + result = settings_get_int("CIRCUITPY_DISPLAY_WIDTH", &width); + if (result == SETTINGS_OK && width == 800) { width = 800; height = 480; frequency = 6500000; - } else if (result == GETENV_OK && width == 1024) { + } else if (result == SETTINGS_OK && width == 1024) { width = 1024; height = 600; frequency = 10000000; diff --git a/ports/espressif/boards/sunton_esp32_2432S024C/board.c b/ports/espressif/boards/sunton_esp32_2432S024C/board.c index a43b5ad65a032..de7504a871532 100644 --- a/ports/espressif/boards/sunton_esp32_2432S024C/board.c +++ b/ports/espressif/boards/sunton_esp32_2432S024C/board.c @@ -6,13 +6,14 @@ #include "supervisor/board.h" #include "mpconfigboard.h" + +#include "common-hal/microcontroller/Pin.h" +#include "driver/gpio.h" #include "shared-bindings/board/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/displayio/__init__.h" #include "shared-module/displayio/mipi_constants.h" -#include "driver/gpio.h" -#include "common-hal/microcontroller/Pin.h" -#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" uint8_t display_init_sequence[] = { 0x01, 0x80, 0x80, // # Software reset then delay 0x80 (128ms) @@ -59,8 +60,8 @@ static void display_init(void) { busdisplay_busdisplay_obj_t *display = &allocate_display()->display; display->base.type = &busdisplay_busdisplay_type; - os_getenv_err_t result = common_hal_os_getenv_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); - if (result != GETENV_OK) { + settings_err_t result = settings_get_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); + if (result != SETTINGS_OK) { rotation = 0; } diff --git a/ports/espressif/boards/sunton_esp32_2432S028/board.c b/ports/espressif/boards/sunton_esp32_2432S028/board.c index 7380e252410ac..214e3b13e158c 100644 --- a/ports/espressif/boards/sunton_esp32_2432S028/board.c +++ b/ports/espressif/boards/sunton_esp32_2432S028/board.c @@ -6,13 +6,14 @@ #include "supervisor/board.h" #include "mpconfigboard.h" + +#include "common-hal/microcontroller/Pin.h" +#include "driver/gpio.h" #include "shared-bindings/board/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/displayio/__init__.h" #include "shared-module/displayio/mipi_constants.h" -#include "driver/gpio.h" -#include "common-hal/microcontroller/Pin.h" -#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" uint8_t display_init_sequence[] = { 0x01, 0x80, 0x80, // # Software reset then delay 0x80 (128ms) @@ -59,8 +60,8 @@ static void display_init(void) { busdisplay_busdisplay_obj_t *display = &allocate_display()->display; display->base.type = &busdisplay_busdisplay_type; - os_getenv_err_t result = common_hal_os_getenv_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); - if (result != GETENV_OK) { + settings_err_t result = settings_get_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); + if (result != SETTINGS_OK) { rotation = 0; } diff --git a/ports/espressif/boards/sunton_esp32_8048S050/board.c b/ports/espressif/boards/sunton_esp32_8048S050/board.c index e01f857bfd980..a6f573a86e79b 100644 --- a/ports/espressif/boards/sunton_esp32_8048S050/board.c +++ b/ports/espressif/boards/sunton_esp32_8048S050/board.c @@ -6,13 +6,14 @@ #include "supervisor/board.h" #include "mpconfigboard.h" + #include "shared-bindings/board/__init__.h" #include "shared-bindings/dotclockframebuffer/DotClockFramebuffer.h" #include "shared-bindings/dotclockframebuffer/__init__.h" #include "shared-bindings/framebufferio/FramebufferDisplay.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/displayio/__init__.h" -#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" static const mcu_pin_obj_t *blue_pins[] = { &pin_GPIO8, @@ -47,8 +48,8 @@ static void display_init(void) { dotclockframebuffer_framebuffer_obj_t *framebuffer = &allocate_display_bus_or_raise()->dotclock; framebuffer->base.type = &dotclockframebuffer_framebuffer_type; - os_getenv_err_t result = common_hal_os_getenv_int("CIRCUITPY_DISPLAY_FREQUENCY", &frequency); - if (result != GETENV_OK) { + settings_err_t result = settings_get_int("CIRCUITPY_DISPLAY_FREQUENCY", &frequency); + if (result != SETTINGS_OK) { frequency = 12500000; } diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c index 352d01372a91c..327515da4de56 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.c +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -44,8 +44,8 @@ #include "esp_nimble_hci.h" #include "nvs_flash.h" -#if CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif // Status variables used while busy-waiting for events. @@ -105,10 +105,10 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable ble_svc_gatt_init(); ble_svc_ans_init(); - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML char ble_name[1 + MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH]; - os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); - if (result == GETENV_OK) { + settings_err_t result = settings_get_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); + if (result == SETTINGS_OK) { ble_svc_gap_device_name_set(ble_name); } else #endif diff --git a/ports/nordic/common-hal/_bleio/Adapter.c b/ports/nordic/common-hal/_bleio/Adapter.c index 9cf40ae91b3ff..2ca9df897107e 100644 --- a/ports/nordic/common-hal/_bleio/Adapter.c +++ b/ports/nordic/common-hal/_bleio/Adapter.c @@ -32,9 +32,8 @@ #include "supervisor/usb.h" #endif -#if CIRCUITPY_OS_GETENV -#include "shared-bindings/os/__init__.h" -#include "shared-module/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif #define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) @@ -324,11 +323,11 @@ static void bleio_adapter_reset_name(bleio_adapter_obj_t *self) { default_ble_name[len - 1] = nibble_to_hex_lower[addr.addr[0] & 0xf]; default_ble_name[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML char ble_name[32]; - os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); - if (result == GETENV_OK) { + settings_err_t result = settings_get_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); + if (result == SETTINGS_OK) { common_hal_bleio_adapter_set_name(self, ble_name); return; } diff --git a/ports/raspberrypi/common-hal/picodvi/__init__.c b/ports/raspberrypi/common-hal/picodvi/__init__.c index e8f344852de96..c2d4b297b39ba 100644 --- a/ports/raspberrypi/common-hal/picodvi/__init__.c +++ b/ports/raspberrypi/common-hal/picodvi/__init__.c @@ -10,8 +10,8 @@ #include "shared-bindings/busio/I2C.h" #include "shared-bindings/board/__init__.h" #include "shared-module/displayio/__init__.h" -#include "shared-module/os/__init__.h" #include "supervisor/shared/safe_mode.h" +#include "supervisor/shared/settings.h" #include "py/gc.h" #include "py/runtime.h" #include "supervisor/port_heap.h" @@ -22,7 +22,7 @@ static bool picodvi_autoconstruct_enabled(mp_int_t *default_width, mp_int_t *def buf[0] = 0; // (any failure leaves the content of buf untouched: an empty nul-terminated string - (void)common_hal_os_getenv_str("CIRCUITPY_PICODVI_ENABLE", buf, sizeof(buf)); + (void)settings_get_str("CIRCUITPY_PICODVI_ENABLE", buf, sizeof(buf)); if (!strcasecmp(buf, "never")) { return false; @@ -106,10 +106,10 @@ void picodvi_autoconstruct(void) { mp_int_t color_depth = 8; mp_int_t rotation = 0; - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_WIDTH", &width); - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_HEIGHT", &height); - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_COLOR_DEPTH", &color_depth); - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); + (void)settings_get_int("CIRCUITPY_DISPLAY_WIDTH", &width); + (void)settings_get_int("CIRCUITPY_DISPLAY_HEIGHT", &height); + (void)settings_get_int("CIRCUITPY_DISPLAY_COLOR_DEPTH", &color_depth); + (void)settings_get_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); if (height == 0) { switch (width) { diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 6aee1a939b306..6461c310eeb68 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -208,6 +208,8 @@ SRC_C += \ input.c \ alloc.c \ fatfs_port.c \ + shared-module/os/__init__.c \ + supervisor/shared/settings.c \ supervisor/stub/filesystem.c \ supervisor/stub/safe_mode.c \ supervisor/stub/stack.c \ diff --git a/ports/unix/main.c b/ports/unix/main.c index ce8b89136370b..259b183eb766d 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -55,35 +55,6 @@ #include "genhdr/mpversion.h" #include "input.h" -// CIRCUITPY-CHANGE -#if defined(MICROPY_UNIX_COVERAGE) -#include "py/objstr.h" -typedef int os_getenv_err_t; -mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_); -os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len); -os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value); - -static mp_obj_t mod_os_getenv_int(mp_obj_t var_in) { - mp_int_t value; - os_getenv_err_t result = common_hal_os_getenv_int(mp_obj_str_get_str(var_in), &value); - if (result == 0) { - return mp_obj_new_int(value); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mod_os_getenv_int_obj, mod_os_getenv_int); - -static mp_obj_t mod_os_getenv_str(mp_obj_t var_in) { - char buf[4096]; - os_getenv_err_t result = common_hal_os_getenv_str(mp_obj_str_get_str(var_in), buf, sizeof(buf)); - if (result == 0) { - return mp_obj_new_str_copy(&mp_type_str, (byte *)buf, strlen(buf)); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mod_os_getenv_str_obj, mod_os_getenv_str); -#endif - // Command line options, with their defaults static bool compile_only = false; static uint emit_opt = MP_EMIT_OPT_NONE; @@ -645,8 +616,6 @@ MP_NOINLINE int main_(int argc, char **argv) { // CIRCUITPY-CHANGE: test native base classes work as needed by CircuitPython libraries. extern const mp_obj_type_t native_base_class_type; mp_store_global(MP_QSTR_NativeBaseClass, MP_OBJ_FROM_PTR(&native_base_class_type)); - mp_store_global(MP_QSTR_getenv_int, MP_OBJ_FROM_PTR(&mod_os_getenv_int_obj)); - mp_store_global(MP_QSTR_getenv_str, MP_OBJ_FROM_PTR(&mod_os_getenv_str_obj)); } #endif diff --git a/ports/unix/modos.c b/ports/unix/modos.c index 896bf351b83f1..af8a1cc4da871 100644 --- a/ports/unix/modos.c +++ b/ports/unix/modos.c @@ -32,13 +32,12 @@ #include "py/runtime.h" #include "py/mphal.h" -// CIRCUITPY-CHANGE: enhanced getenv +// CIRCUITPY-CHANGE: use shared-module os getenv #if defined(MICROPY_UNIX_COVERAGE) #include "py/objstr.h" -typedef int os_getenv_err_t; +#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_); -os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len); -os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value); #endif static mp_obj_t mp_os_getenv(size_t n_args, const mp_obj_t *args) { @@ -58,30 +57,7 @@ static mp_obj_t mp_os_getenv(size_t n_args, const mp_obj_t *args) { } return mp_obj_new_str_from_cstr(s); } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_os_getenv_obj, 1, 2, mp_os_getenv); - -// CIRCUITPY-CHANGE: getenv differences -#if defined(MICROPY_UNIX_COVERAGE) -static mp_obj_t mp_os_getenv_int(mp_obj_t var_in) { - mp_int_t value; - os_getenv_err_t result = common_hal_os_getenv_int(mp_obj_str_get_str(var_in), &value); - if (result == 0) { - return mp_obj_new_int(value); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mp_os_getenv_int_obj, mp_os_getenv_int); - -static mp_obj_t mp_os_getenv_str(mp_obj_t var_in) { - char buf[4096]; - os_getenv_err_t result = common_hal_os_getenv_str(mp_obj_str_get_str(var_in), buf, sizeof(buf)); - if (result == 0) { - return mp_obj_new_str_copy(&mp_type_str, (byte *)buf, strlen(buf)); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mp_os_getenv_str_obj, mp_os_getenv_str); -#endif +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_os_getenv_obj, 1, 2, mp_os_getenv); static mp_obj_t mp_os_putenv(mp_obj_t key_in, mp_obj_t value_in) { const char *key = mp_obj_str_get_str(key_in); diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 6ec0e85377ccf..292e4de81699f 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -106,7 +106,6 @@ SRC_BITMAP := \ shared-module/floppyio/__init__.c \ shared-module/jpegio/__init__.c \ shared-module/jpegio/JpegDecoder.c \ - shared-module/os/getenv.c \ shared-module/rainbowio/__init__.c \ shared-module/struct/__init__.c \ shared-module/synthio/__init__.c \ @@ -164,8 +163,8 @@ CFLAGS += \ -DCIRCUITPY_GIFIO=1 \ -DCIRCUITPY_JPEGIO=1 \ -DCIRCUITPY_LOCALE=1 \ - -DCIRCUITPY_OS_GETENV=1 \ -DCIRCUITPY_RAINBOWIO=1 \ + -DCIRCUITPY_SETTINGS_TOML=1 \ -DCIRCUITPY_STRUCT=1 \ -DCIRCUITPY_SYNTHIO=1 \ -DCIRCUITPY_SYNTHIO_MAX_CHANNELS=14 \ diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 5760dbd9eb188..d8c95d18a79b9 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -959,7 +959,6 @@ SRC_SHARED_MODULE_INTERNAL = \ $(filter $(SRC_PATTERNS), \ displayio/bus_core.c \ displayio/display_core.c \ - os/getenv.c \ usb/utf16le.c \ ) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 8e0992f0418f3..56fc695742133 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -295,9 +295,6 @@ CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK) CIRCUITPY_ENABLE_MPY_NATIVE ?= 0 CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE) -CIRCUITPY_OS_GETENV ?= $(CIRCUITPY_FULL_BUILD) -CFLAGS += -DCIRCUITPY_OS_GETENV=$(CIRCUITPY_OS_GETENV) - CIRCUITPY_ERRNO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_ERRNO=$(CIRCUITPY_ERRNO) @@ -535,6 +532,9 @@ CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) CIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY?= 0 CFLAGS += -DCIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY=$(CIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY) +CIRCUITPY_SETTINGS_TOML ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_SETTINGS_TOML=$(CIRCUITPY_SETTINGS_TOML) + CIRCUITPY_SHARPDISPLAY ?= $(CIRCUITPY_FRAMEBUFFERIO) CFLAGS += -DCIRCUITPY_SHARPDISPLAY=$(CIRCUITPY_SHARPDISPLAY) diff --git a/shared-bindings/os/__init__.c b/shared-bindings/os/__init__.c index 5e28c02452d05..65bc90fbf8625 100644 --- a/shared-bindings/os/__init__.c +++ b/shared-bindings/os/__init__.c @@ -94,11 +94,20 @@ static mp_obj_t os_getcwd(void) { MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd); //| def getenv(key: str, default: Optional[str] = None) -> Optional[str]: -//| """Get the environment variable value for the given key or return ``default``. +//| """Get the environment variable value for the given ``key`` from the +//| ``/settings.toml`` file. +//| If ``key`` is not present or the value is ill-formed, return the ``default`` value. +//| The value is returned as a string even if it can be parsed as an integer or boolean. +//| No errors are raised if the value is not a valid TOML value; instead the original +//| string is returned, or ``default`` if no value can be retrieved at all. //| -//| This may load values from disk so cache the result instead of calling this often. +//| If you want to retrieve the value as a ``str``, ``bool``, or ``int``, use `supervisor.get_setting()`. //| -//| On boards that do not support ``settings.toml`` reading in the core, this function will raise NotImplementedError. +//| The ``settings.toml`` file is re-scanned on every call, +//| so cache the result instead of calling `getenv()` frequently. +//| +//| On boards that do not support ``settings.toml`` reading in the core, +//| this function will raise NotImplementedError. //| //| .. raw:: html //| @@ -118,7 +127,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd); //| //| static mp_obj_t os_getenv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML enum { ARG_key, ARG_default }; static const mp_arg_t allowed_args[] = { { MP_QSTR_key, MP_ARG_REQUIRED | MP_ARG_OBJ }, diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index f2fc14c3c9a5d..a0a01f124f786 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -21,6 +21,10 @@ #include "supervisor/usb.h" #endif +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" +#endif + #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/supervisor/__init__.h" #include "shared-bindings/time/__init__.h" @@ -360,6 +364,68 @@ static mp_obj_t supervisor_set_usb_identification(size_t n_args, const mp_obj_t } MP_DEFINE_CONST_FUN_OBJ_KW(supervisor_set_usb_identification_obj, 0, supervisor_set_usb_identification); +//| def get_setting(key: str, default: object=None) -> int | str | bool: +//| """ +//| Get and parse the value for the given ``key`` from the ``/settings.toml`` file. +//| If ``key`` is not found or ``settings.toml`` is not present, return the ``default`` value. +//| +//| :param str key: The setting key to retrieve +//| :return: The setting value as an ``int``, ``str``, or ``bool`` depending on the value in the file +//| +//| :raises ValueError: If the value cannot be parsed as a valid TOML value. +//| +//| The value must be parseable as one of these types: +//| +//| - ``str``: Double-quoted string. +//| The string may include Unicode characters, and ``\\u`` Unicode escapes. Backslash-escaped characters +//| ``\\b``, ``\\r``, ``\\n``, ``\\t``, ``\\v``, ``\\v`` are also allowed. +//| - ``int``: signed or unsigned integer +//| - lower-case boolean words ``true`` and ``false``. +//| The values are returned as Python ``True`` or ``False`` values. +//| +//| Example:: +//| +//| # settings.toml: +//| WIDTH = 42 +//| color = "red" +//| DEBUG = true +//| +//| import supervisor +//| print(supervisor.get_setting("WIDTH")) # prints 42 +//| print(supervisor.get_setting("color")) # prints 'red' +//| print(supervisor.get_setting("DEBUG")) # prints True +//| """ +//| ... +//| +static mp_obj_t supervisor_get_setting(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + #if CIRCUITPY_SETTINGS_TOML + enum { ARG_key, ARG_default }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_key, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_default, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args); + + const char *key = mp_obj_str_get_str(args[ARG_key].u_obj); + mp_obj_t value; + settings_err_t result = settings_get_obj(key, &value); + + switch (result) { + case SETTINGS_OK: + return value; + case SETTINGS_ERR_NOT_FOUND: + case SETTINGS_ERR_OPEN: + return args[ARG_default].u_obj; + default: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_value); + } + #else + mp_raise_NotImplementedError(NULL); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_KW(supervisor_get_setting_obj, 1, supervisor_get_setting); + static const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) }, { MP_ROM_QSTR(MP_QSTR_runtime), MP_ROM_PTR(&common_hal_supervisor_runtime_obj) }, @@ -373,6 +439,7 @@ static const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_set_next_code_file), MP_ROM_PTR(&supervisor_set_next_code_file_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&supervisor_ticks_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_get_previous_traceback), MP_ROM_PTR(&supervisor_get_previous_traceback_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_setting), MP_ROM_PTR(&supervisor_get_setting_obj) }, { MP_ROM_QSTR(MP_QSTR_reset_terminal), MP_ROM_PTR(&supervisor_reset_terminal_obj) }, { MP_ROM_QSTR(MP_QSTR_set_usb_identification), MP_ROM_PTR(&supervisor_set_usb_identification_obj) }, { MP_ROM_QSTR(MP_QSTR_status_bar), MP_ROM_PTR(&shared_module_supervisor_status_bar_obj) }, diff --git a/shared-module/os/__init__.c b/shared-module/os/__init__.c index 3594c9bb02877..3a9c0e31f8edf 100644 --- a/shared-module/os/__init__.c +++ b/shared-module/os/__init__.c @@ -17,6 +17,10 @@ #include "py/runtime.h" #include "shared-bindings/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" +#endif + // This provides all VFS related OS functions so that ports can share the code // as needed. @@ -281,4 +285,17 @@ void common_hal_os_utime(const char *path, mp_obj_t times) { mp_vfs_proxy_call(vfs, MP_QSTR_utime, 2, args); } +#if CIRCUITPY_SETTINGS_TOML +mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_) { + vstr_t vstr; + vstr_init(&vstr, 64); + + settings_err_t result = settings_get_raw_vstr(key, &vstr); + if (result == SETTINGS_OK) { + return mp_obj_new_str_from_vstr(&vstr); + } + return default_; +} +#endif + MP_REGISTER_ROOT_POINTER(const char *cwd_path); diff --git a/shared-module/os/__init__.h b/shared-module/os/__init__.h index d44bae1ffc560..830f762950975 100644 --- a/shared-module/os/__init__.h +++ b/shared-module/os/__init__.h @@ -6,26 +6,5 @@ #pragma once -typedef enum { - GETENV_OK = 0, - GETENV_ERR_OPEN, - GETENV_ERR_UNICODE, - GETENV_ERR_LENGTH, - GETENV_ERR_NOT_FOUND, - GETENV_ERR_UNEXPECTED = 0xff00, // logical or'd with the byte value -} os_getenv_err_t; - -// Allocation free version that returns the full length of the value. -// If it fits, the return value is 0-terminated. The passed in buffer -// may be modified even if an error is returned. Allocation free. -// An error that is not 'open' or 'not found' is printed on the repl. -os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len); - -// Returns GETENV_OK and sets value to the read value. Returns -// GETENV_ERR_... if the value was not numeric. allocation-free. -// If any error code is returned, value is guaranteed not modified -// An error that is not 'open' or 'not found' is printed on the repl. -os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value); - // Not made available to the VM but used by other modules to normalize paths. const char *common_hal_os_path_abspath(const char *path); diff --git a/shared-module/os/getenv.c b/shared-module/os/getenv.c deleted file mode 100644 index c7bfadf3418d8..0000000000000 --- a/shared-module/os/getenv.c +++ /dev/null @@ -1,417 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -// These functions are separate from __init__.c so that os.getenv() can be -// tested in the unix "coverage" build, without bringing in "our" os module - -#include -#include -#include - -#include "shared-bindings/os/__init__.h" -#include "shared-module/os/__init__.h" - -#include "py/gc.h" -#include "py/misc.h" -#include "py/mpstate.h" -#include "py/mpprint.h" -#include "py/objstr.h" -#include "py/parsenum.h" -#include "py/runtime.h" -#include "supervisor/filesystem.h" - -#define GETENV_PATH "/settings.toml" - -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" - -#if CIRCUITPY_OS_GETENV -typedef FIL file_arg; -static bool open_file(const char *name, file_arg *active_file) { - #if defined(UNIX) - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_obj_t file_obj = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), mp_obj_new_str(name, strlen(name)), MP_ROM_QSTR(MP_QSTR_rb)); - mp_arg_validate_type(file_obj, &mp_type_vfs_fat_fileio, MP_QSTR_file); - pyb_file_obj_t *file = MP_OBJ_TO_PTR(file_obj); - *active_file = file->fp; - nlr_pop(); - return true; - } else { - return false; - } - #else - fs_user_mount_t *fs_mount = filesystem_circuitpy(); - if (fs_mount == NULL) { - return false; - } - FATFS *fatfs = &fs_mount->fatfs; - FRESULT result = f_open(fatfs, active_file, name, FA_READ); - return result == FR_OK; - #endif -} -static void close_file(file_arg *active_file) { - // nothing -} -static bool is_eof(file_arg *active_file) { - return f_eof(active_file) || f_error(active_file); -} - -// Return 0 if there is no next character (EOF). -static uint8_t get_next_byte(FIL *active_file) { - uint8_t character = 0; - UINT quantity_read; - // If there's an error or quantity_read is 0, character will remain 0. - f_read(active_file, &character, 1, &quantity_read); - return character; -} -static void seek_eof(file_arg *active_file) { - f_lseek(active_file, f_size(active_file)); -} - -// For a fixed buffer, record the required size rather than throwing -static void vstr_add_byte_nonstd(vstr_t *vstr, byte b) { - if (!vstr->fixed_buf || vstr->alloc > vstr->len) { - vstr_add_byte(vstr, b); - } else { - vstr->len++; - } -} - -// For a fixed buffer, record the required size rather than throwing -static void vstr_add_char_nonstd(vstr_t *vstr, unichar c) { - size_t ulen = - (c < 0x80) ? 1 : - (c < 0x800) ? 2 : - (c < 0x10000) ? 3 : 4; - if (!vstr->fixed_buf || vstr->alloc > vstr->len + ulen) { - vstr_add_char(vstr, c); - } else { - vstr->len += ulen; - } -} - -static void next_line(file_arg *active_file) { - uint8_t character; - do { - character = get_next_byte(active_file); - } while (character != 0 && character != '\n'); -} - -// Discard whitespace, except for newlines, returning the next character after the whitespace. -// Return 0 if there is no next character (EOF). -static uint8_t consume_whitespace(file_arg *active_file) { - uint8_t character; - do { - character = get_next_byte(active_file); - } while (character != '\n' && character != 0 && unichar_isspace(character)); - return character; -} - -// Starting at the start of a new line, determines if the key matches the given -// key. -// -// If result is true, the key matches and file pointer is pointing just after the "=". -// If the result is false, the key does NOT match and the file pointer is -// pointing at the start of the next line, if any -static bool key_matches(file_arg *active_file, const char *key) { - uint8_t character; - character = consume_whitespace(active_file); - if (character == '[' || character == 0) { - seek_eof(active_file); - return false; - } - while (*key) { - if (character != *key++) { - // A character didn't match the key, so it's not a match - // If the non-matching char was not the end of the line, - // then consume the rest of the line - if (character != '\n') { - next_line(active_file); - } - return false; - } - character = get_next_byte(active_file); - } - // the next character could be whitespace; consume if necessary - if (unichar_isspace(character)) { - character = consume_whitespace(active_file); - } - // If we're not looking at the "=" then the key didn't match - if (character != '=') { - // A character didn't match the key, so it's not a match - // If the non-matching char was not the end of the line, - // then consume the rest of the line - if (character != '\n') { - next_line(active_file); - } - return false; - } - return true; -} - -static os_getenv_err_t read_unicode_escape(file_arg *active_file, int sz, vstr_t *buf) { - char hex_buf[sz + 1]; - for (int i = 0; i < sz; i++) { - hex_buf[i] = get_next_byte(active_file); - } - hex_buf[sz] = 0; - char *end; - unsigned long c = strtoul(hex_buf, &end, 16); - if (end != &hex_buf[sz]) { - return GETENV_ERR_UNEXPECTED | *end; - } - if (c >= 0x110000) { - return GETENV_ERR_UNICODE; - } - vstr_add_char_nonstd(buf, c); - return GETENV_OK; -} - -// Read a quoted string -static os_getenv_err_t read_string_value(file_arg *active_file, vstr_t *buf) { - while (true) { - int character = get_next_byte(active_file); - switch (character) { - case 0: - case '\n': - return GETENV_ERR_UNEXPECTED | character; - - case '"': - character = consume_whitespace(active_file); - switch (character) { - case '#': - next_line(active_file); - MP_FALLTHROUGH; - case 0: - case '\n': - return GETENV_OK; - default: - return GETENV_ERR_UNEXPECTED | character; - } - - case '\\': - character = get_next_byte(active_file); - switch (character) { - case 0: - case '\n': - return GETENV_ERR_UNEXPECTED | character; - case 'b': - character = '\b'; - break; - case 'r': - character = '\r'; - break; - case 'n': - character = '\n'; - break; - case 't': - character = '\t'; - break; - case 'v': - character = '\v'; - break; - case 'f': - character = '\f'; - break; - case 'U': - case 'u': { - int sz = (character == 'u') ? 4 : 8; - os_getenv_err_t res; - res = read_unicode_escape(active_file, sz, buf); - if (res != GETENV_OK) { - return res; - } - continue; - } - // default falls through, other escaped characters - // represent themselves - } - MP_FALLTHROUGH; - default: - vstr_add_byte_nonstd(buf, character); - } - } -} - -// Read a numeric value (non-quoted value) as a string -static os_getenv_err_t read_bare_value(file_arg *active_file, vstr_t *buf, int first_character) { - int character = first_character; - while (true) { - switch (character) { - case 0: - case '\n': - return GETENV_OK; - case '#': - next_line(active_file); - return GETENV_OK; - default: - vstr_add_byte_nonstd(buf, character); - } - character = get_next_byte(active_file); - } -} - -static mp_int_t read_value(file_arg *active_file, vstr_t *buf, bool *quoted) { - uint8_t character; - character = consume_whitespace(active_file); - *quoted = (character == '"'); - - if (*quoted) { - return read_string_value(active_file, buf); - } else { - return read_bare_value(active_file, buf, character); - } -} - -static os_getenv_err_t os_getenv_vstr(const char *path, const char *key, vstr_t *buf, bool *quoted) { - file_arg active_file; - if (!open_file(path, &active_file)) { - return GETENV_ERR_OPEN; - } - - os_getenv_err_t result = GETENV_ERR_NOT_FOUND; - while (!is_eof(&active_file)) { - if (key_matches(&active_file, key)) { - result = read_value(&active_file, buf, quoted); - break; - } - } - close_file(&active_file); - return result; -} - -static os_getenv_err_t os_getenv_buf_terminated(const char *key, char *value, size_t value_len, bool *quoted) { - vstr_t buf; - vstr_init_fixed_buf(&buf, value_len, value); - os_getenv_err_t result = os_getenv_vstr(GETENV_PATH, key, &buf, quoted); - - if (result == GETENV_OK) { - vstr_add_byte_nonstd(&buf, 0); - memcpy(value, buf.buf, MIN(buf.len, value_len)); - if (buf.len > value_len) { // this length includes trailing NUL - result = GETENV_ERR_LENGTH; - } - } - return result; -} - -static void print_dont_raise(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - mp_vcprintf(&mp_plat_print, fmt, argptr); - mp_printf(&mp_plat_print, "\n"); - va_end(argptr); -} - -static void handle_getenv_error(os_getenv_err_t error, void (*handle)(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...)) { - if (error == GETENV_OK) { - return; - } - if (error & GETENV_ERR_UNEXPECTED) { - byte character = (error & 0xff); - char buf[8]; - vstr_t vstr; - vstr_init_fixed_buf(&vstr, sizeof(buf), buf); - mp_print_t print = { .data = &vstr, .print_strn = (mp_print_strn_t)vstr_add_strn }; - - if (character) { - mp_str_print_quoted(&print, &character, 1, true); - } else { - mp_str_print_quoted(&print, (byte *)"EOF", 3, true); - } - handle(&mp_type_ValueError, MP_ERROR_TEXT("Invalid byte %.*s"), vstr.len, vstr.buf); - } else { - switch (error) { - case GETENV_ERR_OPEN: - handle(&mp_type_ValueError, MP_ERROR_TEXT("%S"), MP_ERROR_TEXT("File not found")); - break; - case GETENV_ERR_UNICODE: - handle(&mp_type_ValueError, MP_ERROR_TEXT("%S"), MP_ERROR_TEXT("Invalid unicode escape")); - break; - case GETENV_ERR_NOT_FOUND: - handle(&mp_type_ValueError, MP_ERROR_TEXT("%S"), MP_ERROR_TEXT("Key not found")); - break; - default: - handle(&mp_type_RuntimeError, MP_ERROR_TEXT("%S"), MP_ERROR_TEXT("Internal error")); - break; - } - } -} - -static void common_hal_os_getenv_showerr(const char *key, os_getenv_err_t result) { - if (result != GETENV_OK && result != GETENV_ERR_OPEN && result != GETENV_ERR_NOT_FOUND) { - mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("An error occurred while retrieving '%s':\n"), key); - handle_getenv_error(result, print_dont_raise); - } -} - -static -os_getenv_err_t common_hal_os_getenv_str_inner(const char *key, char *value, size_t value_len) { - bool quoted; - os_getenv_err_t result = os_getenv_buf_terminated(key, value, value_len, "ed); - if (result == GETENV_OK && !quoted) { - result = GETENV_ERR_UNEXPECTED | value[0]; - } - return result; -} - -os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len) { - os_getenv_err_t result = common_hal_os_getenv_str_inner(key, value, value_len); - common_hal_os_getenv_showerr(key, result); - return result; -} - -mp_obj_t common_hal_os_getenv_path(const char *path, const char *key, mp_obj_t default_) { - vstr_t buf; - bool quoted; - - vstr_init(&buf, 64); - os_getenv_err_t result = os_getenv_vstr(path, key, &buf, "ed); - if (result == GETENV_ERR_NOT_FOUND || result == GETENV_ERR_OPEN) { - return default_; - } - handle_getenv_error(result, mp_raise_msg_varg); - - if (quoted) { - return mp_obj_new_str_from_vstr(&buf); - } else { - return mp_parse_num_integer(buf.buf, buf.len, 0, NULL); - } -} - -mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_) { - return common_hal_os_getenv_path(GETENV_PATH, key, default_); -} - -static os_getenv_err_t common_hal_os_getenv_int_inner(const char *key, mp_int_t *value) { - char buf[16]; - bool quoted; - os_getenv_err_t result = os_getenv_buf_terminated(key, buf, sizeof(buf), "ed); - if (result != GETENV_OK) { - return result; - } - if (quoted) { - return GETENV_ERR_UNEXPECTED | '"'; - } - char *end; - long num = strtol(buf, &end, 0); - while (unichar_isspace(*end)) { - end++; - } - if (end == buf || *end) { // If the whole buffer was not consumed it's an error - return GETENV_ERR_UNEXPECTED | *end; - } - *value = (mp_int_t)num; - return GETENV_OK; -} - -os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value) { - os_getenv_err_t result = common_hal_os_getenv_int_inner(key, value); - common_hal_os_getenv_showerr(key, result); - return result; -} -#endif diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index 055a5cdf1fdfd..cccb371fc4dd2 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -36,8 +36,8 @@ #include "supervisor/shared/status_bar.h" #endif -#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" +#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif @@ -216,11 +216,11 @@ void supervisor_bluetooth_init(void) { // Checking here allows us to have the status LED solidly on even if no button was // pressed. bool wifi_workflow_active = false; - #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV + #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_SETTINGS_TOML char _api_password[64]; const size_t api_password_len = sizeof(_api_password) - 1; - os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); - wifi_workflow_active = result == GETENV_OK; + settings_err_t result = settings_get_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); + wifi_workflow_active = result == SETTINGS_OK; #endif if (!bonded && !wifi_workflow_active) { boot_in_discovery_mode = true; diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index 99c1497f02300..1352c76b0066a 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -38,9 +38,11 @@ #if CIRCUITPY_TERMINALIO #include "supervisor/port.h" -#if CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" + +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif + #if CIRCUITPY_LVFONTIO #include "shared-bindings/lvfontio/OnDiskFont.h" #include "supervisor/filesystem.h" @@ -66,10 +68,10 @@ static bool check_for_custom_font(const char **font_path_out) { const char *default_font_path = "/fonts/terminal.lvfontbin"; const char *font_path = default_font_path; - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML // Buffer for storing custom font path static char custom_font_path[128]; - if (common_hal_os_getenv_str("CIRCUITPY_TERMINAL_FONT", custom_font_path, sizeof(custom_font_path)) == GETENV_OK) { + if (settings_get_str("CIRCUITPY_TERMINAL_FONT", custom_font_path, sizeof(custom_font_path)) == SETTINGS_OK) { // Use custom font path from environment variable font_path = custom_font_path; } @@ -179,8 +181,8 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { if (width_in_tiles <= 120) { scale = 1; } - #if CIRCUITPY_OS_GETENV - (void)common_hal_os_getenv_int("CIRCUITPY_TERMINAL_SCALE", &scale); + #if CIRCUITPY_SETTINGS_TOML + (void)settings_get_int("CIRCUITPY_TERMINAL_SCALE", &scale); #endif width_in_tiles = MAX(1, width_px / (glyph_width * scale)); diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index 025c9ddbd6de3..eb4b6548d11ff 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -172,7 +172,7 @@ bool filesystem_init(bool create_allowed, bool force_create) { #endif #endif - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML make_empty_file(&circuitpy->fatfs, "/settings.toml"); #endif // make a sample code.py file diff --git a/supervisor/shared/settings.c b/supervisor/shared/settings.c new file mode 100644 index 0000000000000..d1b05a5da9db5 --- /dev/null +++ b/supervisor/shared/settings.c @@ -0,0 +1,463 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include "py/gc.h" +#include "py/misc.h" +#include "py/mpstate.h" +#include "py/mpprint.h" +#include "py/objstr.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "supervisor/filesystem.h" +#include "supervisor/shared/settings.h" + +#define SETTINGS_PATH "/settings.toml" + +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" + +#if CIRCUITPY_SETTINGS_TOML +typedef FIL file_arg; +static bool open_file(const char *name, file_arg *file_handle) { + #if defined(UNIX) + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t file_obj = mp_call_function_2( + MP_OBJ_FROM_PTR(&mp_builtin_open_obj), mp_obj_new_str(name, strlen(name)), MP_ROM_QSTR(MP_QSTR_rb)); + mp_arg_validate_type(file_obj, &mp_type_vfs_fat_fileio, MP_QSTR_file); + pyb_file_obj_t *file = MP_OBJ_TO_PTR(file_obj); + *file_handle = file->fp; + nlr_pop(); + return true; + } else { + return false; + } + #else + fs_user_mount_t *fs_mount = filesystem_circuitpy(); + if (fs_mount == NULL) { + return false; + } + FATFS *fatfs = &fs_mount->fatfs; + FRESULT result = f_open(fatfs, file_handle, name, FA_READ); + return result == FR_OK; + #endif +} + +static void close_file(file_arg *file_handle) { + f_close(file_handle); +} + +static bool is_eof(file_arg *file_handle) { + return f_eof(file_handle) || f_error(file_handle); +} + +// Return 0 if there is no next character (EOF). +static uint8_t get_next_byte(FIL *file_handle) { + uint8_t character = 0; + UINT quantity_read; + // If there's an error or quantity_read is 0, character will remain 0. + f_read(file_handle, &character, 1, &quantity_read); + return character; +} + +static void seek_eof(file_arg *file_handle) { + f_lseek(file_handle, f_size(file_handle)); +} + +// For a fixed buffer, record the required size rather than throwing +static void vstr_add_byte_nonstd(vstr_t *vstr, byte b) { + if (!vstr->fixed_buf || vstr->alloc > vstr->len) { + vstr_add_byte(vstr, b); + } else { + vstr->len++; + } +} + +// For a fixed buffer, record the required size rather than throwing +static void vstr_add_char_nonstd(vstr_t *vstr, unichar c) { + size_t ulen = + (c < 0x80) ? 1 : + (c < 0x800) ? 2 : + (c < 0x10000) ? 3 : 4; + if (!vstr->fixed_buf || vstr->alloc > vstr->len + ulen) { + vstr_add_char(vstr, c); + } else { + vstr->len += ulen; + } +} + +static void next_line(file_arg *file_handle) { + uint8_t character; + do { + character = get_next_byte(file_handle); + } while (character != 0 && character != '\n'); +} + +// Discard whitespace, except for newlines, returning the next character after the whitespace. +// Return 0 if there is no next character (EOF). +static uint8_t consume_whitespace(file_arg *file_handle) { + uint8_t character; + do { + character = get_next_byte(file_handle); + } while (character != '\n' && character != 0 && unichar_isspace(character)); + return character; +} + +// Starting at the start of a new line, determines if the key matches the given key. +// +// If result is true, the key matches and file pointer is pointing just after the "=". +// If the result is false, the key does NOT match and the file pointer is +// pointing at the start of the next line, if any +static bool key_matches(file_arg *file_handle, const char *key) { + uint8_t character; + character = consume_whitespace(file_handle); + // [section] isn't implemented, so skip to end of file. + if (character == '[' || character == 0) { + seek_eof(file_handle); + return false; + } + while (*key) { + if (character != *key++) { + // A character didn't match the key, so it's not a match + // If the non-matching char was not the end of the line, + // then consume the rest of the line + if (character != '\n') { + next_line(file_handle); + } + return false; + } + character = get_next_byte(file_handle); + } + // the next character could be whitespace; consume if necessary + if (unichar_isspace(character)) { + character = consume_whitespace(file_handle); + } + // If we're not looking at the "=" then the key didn't match + if (character != '=') { + // A character didn't match the key, so it's not a match + // If the non-matching char was not the end of the line, + // then consume the rest of the line + if (character != '\n') { + next_line(file_handle); + } + return false; + } + return true; +} + +static settings_err_t read_unicode_escape(file_arg *file_handle, int sz, vstr_t *vstr) { + char hex_buf[sz + 1]; + for (int i = 0; i < sz; i++) { + hex_buf[i] = get_next_byte(file_handle); + } + hex_buf[sz] = 0; + char *end; + unsigned long c = strtoul(hex_buf, &end, 16); + if (end != &hex_buf[sz]) { + return SETTINGS_ERR_BAD_VALUE; + } + if (c >= 0x110000) { + return SETTINGS_ERR_UNICODE; + } + vstr_add_char_nonstd(vstr, c); + return SETTINGS_OK; +} + +// Read a quoted string +static settings_err_t read_string_value(file_arg *file_handle, vstr_t *vstr) { + while (true) { + int character = get_next_byte(file_handle); + switch (character) { + case 0: + case '\n': + return SETTINGS_ERR_BAD_VALUE; + + case '"': + character = consume_whitespace(file_handle); + switch (character) { + case '#': + next_line(file_handle); + MP_FALLTHROUGH; + case 0: + case '\n': + return SETTINGS_OK; + default: + return SETTINGS_ERR_BAD_VALUE; + } + + case '\\': + character = get_next_byte(file_handle); + switch (character) { + case 0: + case '\n': + return SETTINGS_ERR_BAD_VALUE; + case 'b': + character = '\b'; + break; + case 'r': + character = '\r'; + break; + case 'n': + character = '\n'; + break; + case 't': + character = '\t'; + break; + case 'v': + character = '\v'; + break; + case 'f': + character = '\f'; + break; + case 'U': + case 'u': { + int sz = (character == 'u') ? 4 : 8; + settings_err_t res; + res = read_unicode_escape(file_handle, sz, vstr); + if (res != SETTINGS_OK) { + return res; + } + continue; + } + // default falls through, other escaped characters + // represent themselves + } + MP_FALLTHROUGH; + default: + vstr_add_byte_nonstd(vstr, character); + } + } +} + +// Read a bare value (non-quoted value) as a string +// Trims leading and trailing spaces/tabs, stops at # comment or newline +static settings_err_t read_bare_value(file_arg *file_handle, vstr_t *vstr, int first_character) { + int character = first_character; + size_t trailing_space_count = 0; + + while (true) { + switch (character) { + case 0: + case '\n': + // Remove trailing spaces/tabs and \r + vstr->len -= trailing_space_count; + return SETTINGS_OK; + case '#': + // Remove trailing spaces/tabs and \r before comment + vstr->len -= trailing_space_count; + next_line(file_handle); + return SETTINGS_OK; + case ' ': + case '\t': + case '\r': + // Track potential trailing whitespace + vstr_add_byte_nonstd(vstr, character); + trailing_space_count++; + break; + default: + // Non-whitespace character resets trailing space count + vstr_add_byte_nonstd(vstr, character); + trailing_space_count = 0; + } + character = get_next_byte(file_handle); + } +} + +static mp_int_t read_value(file_arg *file_handle, vstr_t *vstr, bool *quoted) { + uint8_t character; + character = consume_whitespace(file_handle); + *quoted = (character == '"'); + + if (*quoted) { + return read_string_value(file_handle, vstr); + } else if (character == '\n' || character == 0) { + // Empty value is an error + return SETTINGS_ERR_BAD_VALUE; + } else { + return read_bare_value(file_handle, vstr, character); + } +} + +static settings_err_t settings_get_vstr(const char *key, vstr_t *vstr, bool *quoted) { + file_arg file_handle; + if (!open_file(SETTINGS_PATH, &file_handle)) { + return SETTINGS_ERR_OPEN; + } + + settings_err_t result = SETTINGS_ERR_NOT_FOUND; + while (!is_eof(&file_handle)) { + if (key_matches(&file_handle, key)) { + result = read_value(&file_handle, vstr, quoted); + break; + } + } + close_file(&file_handle); + return result; +} + +static settings_err_t settings_get_buf_terminated(const char *key, char *value, size_t value_len, bool *quoted) { + vstr_t vstr; + vstr_init_fixed_buf(&vstr, value_len, value); + settings_err_t result = settings_get_vstr(key, &vstr, quoted); + + if (result == SETTINGS_OK) { + vstr_add_byte_nonstd(&vstr, 0); + memcpy(value, vstr.buf, MIN(vstr.len, value_len)); + if (vstr.len > value_len) { // this length includes trailing NUL + result = SETTINGS_ERR_LENGTH; + } + } + return result; +} + +static void print_error(const char *key, settings_err_t result) { + switch (result) { + case SETTINGS_OK: + case SETTINGS_ERR_OPEN: + case SETTINGS_ERR_NOT_FOUND: + // These errors need not be printed. + // The code asking for the value is not necessarily expecting to find one. + return; + default: + mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("An error occurred while retrieving '%s':\n"), key); + break; + } + + switch (result) { + case SETTINGS_ERR_UNICODE: + mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("Invalid unicode escape")); + break; + case SETTINGS_ERR_BAD_VALUE: + mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("Invalid format")); + break; + default: + mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("Internal error")); + break; + } + mp_printf(&mp_plat_print, "\n"); +} + + +static settings_err_t get_str(const char *key, char *value, size_t value_len) { + bool quoted; + settings_err_t result = settings_get_buf_terminated(key, value, value_len, "ed); + if (result == SETTINGS_OK && !quoted) { + result = SETTINGS_ERR_BAD_VALUE; + } + return result; +} + +settings_err_t settings_get_str(const char *key, char *value, size_t value_len) { + settings_err_t result = get_str(key, value, value_len); + print_error(key, result); + return result; +} + +static settings_err_t get_int(const char *key, mp_int_t *value) { + char buf[16]; + bool quoted; + settings_err_t result = settings_get_buf_terminated(key, buf, sizeof(buf), "ed); + if (result != SETTINGS_OK) { + return result; + } + if (quoted) { + return SETTINGS_ERR_BAD_VALUE; + } + char *end; + long num = strtol(buf, &end, 0); + while (unichar_isspace(*end)) { + end++; + } + if (end == buf || *end) { // If the whole buffer was not consumed it's an error + return SETTINGS_ERR_BAD_VALUE; + } + *value = (mp_int_t)num; + return SETTINGS_OK; +} + +settings_err_t settings_get_int(const char *key, mp_int_t *value) { + settings_err_t result = get_int(key, value); + print_error(key, result); + return result; +} + +static settings_err_t get_bool(const char *key, bool *value) { + char buf[16]; + bool quoted; + settings_err_t result = settings_get_buf_terminated(key, buf, sizeof(buf), "ed); + if (result != SETTINGS_OK) { + return result; + } + if (quoted) { + return SETTINGS_ERR_BAD_VALUE; + } + + // Check for "true" or "false" (case-sensitive) + if (strcmp(buf, "true") == 0) { + *value = true; + return SETTINGS_OK; + } else if (strcmp(buf, "false") == 0) { + *value = false; + return SETTINGS_OK; + } + + // Not a valid boolean value + return SETTINGS_ERR_BAD_VALUE; +} + +settings_err_t settings_get_bool(const char *key, bool *value) { + settings_err_t result = get_bool(key, value); + print_error(key, result); + return result; +} + +// Get the raw value as a vstr, whether quoted or bare. Value may be an invalid TOML value. +settings_err_t settings_get_raw_vstr(const char *key, vstr_t *vstr) { + bool quoted; + return settings_get_vstr(key, vstr, "ed); +} + +settings_err_t settings_get_obj(const char *key, mp_obj_t *value) { + vstr_t vstr; + vstr_init(&vstr, 32); + bool quoted; + + settings_err_t result = settings_get_vstr(key, &vstr, "ed); + if (result != SETTINGS_OK) { + return result; + } + + if (quoted) { + // Successfully parsed a quoted string + *value = mp_obj_new_str_from_vstr(&vstr); + return SETTINGS_OK; + } + + // Not a quoted string, try boolean + bool bool_val; + result = get_bool(key, &bool_val); + if (result == SETTINGS_OK) { + *value = mp_obj_new_bool(bool_val); + return SETTINGS_OK; + } + + // Not a boolean, try integer + mp_int_t int_val; + result = get_int(key, &int_val); + if (result == SETTINGS_OK) { + *value = mp_obj_new_int(int_val); + return SETTINGS_OK; + } + + return SETTINGS_ERR_BAD_VALUE; +} + +#endif diff --git a/supervisor/shared/settings.h b/supervisor/shared/settings.h new file mode 100644 index 0000000000000..eafa9962e9527 --- /dev/null +++ b/supervisor/shared/settings.h @@ -0,0 +1,53 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef enum { + SETTINGS_OK = 0, + SETTINGS_ERR_OPEN, + SETTINGS_ERR_UNICODE, + SETTINGS_ERR_LENGTH, + SETTINGS_ERR_NOT_FOUND, + SETTINGS_ERR_BAD_VALUE, +} settings_err_t; + +// Read a string value from the settings file. +// If it fits, the return value is 0-terminated. The passed-in buffer +// may be modified even if an error is returned. Allocation free. +// An error that is not 'open' or 'not found' is printed on the repl. +// Returns an error if the value is not a quoted string. +settings_err_t settings_get_str(const char *key, char *value, size_t value_len); + +// Read an integer value from the settings file. +// Returns SETTINGS_OK and sets value to the read value. Returns +// SETTINGS_ERR_... if the value was not numeric. allocation-free. +// If any error code is returned, value is guaranteed not modified +// An error that is not 'open' or 'not found' is printed on the repl. +settings_err_t settings_get_int(const char *key, mp_int_t *value); + +// Read a boolean value from the settings file. +// Returns SETTINGS_OK and sets value to the read value. Returns +// SETTINGS_ERR_... if the value was not a boolean (true or false). allocation-free. +// If any error code is returned, value is guaranteed not modified +// An error that is not 'open' or 'not found' is printed on the repl. +settings_err_t settings_get_bool(const char *key, bool *value); + +// Read a value from the settings file and return as parsed Python object. +// Returns SETTINGS_OK and sets value to a parsed Python object from the RHS value: +// - Quoted strings return as str +// - Bare "true" or "false" return as bool +// - Valid integers return as int +// Returns SETTINGS_ERR_... if the value is not parseable as one of these types. +// An error that is not 'open' or 'not found' is printed on the repl. +settings_err_t settings_get_obj(const char *key, mp_obj_t *value); + +// Read the raw value as a string, whether quoted or bare. +// This is used by os.getenv() to always return strings. +// Does not print errors. +settings_err_t settings_get_raw_vstr(const char *key, vstr_t *vstr); diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 553fe8694c6ea..74e4fed57592c 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -46,8 +46,8 @@ #include "shared-bindings/wifi/__init__.h" #endif -#if CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif enum request_state { @@ -237,21 +237,21 @@ void supervisor_web_workflow_status(void) { #endif bool supervisor_start_web_workflow(void) { - #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV + #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_SETTINGS_TOML char ssid[33]; char password[64]; - os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid)); - if (result != GETENV_OK || strlen(ssid) < 1) { + settings_err_t result = settings_get_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid)); + if (result != SETTINGS_OK || strlen(ssid) < 1) { return false; } - result = common_hal_os_getenv_str("CIRCUITPY_WIFI_PASSWORD", password, sizeof(password)); - if (result == GETENV_ERR_NOT_FOUND) { + result = settings_get_str("CIRCUITPY_WIFI_PASSWORD", password, sizeof(password)); + if (result == SETTINGS_ERR_NOT_FOUND) { // if password is unspecified, assume an open network password[0] = '\0'; - } else if (result != GETENV_OK) { + } else if (result != SETTINGS_OK) { return false; } @@ -289,17 +289,17 @@ bool supervisor_start_web_workflow(void) { bool initialized = pool.base.type == &socketpool_socketpool_type; if (!initialized) { - result = common_hal_os_getenv_str("CIRCUITPY_WEB_INSTANCE_NAME", web_instance_name, sizeof(web_instance_name)); - if (result != GETENV_OK || web_instance_name[0] == '\0') { + result = settings_get_str("CIRCUITPY_WEB_INSTANCE_NAME", web_instance_name, sizeof(web_instance_name)); + if (result != SETTINGS_OK || web_instance_name[0] == '\0') { strcpy(web_instance_name, MICROPY_HW_BOARD_NAME); } // (leaves new_port unchanged on any failure) - (void)common_hal_os_getenv_int("CIRCUITPY_WEB_API_PORT", &web_api_port); + (void)settings_get_int("CIRCUITPY_WEB_API_PORT", &web_api_port); const size_t api_password_len = sizeof(_api_password) - 1; - result = common_hal_os_getenv_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); - if (result == GETENV_OK) { + result = settings_get_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); + if (result == SETTINGS_OK) { _api_password[0] = ':'; _base64_in_place(_api_password, strlen(_api_password), sizeof(_api_password) - 1); } else { // Skip starting web-workflow when no password is passed. diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 9ebdd52068e73..bce47353f578f 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -19,6 +19,10 @@ SRC_SUPERVISOR = \ supervisor/shared/translate/translate.c \ supervisor/shared/workflow.c \ +ifeq ($(CIRCUITPY_SETTINGS_TOML),1) +SRC_SUPERVISOR += supervisor/shared/settings.c +endif + ifeq ($(CIRCUITPY_LIB_TLSF),1) SRC_SUPERVISOR += lib/tlsf/tlsf.c endif diff --git a/tests/circuitpython/getenv.py b/tests/circuitpython/getenv.py index 37819dd6f5fa5..3647b562eed13 100644 --- a/tests/circuitpython/getenv.py +++ b/tests/circuitpython/getenv.py @@ -58,7 +58,7 @@ def ioctl(self, op, arg): b'key = """\n', b"key =\n", b'key="', - b"key = strings must be quoted\n", + b"key = this is an unquoted string\n", ] @@ -75,6 +75,7 @@ def run_test(key, content): run_test("key", b"") +# key12 does not exist for i in range(13): run_test(f"key{i}", content_good) @@ -82,11 +83,9 @@ def run_test(key, content): for i in range(13): run_test(f"key{i}", content_good) -run_test(f"K", b"K = 7\r\n") -print(getenv_int("K")) - # Test value without trailing newline run_test(f"noeol", b"noeol=3") +# These return None now for content in content_bad: run_test("key", content) diff --git a/tests/circuitpython/getenv.py.exp b/tests/circuitpython/getenv.py.exp index 67707eb67fd75..89d654403dcf9 100644 --- a/tests/circuitpython/getenv.py.exp +++ b/tests/circuitpython/getenv.py.exp @@ -1,6 +1,6 @@ key None key0 'hello world' -key1 7 +key1 '7' key2 '\n' key3 'Áx' key4 'Áx' @@ -9,11 +9,11 @@ key6 '\t\r\x08' key7 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' key8 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' key9 'hello comment' -key10 127 -key11 0 +key10 '0x7f' +key11 '0' key12 None key0 'hello world' -key1 7 +key1 '7' key2 '\n' key3 'Áx' key4 'Áx' @@ -22,14 +22,12 @@ key6 '\t\r\x08' key7 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' key8 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' key9 'hello comment' -key10 127 -key11 0 +key10 '0x7f' +key11 '0' key12 None -K 7 -7 -noeol 3 -key Invalid byte '\n' -key Invalid byte '"' -key invalid syntax for integer with base 10: '' -key Invalid byte 'EOF' -key invalid syntax for integer with base 10: 'strings must be quoted' +noeol '3' +key None +key None +key None +key None +key 'this is an unquoted string' From 7bb0c022560d9c6a3535b5d5d6a28ecb533d87cd Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 16 Feb 2026 14:46:58 -0500 Subject: [PATCH 328/517] add port_yield from #10795 only on zephyr to fix #10822 --- supervisor/shared/background_callback.c | 6 ++++++ supervisor/shared/safe_mode.c | 3 +++ 2 files changed, 9 insertions(+) diff --git a/supervisor/shared/background_callback.c b/supervisor/shared/background_callback.c index 02aa9ae84d561..79b18f7a13753 100644 --- a/supervisor/shared/background_callback.c +++ b/supervisor/shared/background_callback.c @@ -61,7 +61,10 @@ static int background_prevention_count; void PLACE_IN_ITCM(background_callback_run_all)(void) { port_background_task(); if (!background_callback_pending()) { + // TEMPORARY to fix #10822 + #ifdef __ZEPHYR__ port_yield(); + #endif return; } CALLBACK_CRITICAL_BEGIN; @@ -88,7 +91,10 @@ void PLACE_IN_ITCM(background_callback_run_all)(void) { } --background_prevention_count; CALLBACK_CRITICAL_END; + // TEMPORARY to fix #10822 + #ifdef __ZEPHYR__ port_yield(); + #endif } void background_callback_prevent(void) { diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index 9650556546030..bfe27ac7c6513 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -82,7 +82,10 @@ safe_mode_t wait_for_safe_mode_reset(void) { boot_in_safe_mode = true; break; } + // TEMPORARY to fix #10822 + #ifdef __ZEPHYR__ port_yield(); + #endif diff = supervisor_ticks_ms64() - start_ticks; } #if CIRCUITPY_STATUS_LED From f4cc5f5a7aff7a5fa4dd6a66e36bfd85162dcad7 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 16 Feb 2026 16:07:03 -0500 Subject: [PATCH 329/517] shrink pygamer --- ports/atmel-samd/boards/pygamer/mpconfigboard.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/atmel-samd/boards/pygamer/mpconfigboard.mk b/ports/atmel-samd/boards/pygamer/mpconfigboard.mk index 19fa4f19b9de6..0c6f16f8b6381 100644 --- a/ports/atmel-samd/boards/pygamer/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pygamer/mpconfigboard.mk @@ -12,6 +12,7 @@ LONGINT_IMPL = MPZ CIRCUITPY_AESIO = 0 CIRCUITPY_FLOPPYIO = 0 +CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_GIFIO = 0 CIRCUITPY_I2CDISPLAYBUS = 0 From 4ec807fc3ddffeb869e070ed26e517cc8dc1be9a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 16 Feb 2026 19:21:53 -0500 Subject: [PATCH 330/517] update to arm gcc 15.2Rel1 --- .github/actions/deps/external/action.yml | 2 +- .github/actions/deps/ports/broadcom/action.yml | 4 ++-- .github/workflows/build.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/actions/deps/external/action.yml b/.github/actions/deps/external/action.yml index 10aace1134ec5..564c851cfc913 100644 --- a/.github/actions/deps/external/action.yml +++ b/.github/actions/deps/external/action.yml @@ -27,7 +27,7 @@ runs: uses: carlosperate/arm-none-eabi-gcc-action@v1 with: # When changing this update what Windows grabs too! - release: '14.2.Rel1' + release: '15.2.Rel1' # espressif - name: Get espressif toolchain diff --git a/.github/actions/deps/ports/broadcom/action.yml b/.github/actions/deps/ports/broadcom/action.yml index 9ad0e361bda1b..fc375aa10f74f 100644 --- a/.github/actions/deps/ports/broadcom/action.yml +++ b/.github/actions/deps/ports/broadcom/action.yml @@ -5,8 +5,8 @@ runs: steps: - name: Get broadcom toolchain run: | - wget --no-verbose https://adafruit-circuit-python.s3.amazonaws.com/arm-gnu-toolchain-14.2.rel1-x86_64-aarch64-none-elf.tar.xz - sudo tar -C /usr --strip-components=1 -xaf arm-gnu-toolchain-14.2.rel1-x86_64-aarch64-none-elf.tar.xz + wget --no-verbose https://adafruit-circuit-python.s3.amazonaws.com/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-elf.tar.xz + sudo tar -C /usr --strip-components=1 -xaf arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-elf.tar.xz sudo apt-get update sudo apt-get install -y mtools shell: bash diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fbd3af440c6b7..5b755eb398ea6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -246,7 +246,7 @@ jobs: python3 -c "import sys, locale; print(sys.getdefaultencoding(), locale.getpreferredencoding(False))" - name: Install dependencies run: | - wget --no-verbose -O gcc-arm.zip https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi.zip + wget --no-verbose -O gcc-arm.zip https://developer.arm.com/-/media/Files/downloads/gnu/15.2.rel1/binrel/arm-gnu-toolchain-15.2.rel1-mingw-w64-i686-arm-none-eabi.zip unzip -q -d /tmp/arm-gnu-toolchain gcc-arm.zip tar -C /tmp/arm-gnu-toolchain -cf - . | tar -C /usr/local -xf - # We could use a venv instead, but that requires entering the venv on each run step From 40abcd180028a20df940939a4347a8cc765015e9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 17 Feb 2026 08:31:18 -0500 Subject: [PATCH 331/517] update .devcontainer to gcc 15.2 --- .devcontainer/cortex-m-toolchain.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/cortex-m-toolchain.sh b/.devcontainer/cortex-m-toolchain.sh index 10896abd08c00..56be006544f1c 100755 --- a/.devcontainer/cortex-m-toolchain.sh +++ b/.devcontainer/cortex-m-toolchain.sh @@ -14,10 +14,10 @@ echo -e "[cortex-m-toolchain.sh] downloading and installing gcc-arm-non-eabi too cd /workspaces wget -qO gcc-arm-none-eabi.tar.xz \ - https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi.tar.xz + https://developer.arm.com/-/media/Files/downloads/gnu/15.2.rel1/binrel/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-eabi.tar.xz tar -xJf gcc-arm-none-eabi.tar.xz -ln -s arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi gcc-arm-none-eabi +ln -s arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-eabi gcc-arm-none-eabi rm -f gcc-arm-none-eabi.tar.xz echo -e "[cortex-m-toolchain.sh] update PATH in environment" From 3840b81946072d3d06f6689a2aba95d041df2a71 Mon Sep 17 00:00:00 2001 From: mimi89999 Date: Tue, 17 Feb 2026 17:22:31 +0100 Subject: [PATCH 332/517] Use TRNG as additional entropy source on RP2350 --- ports/raspberrypi/common-hal/os/__init__.c | 153 +++++++++++++++++++-- 1 file changed, 139 insertions(+), 14 deletions(-) diff --git a/ports/raspberrypi/common-hal/os/__init__.c b/ports/raspberrypi/common-hal/os/__init__.c index 616bb8d8c7929..d2a8da4caefdd 100644 --- a/ports/raspberrypi/common-hal/os/__init__.c +++ b/ports/raspberrypi/common-hal/os/__init__.c @@ -18,35 +18,159 @@ #include +#ifdef HAS_RP2350_TRNG +#include "hardware/structs/trng.h" +#include "hardware/sync.h" +#endif + // NIST Special Publication 800-90B (draft) recommends several extractors, // including the SHA hash family and states that if the amount of entropy input // is twice the number of bits output from them, that output can be considered -// essentially fully random. If every RANDOM_SAFETY_MARGIN bits from -// `rosc_hw->randombit` have at least 1 bit of entropy, then this criterion is met. +// essentially fully random. +// +// This works by seeding `random_state` with entropy from hardware sources +// (SHA-256 as the conditioning function), then using that state as a counter +// input (SHA-256 as a CSPRNG), re-seeding at least every 256 blocks (8kB). // -// This works by seeding the `random_state` with plenty of random bits (SHA256 -// as entropy harvesting function), then using that state it as a counter input -// (SHA256 as a CSPRNG), re-seeding at least every 256 blocks (8kB). +// On RP2350, entropy comes from both the dedicated TRNG peripheral and the +// ROSC. On RP2040, the ROSC is the only available source. // // In practice, `PractRand` doesn't detect any gross problems with the output // random numbers on samples of 1 to 8 megabytes, no matter the setting of -// RANDOM_SAFETY_MARGIN. (it does detect "unusual" results from time to time, +// ROSC_SAFETY_MARGIN. (it does detect "unusual" results from time to time, // as it will with any RNG) -#define RANDOM_SAFETY_MARGIN (4) + +// Number of ROSC collection rounds on RP2040. Each round feeds +// SHA256_BLOCK_SIZE bytes into the hash; we do 2*N rounds so the +// raw-to-output ratio satisfies 800-90B's 2:1 minimum. +#define ROSC_SAFETY_MARGIN (4) static BYTE random_state[SHA256_BLOCK_SIZE]; + +// Collect `count` bytes from the ROSC, one bit per read. +static void rosc_random_bytes(BYTE *buf, size_t count) { + for (size_t i = 0; i < count; i++) { + buf[i] = rosc_hw->randombit & 1; + for (int k = 0; k < 8; k++) { + buf[i] = (buf[i] << 1) ^ (rosc_hw->randombit & 1); + } + } +} + +#ifdef HAS_RP2350_TRNG + +// TRNG_DEBUG_CONTROL bypass bits: +// +// bit 1 VNC_BYPASS Von Neumann corrector +// bit 2 TRNG_CRNGT_BYPASS Continuous Random Number Generator Test +// bit 3 AUTO_CORRELATE_BYPASS Autocorrelation test +// +// We bypass Von Neumann and autocorrelation but keep CRNGT. +// +// Von Neumann (bypassed): ~4x throughput cost for bias removal. +// Redundant here because SHA-256 conditioning already handles +// biased input -- that's what the 2:1 oversampling ratio is for. +// +// Autocorrelation (bypassed): has a non-trivial false-positive rate +// at high sampling speeds and halts the TRNG until SW reset on +// failure. SHA-256 is not bothered by correlated input. ARM's own +// TZ-TRNG 90B reference configuration also bypasses it (0x0A). +// +// CRNGT (kept): compares consecutive 192-bit EHR outputs. Flags if +// identical -- false-positive rate 2^-192, throughput cost zero. +// This is our early warning for a stuck oscillator or a successful +// injection lock to a fixed state. +#define TRNG_BYPASS_BITS \ + (TRNG_TRNG_DEBUG_CONTROL_VNC_BYPASS_BITS | \ + TRNG_TRNG_DEBUG_CONTROL_AUTO_CORRELATE_BYPASS_BITS) + +// Collect 192 raw bits (6 x 32-bit words) from the TRNG. +// Returns false on CRNGT failure (consecutive identical EHR outputs). +// +// Holds PICO_SPINLOCK_ID_RAND (the SDK's lock for this peripheral) +// with interrupts disabled for the duration of the collection, which +// takes ~192 ROSC cycles (~24us at 8MHz). +static bool trng_collect_192(uint32_t out[6]) { + spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND); + uint32_t save = spin_lock_blocking(lock); + + trng_hw->trng_debug_control = TRNG_BYPASS_BITS; + // One rng_clk cycle between samples. The SDK uses 0 here, but it + // also sets debug_control = -1u (full bypass). The behavior of + // sample_cnt1 = 0 with health tests still active is undocumented, + // so we use 1 to be safe. + trng_hw->sample_cnt1 = 1; + trng_hw->rnd_source_enable = 1; + trng_hw->rng_icr = 0xFFFFFFFF; + + while (trng_hw->trng_busy) { + } + + if (trng_hw->rng_isr & TRNG_RNG_ISR_CRNGT_ERR_BITS) { + // Drain ehr_data so the hardware starts a fresh collection. + // (Reading the last word clears the valid flag.) + for (int i = 0; i < 6; i++) { + (void)trng_hw->ehr_data[i]; + } + trng_hw->rng_icr = TRNG_RNG_ISR_CRNGT_ERR_BITS; + spin_unlock(lock, save); + return false; + } + + for (int i = 0; i < 6; i++) { + out[i] = trng_hw->ehr_data[i]; + } + + // Switch the inverter chain length for the next collection, using + // bits from the sample we just read. Only bits [1:0] matter -- they + // select one of four chain lengths, changing the ROSC frequency. + // This is borrowed from pico_rand's injection-locking countermeasure. + // (The SDK uses its PRNG state here instead of raw output; either + // works since the real defense is SHA-256 conditioning, not this.) + trng_hw->trng_config = out[0]; + + spin_unlock(lock, save); + return true; +} + +#endif // HAS_RP2350_TRNG + static void seed_random_bits(BYTE out[SHA256_BLOCK_SIZE]) { CRYAL_SHA256_CTX context; sha256_init(&context); - for (int i = 0; i < 2 * RANDOM_SAFETY_MARGIN; i++) { - for (int j = 0; j < SHA256_BLOCK_SIZE; j++) { - out[j] = rosc_hw->randombit & 1; - for (int k = 0; k < 8; k++) { - out[j] = (out[j] << 1) ^ (rosc_hw->randombit & 1); + + #ifdef HAS_RP2350_TRNG + // 384 bits from TRNG + 384 bits from ROSC = 768 bits into the hash, + // giving a 3:1 ratio over the 256-bit output (800-90B wants >= 2:1). + // Two independent sources so a failure in one doesn't zero the input. + + // TRNG: 2 x 192 bits. + for (int i = 0; i < 2; i++) { + uint32_t trng_buf[6] = {0}; + for (int attempt = 0; attempt < 3; attempt++) { + if (trng_collect_192(trng_buf)) { + break; } + // CRNGT failure. If all 3 retries fail, trng_buf stays zeroed + // and we rely entirely on the ROSC contribution below. } + sha256_update(&context, (const BYTE *)trng_buf, sizeof(trng_buf)); + } + + // ROSC: 2 x 24 bytes = 384 bits. + for (int i = 0; i < 2; i++) { + BYTE rosc_buf[24]; + rosc_random_bytes(rosc_buf, sizeof(rosc_buf)); + sha256_update(&context, rosc_buf, sizeof(rosc_buf)); + } + #else + // RP2040: ROSC is the only entropy source. + for (int i = 0; i < 2 * ROSC_SAFETY_MARGIN; i++) { + rosc_random_bytes(out, SHA256_BLOCK_SIZE); sha256_update(&context, out, SHA256_BLOCK_SIZE); } + #endif + sha256_final(&context, out); } @@ -61,10 +185,11 @@ static void get_random_bits(BYTE out[SHA256_BLOCK_SIZE]) { } bool common_hal_os_urandom(uint8_t *buffer, mp_uint_t length) { -#define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time + #define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time #if ROSC_POWER_SAVE uint32_t old_rosc_ctrl = rosc_hw->ctrl; - rosc_hw->ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS) | (ROSC_CTRL_ENABLE_VALUE_ENABLE << 12); + rosc_hw->ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS) + | (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB); #endif while (length) { size_t n = MIN(length, SHA256_BLOCK_SIZE); From d85a4fc9975817c7e382a3dd9bb62e161bb55753 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 17 Feb 2026 11:25:07 -0500 Subject: [PATCH 333/517] remove unneeded -fno-inline-functions and -ggdb3; fix cxd56 noreturn warning --- ports/cxd56/Makefile | 2 +- ports/cxd56/common-hal/microcontroller/__init__.c | 2 ++ ports/litex/Makefile | 2 +- ports/nordic/Makefile | 4 ++-- ports/silabs/Makefile | 2 +- ports/stm/Makefile | 3 +-- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ports/cxd56/Makefile b/ports/cxd56/Makefile index c4526f31252ac..88c3921022061 100644 --- a/ports/cxd56/Makefile +++ b/ports/cxd56/Makefile @@ -74,7 +74,7 @@ CFLAGS += \ -fdata-sections \ -Wall \ -OPTIMIZATION_FLAGS ?= -O2 -fno-inline-functions +OPTIMIZATION_FLAGS ?= -O2 # option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk CFLAGS += $(OPTIMIZATION_FLAGS) diff --git a/ports/cxd56/common-hal/microcontroller/__init__.c b/ports/cxd56/common-hal/microcontroller/__init__.c index fa872f2807ba0..f7ac0c174ae07 100644 --- a/ports/cxd56/common-hal/microcontroller/__init__.c +++ b/ports/cxd56/common-hal/microcontroller/__init__.c @@ -61,6 +61,8 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { void common_hal_mcu_reset(void) { filesystem_flush(); boardctl(BOARDIOC_RESET, 0); + // boardctl is noreturn in this case. + __builtin_unreachable(); } static const mp_rom_map_elem_t mcu_pin_globals_table[] = { diff --git a/ports/litex/Makefile b/ports/litex/Makefile index 940d751e3d92f..cc813a49f8e70 100644 --- a/ports/litex/Makefile +++ b/ports/litex/Makefile @@ -32,7 +32,7 @@ ifeq ($(DEBUG), 1) OPTIMIZATION_FLAGS ?= -Og else CFLAGS += -DNDEBUG -ggdb3 - OPTIMIZATION_FLAGS ?= -O2 -fno-inline-functions + OPTIMIZATION_FLAGS ?= -O2 endif # option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk diff --git a/ports/nordic/Makefile b/ports/nordic/Makefile index 0d8d8878f4ca1..1e283fc699c7f 100755 --- a/ports/nordic/Makefile +++ b/ports/nordic/Makefile @@ -36,8 +36,8 @@ ifeq ($(DEBUG), 1) CFLAGS += -ggdb3 OPTIMIZATION_FLAGS = -Og else - OPTIMIZATION_FLAGS ?= -O2 -fno-inline-functions - CFLAGS += -DNDEBUG -ggdb3 + OPTIMIZATION_FLAGS ?= -O2 + CFLAGS += -DNDEBUG endif ifeq ($(NRF_DEBUG_PRINT), 1) diff --git a/ports/silabs/Makefile b/ports/silabs/Makefile index 34b6346d0d428..810ab53f1f331 100644 --- a/ports/silabs/Makefile +++ b/ports/silabs/Makefile @@ -71,7 +71,7 @@ ifeq ($(DEBUG), 1) CFLAGS += -fno-inline -fno-ipa-sra -Og else CFLAGS += -DNDEBUG - OPTIMIZATION_FLAGS ?= -Os -fno-inline-functions + OPTIMIZATION_FLAGS ?= -Os CFLAGS += -g endif diff --git a/ports/stm/Makefile b/ports/stm/Makefile index c0e64e2339de1..2ed3bddb5b7fb 100755 --- a/ports/stm/Makefile +++ b/ports/stm/Makefile @@ -38,8 +38,7 @@ ifeq ($(DEBUG), 1) CFLAGS += -fno-inline -fno-ipa-sra else CFLAGS += -DNDEBUG - OPTIMIZATION_FLAGS ?= -O2 -fno-inline-functions - CFLAGS += -ggdb3 + OPTIMIZATION_FLAGS ?= -O2 endif # to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk From 2dee1904c7efd927139effede8da8634fbb7146e Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 17 Feb 2026 13:09:45 -0500 Subject: [PATCH 334/517] -Os for some stm boards --- .../boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk | 1 + ports/stm/mpconfigport.mk | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk b/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk index 64be6f4f14242..c46a270df652a 100644 --- a/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk +++ b/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk @@ -9,6 +9,7 @@ EXTERNAL_FLASH_DEVICES = GD25Q16C,W25Q16JVxQ,W25Q64FV,W25Q32JVxQ,W25Q64JVxQ LONGINT_IMPL = MPZ INTERNAL_FLASH_FILESYSTEM = 0 +OPTIMIZATION_FLAGS = -Os MCU_SERIES = F4 MCU_VARIANT = STM32F411xE diff --git a/ports/stm/mpconfigport.mk b/ports/stm/mpconfigport.mk index 83759fc5d2405..96724a5296090 100644 --- a/ports/stm/mpconfigport.mk +++ b/ports/stm/mpconfigport.mk @@ -10,6 +10,10 @@ ifeq ($(MCU_VARIANT),$(filter $(MCU_VARIANT),STM32F405xx STM32F407xx)) USB_NUM_ENDPOINT_PAIRS = 4 endif +ifeq ($(INTERNAL_FLASH_FILESYSTEM),1) + OPTIMIZATION_FLAGS ?= -Os +endif + ifeq ($(MCU_VARIANT),STM32F407xx) UF2_FAMILY_ID ?= 0x6d0922fa endif From fce9ce6c2829226df67b329bedc59307570345cb Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 17 Feb 2026 23:31:29 +0100 Subject: [PATCH 335/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 25 +++++++++++-------------- locale/el.po | 18 ++++-------------- locale/hi.po | 18 ++++-------------- locale/ko.po | 25 +++++++++++-------------- locale/ru.po | 25 +++++++++++-------------- locale/tr.po | 18 ++++-------------- 6 files changed, 45 insertions(+), 84 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index ec160f6bdd39e..24dc429275341 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -594,7 +594,7 @@ msgstr "Již běží" msgid "Already scanning for wifi networks" msgstr "Již skenuje wifi sítě" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "Došlo k chybě při načítání '%s'\n" @@ -1110,7 +1110,6 @@ msgid "File exists" msgstr "soubor existuje" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "Soubor nenalezen" @@ -1263,7 +1262,7 @@ msgstr "Interní audio buffer je příliš malý" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c msgid "Internal error" msgstr "Interní chyba" @@ -1360,17 +1359,12 @@ msgstr "Neplatný argument" msgid "Invalid bits per value" msgstr "" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "Špatný byte %.*s" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "Chybný data_pin[%d]" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "Špatný formát" @@ -1401,7 +1395,7 @@ msgstr "Chybný soket pro TLS" msgid "Invalid state" msgstr "Chybný stav" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "Neplatná unicode escape sekvence" @@ -1409,10 +1403,6 @@ msgstr "Neplatná unicode escape sekvence" msgid "Key must be 16, 24, or 32 bytes long" msgstr "Klíč musí být dlouhý 16, 24 nebo 32 bajtů" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "Klíč nenalezen" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "Mapování LED musí korespondovat s velikostí displeje" @@ -4615,6 +4605,13 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, c-format +#~ msgid "Invalid byte %.*s" +#~ msgstr "Špatný byte %.*s" + +#~ msgid "Key not found" +#~ msgstr "Klíč nenalezen" + #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c vyžaduje int nebo char" diff --git a/locale/el.po b/locale/el.po index 7b1a182690ed9..016fb3a381e03 100644 --- a/locale/el.po +++ b/locale/el.po @@ -598,7 +598,7 @@ msgstr "Τρέχει ήδη" msgid "Already scanning for wifi networks" msgstr "Ήδη γίνεται σάρωση για δίκτυα wifi" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "Παρουσιάστηκε σφάλμα κατά την ανάκτηση '%s':\n" @@ -1118,7 +1118,6 @@ msgid "File exists" msgstr "" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "" @@ -1269,7 +1268,7 @@ msgstr "" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1366,17 +1365,12 @@ msgstr "" msgid "Invalid bits per value" msgstr "" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "" @@ -1407,7 +1401,7 @@ msgstr "" msgid "Invalid state" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "" @@ -1415,10 +1409,6 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index c11116b34f690..be00c7cf1c906 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -585,7 +585,7 @@ msgstr "" msgid "Already scanning for wifi networks" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "" @@ -1094,7 +1094,6 @@ msgid "File exists" msgstr "" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "" @@ -1245,7 +1244,7 @@ msgstr "" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1342,17 +1341,12 @@ msgstr "" msgid "Invalid bits per value" msgstr "" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "" @@ -1383,7 +1377,7 @@ msgstr "" msgid "Invalid state" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "" @@ -1391,10 +1385,6 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 99c62684dc603..8e96781ed270b 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -622,7 +622,7 @@ msgstr "이미 실행 중입니다" msgid "Already scanning for wifi networks" msgstr "이미 wifi 네트워크를 찾고 있습니다" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "%s'을(를) 검색하는 동안 오류가 발생했습니다:\n" @@ -1143,7 +1143,6 @@ msgid "File exists" msgstr "파일이 있습니다" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "파일을 찾을 수 없습니다" @@ -1296,7 +1295,7 @@ msgstr "내부 오디오 버퍼가 너무 작습니다" msgid "Internal define error" msgstr "내부 정의 오류" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c msgid "Internal error" msgstr "내부 오류" @@ -1393,17 +1392,12 @@ msgstr "잘못된 인수" msgid "Invalid bits per value" msgstr "값 당 잘못된 비트" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "잘못된 바이트 %.*s" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "잘못된 data_pins[%d]" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "잘못된 형식" @@ -1434,7 +1428,7 @@ msgstr "TLS에 대한 잘못된 소켓" msgid "Invalid state" msgstr "잘못된 상태" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "잘못된 유니코드 이스케이프" @@ -1442,10 +1436,6 @@ msgstr "잘못된 유니코드 이스케이프" msgid "Key must be 16, 24, or 32 bytes long" msgstr "키는 16, 24, 또는 32 바이트 길이여야 합니다" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "키를 찾을 수 없습니다" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "LED 매핑은 디스플레이 크기와 일치해야 합니다" @@ -4662,6 +4652,13 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, c-format +#~ msgid "Invalid byte %.*s" +#~ msgstr "잘못된 바이트 %.*s" + +#~ msgid "Key not found" +#~ msgstr "키를 찾을 수 없습니다" + #, fuzzy #~ msgid "Not supported JPEG standard" #~ msgstr "지원되지 않는 JPEG 표준" diff --git a/locale/ru.po b/locale/ru.po index cf83523e82c71..cff7a97b39609 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -598,7 +598,7 @@ msgstr "Уже запущен" msgid "Already scanning for wifi networks" msgstr "Поиск сетей wifi уже происходит" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "Произошла ошибка при получении '%s':\n" @@ -1123,7 +1123,6 @@ msgid "File exists" msgstr "Файл существует" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "Файл не найден" @@ -1284,7 +1283,7 @@ msgstr "Внутренний звуковой буфер слишком мал" msgid "Internal define error" msgstr "Внутренняя ошибка определения" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c msgid "Internal error" msgstr "Внутренняя ошибка" @@ -1381,17 +1380,12 @@ msgstr "Недопустимый аргумент" msgid "Invalid bits per value" msgstr "Недопустимое бит-на-значение" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "Неверный байт %.*s" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "Неверный data_pins[%d]" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "Недопустимый формат" @@ -1422,7 +1416,7 @@ msgstr "Неверный сокет для TLS" msgid "Invalid state" msgstr "Неверное состояние" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "Недопустимое экранирование Юникода" @@ -1430,10 +1424,6 @@ msgstr "Недопустимое экранирование Юникода" msgid "Key must be 16, 24, or 32 bytes long" msgstr "Ключ должен быть длинной 16, 24 или 32 байта" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "Ключ не найден" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "Светодиодные сопоставления должны соответствовать размеру дисплея" @@ -4681,6 +4671,13 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" +#, c-format +#~ msgid "Invalid byte %.*s" +#~ msgstr "Неверный байт %.*s" + +#~ msgid "Key not found" +#~ msgstr "Ключ не найден" + #~ msgid "Not supported JPEG standard" #~ msgstr "Не поддерживается Стандарт JPEG" diff --git a/locale/tr.po b/locale/tr.po index f8db30f3893cc..0449d8d1261e8 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -596,7 +596,7 @@ msgstr "Halihazırda çalışıyor" msgid "Already scanning for wifi networks" msgstr "Halihazırda wifi ağları için tarama yapılıyor" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "" @@ -1108,7 +1108,6 @@ msgid "File exists" msgstr "Dosya var" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "" @@ -1263,7 +1262,7 @@ msgstr "Dahili ses arabelleği çok küçük" msgid "Internal define error" msgstr "Dahili tanımlama hatası" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c msgid "Internal error" msgstr "Dahili hata" @@ -1361,17 +1360,12 @@ msgstr "Geçersiz argüman" msgid "Invalid bits per value" msgstr "Geçersiz bit başına değer" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "Geçersiz veri_pini [%d]" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "" @@ -1402,7 +1396,7 @@ msgstr "TLS için geçersiz soket" msgid "Invalid state" msgstr "Geçersiz durum" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "" @@ -1410,10 +1404,6 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "Anahtar 16, 24 veya 32 bayt uzunluğunda olmalıdır" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "LED eşlemeleri ekran boyutuyla eşleşmelidir" From 757e3a3ff75e56d228bd724696e034271d615578 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 23 Jan 2026 09:34:51 -0800 Subject: [PATCH 336/517] Add basic _bleio implementation for zephyr-cp port Adds basic scanning and advertising support. Uses BabbleSim to test on a host computer. --- .github/workflows/run-tests.yml | 5 + .gitignore | 3 + AGENTS.md | 3 + locale/circuitpython.pot | 6 + ports/zephyr-cp/AGENTS.md | 5 + ports/zephyr-cp/Kconfig.sysbuild | 1 + ports/zephyr-cp/Makefile | 6 +- ports/zephyr-cp/boards/board_aliases.cmake | 4 + ports/zephyr-cp/boards/da14695_dk_usb.conf | 20 + ports/zephyr-cp/boards/da14695_dk_usb.overlay | 10 + ports/zephyr-cp/boards/frdm_rw612.conf | 46 ++ ports/zephyr-cp/boards/frdm_rw612.overlay | 11 + .../native/native_sim/autogen_board_info.toml | 2 +- .../nrf5340bsim/autogen_board_info.toml | 115 +++++ .../native/nrf5340bsim/circuitpython.toml | 1 + .../nordic/nrf5340dk/autogen_board_info.toml | 4 +- .../nordic/nrf54h20dk/autogen_board_info.toml | 2 +- .../nordic/nrf54l15dk/autogen_board_info.toml | 2 +- .../nordic/nrf7002dk/autogen_board_info.toml | 4 +- .../boards/nrf5340bsim_nrf5340_cpuapp.conf | 26 ++ .../boards/nrf5340bsim_nrf5340_cpuapp.overlay | 28 ++ .../boards/nrf5340dk_nrf5340_cpuapp.conf | 14 + .../boards/nrf54h20dk_nrf54h20_cpuapp.conf | 7 + .../boards/nrf7002dk_nrf5340_cpuapp.conf | 4 + .../nxp/frdm_mcxn947/autogen_board_info.toml | 2 +- .../nxp/frdm_rw612/autogen_board_info.toml | 114 +++++ .../boards/nxp/frdm_rw612/circuitpython.toml | 5 + .../mimxrt1170_evk/autogen_board_info.toml | 2 +- .../da14695_dk_usb/autogen_board_info.toml | 114 +++++ .../renesas/da14695_dk_usb/circuitpython.toml | 1 + .../renesas/ek_ra6m5/autogen_board_info.toml | 2 +- .../renesas/ek_ra8d1/autogen_board_info.toml | 2 +- .../boards/renesas_da14695_dk_usb.conf | 20 + .../nucleo_n657x0_q/autogen_board_info.toml | 2 +- .../nucleo_u575zi_q/autogen_board_info.toml | 2 +- .../st/stm32h7b3i_dk/autogen_board_info.toml | 2 +- .../stm32wba65i_dk1/autogen_board_info.toml | 115 +++++ .../st/stm32wba65i_dk1/circuitpython.toml | 1 + ports/zephyr-cp/boards/stm32wba65i_dk1.conf | 24 + .../zephyr-cp/boards/stm32wba65i_dk1.overlay | 63 +++ ports/zephyr-cp/common-hal/_bleio/Adapter.c | 412 ++++++++++++++++++ ports/zephyr-cp/common-hal/_bleio/Adapter.h | 29 ++ ports/zephyr-cp/common-hal/_bleio/Attribute.c | 7 + ports/zephyr-cp/common-hal/_bleio/Attribute.h | 9 + .../common-hal/_bleio/Characteristic.c | 66 +++ .../common-hal/_bleio/Characteristic.h | 39 ++ .../common-hal/_bleio/CharacteristicBuffer.c | 72 +++ .../common-hal/_bleio/CharacteristicBuffer.h | 18 + .../zephyr-cp/common-hal/_bleio/Connection.c | 56 +++ .../zephyr-cp/common-hal/_bleio/Connection.h | 48 ++ .../zephyr-cp/common-hal/_bleio/Descriptor.c | 29 ++ .../zephyr-cp/common-hal/_bleio/Descriptor.h | 23 + .../common-hal/_bleio/PacketBuffer.c | 65 +++ .../common-hal/_bleio/PacketBuffer.h | 20 + ports/zephyr-cp/common-hal/_bleio/Service.c | 45 ++ ports/zephyr-cp/common-hal/_bleio/Service.h | 22 + ports/zephyr-cp/common-hal/_bleio/UUID.c | 51 +++ ports/zephyr-cp/common-hal/_bleio/UUID.h | 15 + ports/zephyr-cp/common-hal/_bleio/__init__.c | 74 ++++ ports/zephyr-cp/common-hal/_bleio/__init__.h | 9 + ports/zephyr-cp/common-hal/wifi/__init__.c | 21 +- .../common-hal/zephyr_kernel/__init__.c | 10 + .../zephyr-cp/cptools/build_circuitpython.py | 18 +- .../zephyr-cp/cptools/tests/test_zephyr2cp.py | 39 ++ ports/zephyr-cp/cptools/zephyr2cp.py | 59 ++- ports/zephyr-cp/prj.conf | 17 +- ports/zephyr-cp/supervisor/port.c | 77 +++- ports/zephyr-cp/supervisor/usb.c | 28 +- ports/zephyr-cp/sysbuild.cmake | 2 + ports/zephyr-cp/tests/__init__.py | 136 ++++++ ports/zephyr-cp/tests/bsim/__init__.py | 3 + ports/zephyr-cp/tests/bsim/conftest.py | 222 ++++++++++ .../zephyr-cp/tests/bsim/test_bsim_basics.py | 34 ++ .../tests/bsim/test_bsim_ble_advertising.py | 111 +++++ .../tests/bsim/test_bsim_ble_name.py | 31 ++ .../tests/bsim/test_bsim_ble_scan.py | 151 +++++++ ports/zephyr-cp/tests/conftest.py | 315 +++++-------- ports/zephyr-cp/tests/docs/babblesim.md | 69 +++ ports/zephyr-cp/tests/test_basics.py | 130 +++--- ports/zephyr-cp/tests/test_i2c.py | 60 +-- shared-bindings/_bleio/PacketBuffer.h | 1 + 81 files changed, 3111 insertions(+), 332 deletions(-) create mode 100644 AGENTS.md create mode 100644 ports/zephyr-cp/AGENTS.md create mode 100644 ports/zephyr-cp/boards/da14695_dk_usb.conf create mode 100644 ports/zephyr-cp/boards/da14695_dk_usb.overlay create mode 100644 ports/zephyr-cp/boards/frdm_rw612.conf create mode 100644 ports/zephyr-cp/boards/frdm_rw612.overlay create mode 100644 ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf create mode 100644 ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay create mode 100644 ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/nxp/frdm_rw612/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf create mode 100644 ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/stm32wba65i_dk1.conf create mode 100644 ports/zephyr-cp/boards/stm32wba65i_dk1.overlay create mode 100644 ports/zephyr-cp/common-hal/_bleio/Adapter.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/Adapter.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/Attribute.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/Attribute.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/Characteristic.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/Characteristic.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/Connection.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/Connection.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/Descriptor.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/Descriptor.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/Service.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/Service.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/UUID.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/UUID.h create mode 100644 ports/zephyr-cp/common-hal/_bleio/__init__.c create mode 100644 ports/zephyr-cp/common-hal/_bleio/__init__.h create mode 100644 ports/zephyr-cp/tests/__init__.py create mode 100644 ports/zephyr-cp/tests/bsim/__init__.py create mode 100644 ports/zephyr-cp/tests/bsim/conftest.py create mode 100644 ports/zephyr-cp/tests/bsim/test_bsim_basics.py create mode 100644 ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py create mode 100644 ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py create mode 100644 ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py create mode 100644 ports/zephyr-cp/tests/docs/babblesim.md diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 01172041b6205..925ee4698542c 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -95,5 +95,10 @@ jobs: uses: ./.github/actions/deps/external - name: Build native sim target run: make -C ports/zephyr-cp -j2 BOARD=native_native_sim + - name: Build bsim + run: make -j 2 everything + working-directory: ports/zephyr-cp/tools/bsim + - name: Build native_nrf5340bsim + run: make -C ports/zephyr-cp -j2 BOARD=native_nrf5340bsim - name: Run Zephyr tests run: make -C ports/zephyr-cp test diff --git a/.gitignore b/.gitignore index f996911d8d746..fdc71c92e323d 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,6 @@ TAGS # windsurf rules .windsurfrules + +# git-review-web outputs +.review diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000000..145e31c127159 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,3 @@ +- Capture CircuitPython output by finding the matching device in `/dev/serial/by-id` +- You can mount the CIRCUITPY drive by doing `udisksctl mount -b /dev/disk/by-label/CIRCUITPY` and access it via `/run/media//CIRCUITPY`. +- `circup` is a command line tool to install libraries and examples to CIRCUITPY. diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index c135442f6001f..37c2ca250c997 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1332,6 +1332,10 @@ msgstr "" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "" @@ -3811,6 +3815,7 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4294,6 +4299,7 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" diff --git a/ports/zephyr-cp/AGENTS.md b/ports/zephyr-cp/AGENTS.md new file mode 100644 index 0000000000000..a2413e64e428f --- /dev/null +++ b/ports/zephyr-cp/AGENTS.md @@ -0,0 +1,5 @@ +- Build a board by doing `make BOARD=_`. +- The corresponding configuration files are in `boards//` +- The files (not folders) in `boards/` directory are used by Zephyr. +- To flash it on a board do `make BOARD=_ flash`. +- Zephyr board docs are at `zephyr/boards//`. diff --git a/ports/zephyr-cp/Kconfig.sysbuild b/ports/zephyr-cp/Kconfig.sysbuild index cd74ff13592c1..11b49446f422f 100644 --- a/ports/zephyr-cp/Kconfig.sysbuild +++ b/ports/zephyr-cp/Kconfig.sysbuild @@ -8,6 +8,7 @@ config NET_CORE_BOARD default "nrf5340dk/nrf5340/cpunet" if $(BOARD) = "nrf5340dk" default "nrf7002dk/nrf5340/cpunet" if $(BOARD) = "nrf7002dk" default "nrf5340_audio_dk/nrf5340/cpunet" if $(BOARD) = "nrf5340_audio_dk" + default "nrf5340bsim/nrf5340/cpunet" if $(BOARD) = "nrf5340bsim" config NET_CORE_IMAGE_HCI_IPC bool "HCI IPC image on network core" diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index 622fe4901a99f..c4feead23b9b5 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -8,7 +8,8 @@ BUILD ?= build-$(BOARD) TRANSLATION ?= en_US -.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug run clean menuconfig all clean-all test fetch-port-submodules + +.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug run clean menuconfig all clean-all test fetch-port-submodules $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) @@ -26,6 +27,9 @@ $(BUILD)/firmware.exe: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west flash -d $(BUILD) +recover: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + west flash --recover -d $(BUILD) + debug: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west debug -d $(BUILD) diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index 954bce0b29823..ddf1627a924db 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -1,13 +1,17 @@ set(pca10056_BOARD_ALIAS nrf52840dk/nrf52840) set(renesas_ek_ra6m5_BOARD_ALIAS ek_ra6m5) set(renesas_ek_ra8d1_BOARD_ALIAS ek_ra8d1) +set(renesas_da14695_dk_usb_BOARD_ALIAS da14695_dk_usb) set(native_native_sim_BOARD_ALIAS native_sim) +set(native_nrf5340bsim_BOARD_ALIAS nrf5340bsim/nrf5340/cpuapp) set(nordic_nrf54l15dk_BOARD_ALIAS nrf54l15dk/nrf54l15/cpuapp) set(nordic_nrf54h20dk_BOARD_ALIAS nrf54h20dk/nrf54h20/cpuapp) set(nordic_nrf5340dk_BOARD_ALIAS nrf5340dk/nrf5340/cpuapp) set(nordic_nrf7002dk_BOARD_ALIAS nrf7002dk/nrf5340/cpuapp) set(nxp_frdm_mcxn947_BOARD_ALIAS frdm_mcxn947/mcxn947/cpu0) +set(nxp_frdm_rw612_BOARD_ALIAS frdm_rw612) set(nxp_mimxrt1170_evk_BOARD_ALIAS mimxrt1170_evk@A/mimxrt1176/cm7) set(st_stm32h7b3i_dk_BOARD_ALIAS stm32h7b3i_dk) +set(st_stm32wba65i_dk1_BOARD_ALIAS stm32wba65i_dk1) set(st_nucleo_u575zi_q_BOARD_ALIAS nucleo_u575zi_q/stm32u575xx) set(st_nucleo_n657x0_q_BOARD_ALIAS nucleo_n657x0_q/stm32n657xx) diff --git a/ports/zephyr-cp/boards/da14695_dk_usb.conf b/ports/zephyr-cp/boards/da14695_dk_usb.conf new file mode 100644 index 0000000000000..145a93934070f --- /dev/null +++ b/ports/zephyr-cp/boards/da14695_dk_usb.conf @@ -0,0 +1,20 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/da14695_dk_usb.overlay b/ports/zephyr-cp/boards/da14695_dk_usb.overlay new file mode 100644 index 0000000000000..fbc1817c759c6 --- /dev/null +++ b/ports/zephyr-cp/boards/da14695_dk_usb.overlay @@ -0,0 +1,10 @@ +&flash0 { + partitions{ + circuitpy_partition: partition@118000 { + label = "circuitpy"; + reg = <0x118000 (DT_SIZE_M(4) - DT_SIZE_K(1120))>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/frdm_rw612.conf b/ports/zephyr-cp/boards/frdm_rw612.conf new file mode 100644 index 0000000000000..7f063218153e5 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_rw612.conf @@ -0,0 +1,46 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y +CONFIG_NET_SOCKETS_POSIX_NAMES=y + +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y + +CONFIG_NET_HOSTNAME_ENABLE=y +CONFIG_NET_HOSTNAME_DYNAMIC=y +CONFIG_NET_HOSTNAME="circuitpython" + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_RSA_C=y +CONFIG_MBEDTLS_PKCS1_V15=y +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=y +CONFIG_MBEDTLS_ENTROPY_C=y +CONFIG_MBEDTLS_CTR_DRBG_ENABLED=y +CONFIG_MBEDTLS_USE_PSA_CRYPTO=n + +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=8 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 + +CONFIG_UDC_WORKQUEUE_STACK_SIZE=1024 diff --git a/ports/zephyr-cp/boards/frdm_rw612.overlay b/ports/zephyr-cp/boards/frdm_rw612.overlay new file mode 100644 index 0000000000000..1c38f42c4abd2 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_rw612.overlay @@ -0,0 +1,11 @@ +&w25q512jvfiq { + partitions { + /delete-node/ storage_partition; + circuitpy_partition: partition@620000 { + label = "circuitpy"; + reg = <0x00620000 (DT_SIZE_M(58) - DT_SIZE_K(128))>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 2cf9d7127d57e..73897f7162082 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "POSIX/Native Boards Native simulator - native_sim" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml new file mode 100644 index 0000000000000..9ddbb2153fb63 --- /dev/null +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -0,0 +1,115 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "POSIX/Native Boards nRF5340 simulated boards (BabbleSim)" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 334cf4001ebe9..52beeda076fc3 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -2,8 +2,8 @@ name = "Nordic Semiconductor nRF5340 DK" [modules] -__future__ = false -_bleio = false +__future__ = true +_bleio = true # Zephyr board has _bleio _eve = false _pew = false _pixelmap = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index 40edb7b705827..2759dfb89c1c7 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "Nordic Semiconductor nRF54H20 DK" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 509f14cd20d26..6df832f607b2c 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "Nordic Semiconductor nRF54L15 DK" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 3b0ecedcc6385..d713552d87be7 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -2,8 +2,8 @@ name = "Nordic Semiconductor nRF7002 DK" [modules] -__future__ = false -_bleio = false +__future__ = true +_bleio = true # Zephyr board has _bleio _eve = false _pew = false _pixelmap = false diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf new file mode 100644 index 0000000000000..f9507bd031926 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf @@ -0,0 +1,26 @@ +# Configuration for nrf5340bsim simulated board +# Mirror settings from native_sim.conf for compatibility + +CONFIG_GPIO=y + +# Enable Bluetooth stack - bsim is for BT simulation +CONFIG_BT=y +CONFIG_BT_HCI=y +CONFIG_BT_HCI_IPC=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_BROADCASTER=y + +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 + +# So we can test safe mode +CONFIG_NATIVE_SIM_REBOOT=y + +# Ensure the network core image starts when using native simulator +CONFIG_NATIVE_SIMULATOR_AUTOSTART_MCU=y + +CONFIG_TRACING=y +CONFIG_TRACING_PERFETTO=y +CONFIG_TRACING_SYNC=y +CONFIG_TRACING_BACKEND_POSIX=y +CONFIG_TRACING_GPIO=y diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay new file mode 100644 index 0000000000000..eeb043c6f3ce3 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/ { + chosen { + zephyr,sram = &sram0; + }; +}; + +&sram0 { + compatible = "zephyr,memory-region", "mmio-sram"; + zephyr,memory-region = "SRAM"; +}; + +&flash0 { + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + circuitpy_partition: partition@0 { + label = "circuitpy"; + reg = <0x00000000 DT_SIZE_K(1024)>; + }; + }; +}; + +/* Note: bsim doesn't have USB, so we don't include app.overlay */ diff --git a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf index fa0532e815069..145a93934070f 100644 --- a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -4,3 +4,17 @@ CONFIG_BT_CENTRAL=y CONFIG_BT_BROADCASTER=y CONFIG_BT_OBSERVER=y CONFIG_BT_EXT_ADV=y + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf index f7443ecfa33d4..a55b90c50e747 100644 --- a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -1 +1,8 @@ CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE=4096 + +# Reduce flash usage for this board. +CONFIG_LOG=y +CONFIG_LOG_MAX_LEVEL=2 +CONFIG_ASSERT=n +CONFIG_FRAME_POINTER=n +CONFIG_HW_STACK_PROTECTION=n diff --git a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf index c61851fad2d24..9829d4a15817e 100644 --- a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf @@ -4,6 +4,10 @@ CONFIG_WIFI=y CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y CONFIG_MBEDTLS_USE_PSA_CRYPTO=n +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 + CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_CENTRAL=y diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 7e03594f1d1ec..233796fc6f443 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "NXP Semiconductors FRDM-MCXN947" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml new file mode 100644 index 0000000000000..c9bd2729b9e6d --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -0,0 +1,114 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "NXP Semiconductors FRDM_RW612" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = true # Zephyr board has busio +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = true # Zephyr networking enabled +spitarget = false +ssl = true # Zephyr networking enabled +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = true # Zephyr board has wifi +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/circuitpython.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/circuitpython.toml new file mode 100644 index 0000000000000..9bceea470cab1 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/circuitpython.toml @@ -0,0 +1,5 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +BLOBS=["hal_nxp"] + +[blob_fetch_args] +hal_nxp = ["--allow-regex", "^rw61x/"] diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index 4398a38ab8799..4926b5c9a6cce 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "NXP Semiconductors MIMXRT1170-EVK/EVKB" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml new file mode 100644 index 0000000000000..e5adbfd582d3f --- /dev/null +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -0,0 +1,114 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Renesas Electronics Corporation DA14695 Development Kit USB" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = true # Zephyr board has busio +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index d60e8eb4b26f1..2ea2cea90b775 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "Renesas Electronics Corporation RA6M5 Evaluation Kit" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 8743994bbc72a..3be48a8b72c3f 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "Renesas Electronics Corporation RA8D1 Evaluation Kit" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf b/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf new file mode 100644 index 0000000000000..145a93934070f --- /dev/null +++ b/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf @@ -0,0 +1,20 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index 9b10fbe3c2d86..4b9c1053f2289 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "STMicroelectronics Nucleo N657X0-Q" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index ef54f4bb0f7fb..ec78a62f066e2 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "STMicroelectronics Nucleo U575ZI Q" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index 30ac22d00ccd7..24e44662e4016 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -2,7 +2,7 @@ name = "STMicroelectronics STM32H7B3I Discovery kit" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml new file mode 100644 index 0000000000000..e26084cc43ee3 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -0,0 +1,115 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "STMicroelectronics STM32WBA65I Discovery kit" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = false +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml new file mode 100644 index 0000000000000..83e6bcd39c4f9 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] diff --git a/ports/zephyr-cp/boards/stm32wba65i_dk1.conf b/ports/zephyr-cp/boards/stm32wba65i_dk1.conf new file mode 100644 index 0000000000000..55d951959e669 --- /dev/null +++ b/ports/zephyr-cp/boards/stm32wba65i_dk1.conf @@ -0,0 +1,24 @@ +# USB OTG on STM32WBA requires VOS Range 1. Keep HCLK > 16 MHz. +CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=32000000 + +# Bluetooth doesn't start up for some reason. +# CONFIG_BT=y +# CONFIG_BT_PERIPHERAL=y +# CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y +CONFIG_BT_STM32WBA_USE_TEMP_BASED_CALIB=n + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay b/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay new file mode 100644 index 0000000000000..1b6e55ba462b3 --- /dev/null +++ b/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay @@ -0,0 +1,63 @@ +&flash0 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 DT_SIZE_K(64)>; + }; + + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x00010000 DT_SIZE_K(928)>; + }; + + storage_partition: partition@f80000 { + label = "storage"; + reg = <0x001e0000 DT_SIZE_K(64)>; + }; + + circuitpy_partition: partition@108000 { + label = "circuitpy"; + reg = <0x00108000 DT_SIZE_K(992)>; + }; + }; +}; + +&rng { + status = "okay"; +}; + +/* + * USB on STM32WBA requires VOS Range 1. Zephyr selects VOS from HCLK, and + * 16 MHz keeps it in Range 2, which trips an assertion in udc_stm32. + * Run SYSCLK from full 32 MHz HSE so VOS is set to Range 1. + */ +&clk_hse { + /delete-property/ hse-div2; +}; + +&rcc { + clock-frequency = ; + ahb5-prescaler = <1>; +}; + +zephyr_udc0: &usbotg_hs { + clocks = <&rcc STM32_CLOCK(AHB2, 14)>, + <&rcc STM32_SRC_HSE OTGHS_SEL(0)>; + pinctrl-0 = <&usb_otg_hs_dm_pd7 &usb_otg_hs_dp_pd6>; + pinctrl-names = "default"; + status = "okay"; +}; + +&otghs_phy { + /* OTG HS clock source is 32 MHz HSE */ + clock-reference = "SYSCFG_OTG_HS_PHY_CLK_32MHz"; + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.c b/ports/zephyr-cp/common-hal/_bleio/Adapter.c new file mode 100644 index 0000000000000..7257f54c35f9e --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -0,0 +1,412 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include +#include +#include +#include + +#include "py/runtime.h" +#include "bindings/zephyr_kernel/__init__.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-module/_bleio/Address.h" +#include "shared-module/_bleio/ScanResults.h" +#include "supervisor/shared/tick.h" + +bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; + +static bool scan_callbacks_registered = false; +static bleio_scanresults_obj_t *active_scan_results = NULL; +static struct bt_le_scan_cb scan_callbacks; +static bool ble_advertising = false; +static bool ble_adapter_enabled = true; + +#define BLEIO_ADV_MAX_FIELDS 16 +#define BLEIO_ADV_MAX_DATA_LEN 31 +static struct bt_data adv_data[BLEIO_ADV_MAX_FIELDS]; +static struct bt_data scan_resp_data[BLEIO_ADV_MAX_FIELDS]; +static uint8_t adv_data_storage[BLEIO_ADV_MAX_DATA_LEN]; +static uint8_t scan_resp_storage[BLEIO_ADV_MAX_DATA_LEN]; + +static uint8_t bleio_address_type_from_zephyr(const bt_addr_le_t *addr) { + if (addr == NULL) { + return BLEIO_ADDRESS_TYPE_PUBLIC; + } + + switch (addr->type) { + case BT_ADDR_LE_PUBLIC: + case BT_ADDR_LE_PUBLIC_ID: + return BLEIO_ADDRESS_TYPE_PUBLIC; + case BT_ADDR_LE_RANDOM: + case BT_ADDR_LE_RANDOM_ID: + case BT_ADDR_LE_UNRESOLVED: + if (BT_ADDR_IS_RPA(&addr->a)) { + return BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE; + } + if (BT_ADDR_IS_NRPA(&addr->a)) { + return BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE; + } + return BLEIO_ADDRESS_TYPE_RANDOM_STATIC; + default: + return BLEIO_ADDRESS_TYPE_PUBLIC; + } +} + +static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf) { + if (active_scan_results == NULL || info == NULL || buf == NULL) { + return; + } + + const bool connectable = (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0; + const bool scan_response = (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0; + const bt_addr_le_t *addr = info->addr; + + uint8_t addr_bytes[NUM_BLEIO_ADDRESS_BYTES] = {0}; + if (addr != NULL) { + memcpy(addr_bytes, addr->a.val, sizeof(addr_bytes)); + } + + shared_module_bleio_scanresults_append(active_scan_results, + supervisor_ticks_ms64(), + connectable, + scan_response, + info->rssi, + addr_bytes, + bleio_address_type_from_zephyr(addr), + buf->data, + buf->len); +} + +static void scan_timeout_cb(void) { + if (active_scan_results == NULL) { + return; + } + shared_module_bleio_scanresults_set_done(active_scan_results, true); + active_scan_results = NULL; +} + +// We need to disassemble the full advertisement packet because the Zephyr takes +// in each ADT in an array. +static size_t bleio_parse_adv_data(const uint8_t *raw, size_t raw_len, struct bt_data *out, + size_t out_len, uint8_t *storage, size_t storage_len) { + size_t count = 0; + size_t offset = 0; + size_t storage_offset = 0; + + while (offset < raw_len) { + uint8_t field_len = raw[offset]; + if (field_len == 0) { + offset++; + continue; + } + uint8_t data_len = field_len - 1; + if (offset + field_len + 1 > raw_len || + count >= out_len || + field_len < 1 || + storage_offset + data_len > storage_len) { + mp_raise_ValueError(MP_ERROR_TEXT("Invalid advertising data")); + } + uint8_t type = raw[offset + 1]; + memcpy(storage + storage_offset, raw + offset + 2, data_len); + out[count].type = type; + out[count].data_len = data_len; + out[count].data = storage + storage_offset; + storage_offset += data_len; + count++; + offset += field_len + 1; + } + + return count; +} + +void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) { + if (enabled) { + if (!bt_is_ready()) { + int err = bt_enable(NULL); + raise_zephyr_error(err); + if (err != 0) { + return; + } + } + ble_adapter_enabled = true; + return; + } + + // On Zephyr bsim + HCI IPC, disabling and immediately re-enabling BLE can + // race endpoint rebinding during soft reload. Keep the controller running, + // but present adapter.enabled=False to CircuitPython code. + common_hal_bleio_adapter_stop_scan(self); + common_hal_bleio_adapter_stop_advertising(self); + ble_adapter_enabled = false; +} + +bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self) { + return ble_adapter_enabled; +} + +mp_int_t common_hal_bleio_adapter_get_tx_power(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_adapter_set_tx_power(bleio_adapter_obj_t *self, mp_int_t tx_power) { + mp_raise_NotImplementedError(NULL); +} + +bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) { + (void)self; + const char *name = bt_get_name(); + return mp_obj_new_str(name, strlen(name)); +} + +void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name) { + (void)self; + size_t len = strlen(name); + int err = 0; + if (len > CONFIG_BT_DEVICE_NAME_MAX) { + char truncated[CONFIG_BT_DEVICE_NAME_MAX + 1]; + memcpy(truncated, name, CONFIG_BT_DEVICE_NAME_MAX); + truncated[CONFIG_BT_DEVICE_NAME_MAX] = '\0'; + err = bt_set_name(truncated); + } else { + err = bt_set_name(name); + } + if (err != 0) { + raise_zephyr_error(err); + } +} + +void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, mp_float_t interval, + mp_buffer_info_t *advertising_data_bufinfo, + mp_buffer_info_t *scan_response_data_bufinfo, + mp_int_t tx_power, const bleio_address_obj_t *directed_to) { + (void)tx_power; + (void)directed_to; + (void)interval; + + if (advertising_data_bufinfo->len > BLEIO_ADV_MAX_DATA_LEN || + scan_response_data_bufinfo->len > BLEIO_ADV_MAX_DATA_LEN) { + mp_raise_NotImplementedError(NULL); + } + + if (timeout != 0) { + mp_raise_NotImplementedError(NULL); + } + + if (ble_advertising) { + raise_zephyr_error(-EALREADY); + } + + bt_addr_le_t id_addrs[CONFIG_BT_ID_MAX]; + size_t id_count = CONFIG_BT_ID_MAX; + bt_id_get(id_addrs, &id_count); + if (id_count == 0 || bt_addr_le_eq(&id_addrs[BT_ID_DEFAULT], BT_ADDR_LE_ANY)) { + int id = bt_id_create(NULL, NULL); + if (id < 0) { + printk("Failed to create identity address: %d\n", id); + raise_zephyr_error(id); + } + } + + size_t adv_count = bleio_parse_adv_data(advertising_data_bufinfo->buf, + advertising_data_bufinfo->len, + adv_data, + BLEIO_ADV_MAX_FIELDS, + adv_data_storage, + sizeof(adv_data_storage)); + + size_t scan_resp_count = 0; + if (scan_response_data_bufinfo->len > 0) { + scan_resp_count = bleio_parse_adv_data(scan_response_data_bufinfo->buf, + scan_response_data_bufinfo->len, + scan_resp_data, + BLEIO_ADV_MAX_FIELDS, + scan_resp_storage, + sizeof(scan_resp_storage)); + } + + if (anonymous) { + mp_raise_NotImplementedError(NULL); + } + + struct bt_le_adv_param adv_params; + if (connectable) { + adv_params = (struct bt_le_adv_param)BT_LE_ADV_PARAM_INIT( + BT_LE_ADV_OPT_CONN, + BT_GAP_ADV_FAST_INT_MIN_1, + BT_GAP_ADV_FAST_INT_MAX_1, + NULL); + } else if (scan_resp_count > 0) { + adv_params = (struct bt_le_adv_param)BT_LE_ADV_PARAM_INIT( + BT_LE_ADV_OPT_SCANNABLE, + BT_GAP_ADV_FAST_INT_MIN_2, + BT_GAP_ADV_FAST_INT_MAX_2, + NULL); + } else { + adv_params = (struct bt_le_adv_param)BT_LE_ADV_PARAM_INIT( + 0, + BT_GAP_ADV_FAST_INT_MIN_2, + BT_GAP_ADV_FAST_INT_MAX_2, + NULL); + } + + raise_zephyr_error(bt_le_adv_start(&adv_params, + adv_data, + adv_count, + scan_resp_count > 0 ? scan_resp_data : NULL, + scan_resp_count)); + + ble_advertising = true; +} + +void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { + (void)self; + if (!ble_advertising) { + return; + } + bt_le_adv_stop(); + ble_advertising = false; +} + +bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) { + (void)self; + return ble_advertising; +} + +mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t *prefixes, size_t prefix_length, bool extended, mp_int_t buffer_size, mp_float_t timeout, mp_float_t interval, mp_float_t window, mp_int_t minimum_rssi, bool active) { + (void)extended; + + if (self->scan_results != NULL) { + if (!shared_module_bleio_scanresults_get_done(self->scan_results)) { + common_hal_bleio_adapter_stop_scan(self); + } else { + self->scan_results = NULL; + } + } + + int err = 0; + + self->scan_results = shared_module_bleio_new_scanresults(buffer_size, prefixes, prefix_length, minimum_rssi); + active_scan_results = self->scan_results; + + if (!scan_callbacks_registered) { + scan_callbacks.recv = scan_recv_cb; + scan_callbacks.timeout = scan_timeout_cb; + err = bt_le_scan_cb_register(&scan_callbacks); + if (err != 0) { + self->scan_results = NULL; + active_scan_results = NULL; + raise_zephyr_error(err); + } + scan_callbacks_registered = true; + } + + uint16_t interval_units = (uint16_t)((interval / 0.000625f) + 0.5f); + uint16_t window_units = (uint16_t)((window / 0.000625f) + 0.5f); + uint32_t timeout_units = 0; + + if (timeout > 0.0f) { + timeout_units = (uint32_t)(timeout * 100.0f + 0.5f); + if (timeout_units > UINT16_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("timeout must be < 655.35 secs")); + } + if (timeout_units == 0) { + mp_raise_ValueError(MP_ERROR_TEXT("non-zero timeout must be > 0.01")); + } + } + + struct bt_le_scan_param scan_params = { + .type = active ? BT_LE_SCAN_TYPE_ACTIVE : BT_LE_SCAN_TYPE_PASSIVE, + .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, + .interval = interval_units, + .window = window_units, + .timeout = (uint16_t)timeout_units, + .interval_coded = 0, + .window_coded = 0, + }; + + err = bt_le_scan_start(&scan_params, NULL); + if (err != 0) { + self->scan_results = NULL; + active_scan_results = NULL; + raise_zephyr_error(err); + } + + return MP_OBJ_FROM_PTR(self->scan_results); +} + +void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) { + if (self->scan_results == NULL) { + return; + } + bt_le_scan_stop(); + shared_module_bleio_scanresults_set_done(self->scan_results, true); + active_scan_results = NULL; + self->scan_results = NULL; +} + +bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) { + return false; +} + +mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self) { + return false; +} + +void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter) { + // Nothing to do for now. +} + +void bleio_adapter_reset(bleio_adapter_obj_t *adapter) { + if (adapter == NULL) { + return; + } + adapter->scan_results = NULL; + adapter->connection_objs = NULL; + active_scan_results = NULL; + ble_advertising = false; + ble_adapter_enabled = bt_is_ready(); +} + +bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void) { + return &common_hal_bleio_adapter_obj; +} + +uint16_t bleio_adapter_get_name(char *buf, uint16_t len) { + const char *name = bt_get_name(); + uint16_t full_len = strlen(name); + if (len > 0) { + uint16_t copy_len = len < full_len ? len : full_len; + memcpy(buf, name, copy_len); + } + return full_len; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.h b/ports/zephyr-cp/common-hal/_bleio/Adapter.h new file mode 100644 index 0000000000000..7cb3dc402c609 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.h @@ -0,0 +1,29 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/ScanResults.h" + +#define BLEIO_TOTAL_CONNECTION_COUNT 5 +#define BLEIO_HANDLE_INVALID 0xffff + +extern bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; + +typedef struct { + mp_obj_base_t base; + bleio_scanresults_obj_t *scan_results; + mp_obj_t name; + mp_obj_tuple_t *connection_objs; + bool user_advertising; +} bleio_adapter_obj_t; + +void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter); +void bleio_adapter_reset(bleio_adapter_obj_t *adapter); diff --git a/ports/zephyr-cp/common-hal/_bleio/Attribute.c b/ports/zephyr-cp/common-hal/_bleio/Attribute.c new file mode 100644 index 0000000000000..cf8a818e6a74e --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Attribute.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Attribute is defined in shared-module, no port-specific implementation needed diff --git a/ports/zephyr-cp/common-hal/_bleio/Attribute.h b/ports/zephyr-cp/common-hal/_bleio/Attribute.h new file mode 100644 index 0000000000000..417ac3c454955 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Attribute.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/_bleio/Attribute.h" diff --git a/ports/zephyr-cp/common-hal/_bleio/Characteristic.c b/ports/zephyr-cp/common-hal/_bleio/Characteristic.c new file mode 100644 index 0000000000000..6e71f84862781 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Characteristic.c @@ -0,0 +1,66 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" + +bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self) { + return self->props; +} + +mp_obj_tuple_t *common_hal_bleio_characteristic_get_descriptors(bleio_characteristic_obj_t *self) { + return mp_obj_new_tuple(self->descriptor_list->len, self->descriptor_list->items); +} + +bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) { + return self->service; +} + +bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) { + return self->uuid; +} + +size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t *self) { + return self->max_length; +} + +size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo, const char *user_description) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_characteristic_deinited(bleio_characteristic_obj_t *self) { + return self->service == NULL; +} + +void common_hal_bleio_characteristic_deinit(bleio_characteristic_obj_t *self) { + // Nothing to do +} + +void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { + mp_raise_NotImplementedError(NULL); +} + +void bleio_characteristic_set_observer(bleio_characteristic_obj_t *self, mp_obj_t observer) { + self->observer = observer; +} + +void bleio_characteristic_clear_observer(bleio_characteristic_obj_t *self) { + self->observer = mp_const_none; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Characteristic.h b/ports/zephyr-cp/common-hal/_bleio/Characteristic.h new file mode 100644 index 0000000000000..b9021c1329523 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Characteristic.h @@ -0,0 +1,39 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "py/objlist.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-module/_bleio/Characteristic.h" +#include "common-hal/_bleio/Descriptor.h" +#include "common-hal/_bleio/Service.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct _bleio_characteristic_obj { + mp_obj_base_t base; + bleio_service_obj_t *service; + bleio_uuid_obj_t *uuid; + mp_obj_t observer; + uint8_t *current_value; + uint16_t current_value_len; + uint16_t current_value_alloc; + uint16_t max_length; + uint16_t def_handle; + uint16_t handle; + bleio_characteristic_properties_t props; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; + mp_obj_list_t *descriptor_list; + uint16_t user_desc_handle; + uint16_t cccd_handle; + uint16_t sccd_handle; + bool fixed_length; +} bleio_characteristic_obj_t; + +void bleio_characteristic_set_observer(bleio_characteristic_obj_t *self, mp_obj_t observer); +void bleio_characteristic_clear_observer(bleio_characteristic_obj_t *self); diff --git a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c new file mode 100644 index 0000000000000..e6ae3bf9b724e --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c @@ -0,0 +1,72 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" + +void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + uint8_t *buffer, size_t buffer_size, + void *static_handler_entry, + bool watch_for_interrupt_char) { + (void)self; + (void)characteristic; + (void)timeout; + (void)buffer; + (void)buffer_size; + (void)static_handler_entry; + (void)watch_for_interrupt_char; + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + size_t buffer_size) { + (void)self; + (void)characteristic; + (void)timeout; + (void)buffer_size; + mp_raise_NotImplementedError(NULL); +} + +uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) { + (void)self; + (void)data; + (void)len; + if (errcode != NULL) { + *errcode = MP_EAGAIN; + } + mp_raise_NotImplementedError(NULL); +} + +uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self) { + return self->deinited; +} + +void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) { + if (self == NULL) { + return; + } + self->deinited = true; +} + +bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) { + (void)self; + return false; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h new file mode 100644 index 0000000000000..53b10413775da --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" +#include "shared-bindings/_bleio/Characteristic.h" + +typedef struct { + mp_obj_base_t base; + bleio_characteristic_obj_t *characteristic; + bool deinited; +} bleio_characteristic_buffer_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.c b/ports/zephyr-cp/common-hal/_bleio/Connection.c new file mode 100644 index 0000000000000..b0cac6443678a --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.c @@ -0,0 +1,56 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/Connection.h" + +void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self) { + return false; +} + +mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_internal_t *self) { + return 20; +} + +bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self) { + return false; +} + +mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist) { + mp_raise_NotImplementedError(NULL); +} + +mp_float_t common_hal_bleio_connection_get_connection_interval(bleio_connection_internal_t *self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_connection_set_connection_interval(bleio_connection_internal_t *self, mp_float_t new_interval) { + mp_raise_NotImplementedError(NULL); +} + +void bleio_connection_clear(bleio_connection_internal_t *self) { + // Nothing to do +} + +uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self) { + return self->connection->conn_handle; +} + +mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection) { + mp_raise_NotImplementedError(NULL); +} + +bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle) { + return NULL; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.h b/ports/zephyr-cp/common-hal/_bleio/Connection.h new file mode 100644 index 0000000000000..b3faef63f8c18 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.h @@ -0,0 +1,48 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" +#include "py/objlist.h" + +#include "common-hal/_bleio/__init__.h" +#include "shared-module/_bleio/Address.h" +#include "common-hal/_bleio/Service.h" + +typedef enum { + PAIR_NOT_PAIRED, + PAIR_WAITING, + PAIR_PAIRED, +} pair_status_t; + +typedef struct { + uint16_t conn_handle; + bool is_central; + mp_obj_list_t *remote_service_list; + uint16_t ediv; + volatile pair_status_t pair_status; + uint8_t sec_status; + mp_obj_t connection_obj; + volatile bool conn_params_updating; + uint16_t mtu; + volatile bool do_bond_cccds; + volatile bool do_bond_keys; + uint64_t do_bond_cccds_request_time; +} bleio_connection_internal_t; + +typedef struct { + mp_obj_base_t base; + bleio_connection_internal_t *connection; + uint8_t disconnect_reason; +} bleio_connection_obj_t; + +void bleio_connection_clear(bleio_connection_internal_t *self); +uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); +mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); +bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/ports/zephyr-cp/common-hal/_bleio/Descriptor.c b/ports/zephyr-cp/common-hal/_bleio/Descriptor.c new file mode 100644 index 0000000000000..847546b1cedf0 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Descriptor.c @@ -0,0 +1,29 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Characteristic.h" + +void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { + mp_raise_NotImplementedError(NULL); +} + +bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) { + return self->uuid; +} + +bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8_t *buf, size_t len) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) { + mp_raise_NotImplementedError(NULL); +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Descriptor.h b/ports/zephyr-cp/common-hal/_bleio/Descriptor.h new file mode 100644 index 0000000000000..8abf1dcfaad4e --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Descriptor.h @@ -0,0 +1,23 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct _bleio_descriptor_obj { + mp_obj_base_t base; + bleio_uuid_obj_t *uuid; + uint16_t handle; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; + uint16_t max_length; + bool fixed_length; + uint8_t *value; + uint16_t value_length; +} bleio_descriptor_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c new file mode 100644 index 0000000000000..6cba213515658 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c @@ -0,0 +1,65 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/PacketBuffer.h" + +void common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + size_t buffer_size, size_t max_packet_size) { + (void)self; + (void)characteristic; + (void)buffer_size; + (void)max_packet_size; + mp_raise_NotImplementedError(NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len) { + (void)self; + (void)data; + (void)len; + (void)header; + (void)header_len; + mp_raise_NotImplementedError(NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len) { + (void)self; + (void)data; + (void)len; + mp_raise_NotImplementedError(NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length(bleio_packet_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length(bleio_packet_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_packet_buffer_flush(bleio_packet_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self) { + return self->deinited; +} + +void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) { + if (self == NULL) { + return; + } + self->deinited = true; +} + +bool common_hal_bleio_packet_buffer_connected(bleio_packet_buffer_obj_t *self) { + (void)self; + return false; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h new file mode 100644 index 0000000000000..c96dd9f2eb6b0 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h @@ -0,0 +1,20 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" + +typedef struct _bleio_characteristic_obj bleio_characteristic_obj_t; + +typedef void *ble_event_handler_t; + +typedef struct { + mp_obj_base_t base; + bool deinited; +} bleio_packet_buffer_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/Service.c b/ports/zephyr-cp/common-hal/_bleio/Service.c new file mode 100644 index 0000000000000..da2bf48e16f5f --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Service.c @@ -0,0 +1,45 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/Characteristic.h" + +uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t *characteristic_list) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_service_deinit(bleio_service_obj_t *self) { + // Nothing to do +} + +void common_hal_bleio_service_from_remote_service(bleio_service_obj_t *self, bleio_connection_obj_t *connection, bleio_uuid_obj_t *uuid, bool is_secondary) { + mp_raise_NotImplementedError(NULL); +} + +bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { + return self->uuid; +} + +mp_obj_tuple_t *common_hal_bleio_service_get_characteristics(bleio_service_obj_t *self) { + return mp_obj_new_tuple(self->characteristic_list->len, self->characteristic_list->items); +} + +bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self) { + return self->is_remote; +} + +bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { + return self->is_secondary; +} + +void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *initial_value_bufinfo, const char *user_description) { + mp_raise_NotImplementedError(NULL); +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Service.h b/ports/zephyr-cp/common-hal/_bleio/Service.h new file mode 100644 index 0000000000000..03ab8b5d7fd8d --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Service.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "py/objlist.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct bleio_service_obj { + mp_obj_base_t base; + bleio_uuid_obj_t *uuid; + mp_obj_t connection; + mp_obj_list_t *characteristic_list; + uint16_t start_handle; + uint16_t end_handle; + bool is_remote; + bool is_secondary; +} bleio_service_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/UUID.c b/ports/zephyr-cp/common-hal/_bleio/UUID.c new file mode 100644 index 0000000000000..3bfbffb5a2b95 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/UUID.c @@ -0,0 +1,51 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/runtime.h" +#include "shared-bindings/_bleio/UUID.h" + +void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, const uint8_t uuid128[16]) { + if (uuid16 != 0) { + // 16-bit UUID + self->size = 16; + // Convert 16-bit UUID to 128-bit + // Bluetooth Base UUID: 00000000-0000-1000-8000-00805F9B34FB + const uint8_t base_uuid[16] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + memcpy(self->uuid128, base_uuid, 16); + self->uuid128[12] = (uuid16 & 0xff); + self->uuid128[13] = (uuid16 >> 8) & 0xff; + } else { + // 128-bit UUID + self->size = 128; + memcpy(self->uuid128, uuid128, 16); + } +} + +uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) { + if (self->size == 16) { + return (self->uuid128[13] << 8) | self->uuid128[12]; + } + return 0; +} + +void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) { + memcpy(uuid128, self->uuid128, 16); +} + +uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self) { + return self->size; +} + +void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t *buf) { + if (self->size == 16) { + buf[0] = self->uuid128[12]; + buf[1] = self->uuid128[13]; + } else { + memcpy(buf, self->uuid128, 16); + } +} diff --git a/ports/zephyr-cp/common-hal/_bleio/UUID.h b/ports/zephyr-cp/common-hal/_bleio/UUID.h new file mode 100644 index 0000000000000..d58016cffefc9 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/UUID.h @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + uint8_t uuid128[16]; + uint8_t size; +} bleio_uuid_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.c b/ports/zephyr-cp/common-hal/_bleio/__init__.c new file mode 100644 index 0000000000000..5b8f8ece8988c --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.c @@ -0,0 +1,74 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "supervisor/shared/bluetooth/bluetooth.h" + +// The singleton _bleio.Adapter object +bleio_adapter_obj_t common_hal_bleio_adapter_obj; + +void common_hal_bleio_init(void) { + common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; +} + +void bleio_user_reset(void) { + common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); + common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); + bleio_adapter_reset(&common_hal_bleio_adapter_obj); + + if (supervisor_bluetooth_workflow_is_enabled()) { + supervisor_bluetooth_background(); + } +} + +void bleio_reset(void) { + common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; + + common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); + common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); + + // Keep Zephyr BLE transport up, but present a disabled adapter state. + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); + bleio_adapter_reset(&common_hal_bleio_adapter_obj); + + if (supervisor_bluetooth_workflow_is_enabled()) { + supervisor_start_bluetooth(); + } +} + +void common_hal_bleio_gc_collect(void) { + // Nothing to do for stubs +} + +void common_hal_bleio_check_connected(uint16_t conn_handle) { + mp_raise_NotImplementedError(NULL); +} + +uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) { + mp_raise_NotImplementedError(NULL); +} + +size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { + mp_raise_NotImplementedError(NULL); +} + +size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { + mp_raise_NotImplementedError(NULL); +} diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.h b/ports/zephyr-cp/common-hal/_bleio/__init__.h new file mode 100644 index 0000000000000..9c30c7c8063b3 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Placeholder for Zephyr-specific BLE defines diff --git a/ports/zephyr-cp/common-hal/wifi/__init__.c b/ports/zephyr-cp/common-hal/wifi/__init__.c index 57f073a9cbb08..f043734ebfb4d 100644 --- a/ports/zephyr-cp/common-hal/wifi/__init__.c +++ b/ports/zephyr-cp/common-hal/wifi/__init__.c @@ -29,6 +29,8 @@ wifi_radio_obj_t common_hal_wifi_radio_obj; #include #include +#include + #define MAC_ADDRESS_LENGTH 6 static void schedule_background_on_cp_core(void *arg) { @@ -44,18 +46,23 @@ static void schedule_background_on_cp_core(void *arg) { static struct net_mgmt_event_callback wifi_cb; static struct net_mgmt_event_callback ipv4_cb; -static void _event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, struct net_if *iface) { +static void _event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface) { wifi_radio_obj_t *self = &common_hal_wifi_radio_obj; - printk("_event_handler cb %p event %08x if %p\n", cb, mgmt_event, iface); + printk("_event_handler cb %p event 0x%" PRIx64 " if %p\n", cb, mgmt_event, iface); switch (mgmt_event) { - case NET_EVENT_WIFI_SCAN_RESULT: + case NET_EVENT_WIFI_SCAN_RESULT: { printk("NET_EVENT_WIFI_SCAN_RESULT\n"); - struct wifi_scan_result *result = (struct wifi_scan_result *)cb->info; - if (self->current_scan != NULL) { + #if defined(CONFIG_NET_MGMT_EVENT_INFO) + const struct wifi_scan_result *result = cb->info; + if (result != NULL && self->current_scan != NULL) { wifi_scannednetworks_scan_result(self->current_scan, result); } + #else + printk("Scan result info unavailable (CONFIG_NET_MGMT_EVENT_INFO disabled)\n"); + #endif break; + } case NET_EVENT_WIFI_SCAN_DONE: printk("NET_EVENT_WIFI_SCAN_DONE\n"); if (self->current_scan != NULL) { @@ -281,6 +288,7 @@ void common_hal_wifi_init(bool user_initiated) { net_mgmt_add_event_callback(&wifi_cb); net_mgmt_add_event_callback(&ipv4_cb); + #if defined(CONFIG_NET_HOSTNAME) // Set the default hostname capped at NET_HOSTNAME_MAX_LEN characters. We trim off // the start of the board name (likely manufacturer) because the end is // often more unique to the board. @@ -301,6 +309,9 @@ void common_hal_wifi_init(bool user_initiated) { if (net_hostname_set(cpy_default_hostname, strlen(cpy_default_hostname)) != 0) { printk("setting hostname failed\n"); } + #else + printk("Hostname support disabled in Zephyr config\n"); + #endif // set station mode to avoid the default SoftAP common_hal_wifi_radio_start_station(self); // start wifi diff --git a/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c b/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c index d5e6fd080062a..b7a5bf9dbf1b4 100644 --- a/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c +++ b/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c @@ -12,6 +12,9 @@ void raise_zephyr_error(int err) { + if (err == 0) { + return; + } switch (-err) { case EALREADY: printk("EALREADY\n"); @@ -40,7 +43,14 @@ void raise_zephyr_error(int err) { case ENOTSUP: printk("ENOTSUP\n"); break; + case EADDRINUSE: + printk("EADDRINUSE\n"); + break; + case EINVAL: + printk("EINVAL\n"); + break; default: printk("Zephyr error %d\n", err); } + mp_raise_OSError(-err); } diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 5d3c7d7515da5..3a007661c77d9 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -43,6 +43,7 @@ ALWAYS_ON_MODULES = ["sys", "collections"] DEFAULT_MODULES = [ + "__future__", "time", "os", "microcontroller", @@ -82,7 +83,13 @@ # Other flags to set when a module is enabled EXTRA_FLAGS = {"busio": ["BUSIO_SPI", "BUSIO_I2C"]} -SHARED_MODULE_AND_COMMON_HAL = ["os"] +SHARED_MODULE_AND_COMMON_HAL = ["_bleio", "os"] + +# Mapping from module directory name to the flag name used in CIRCUITPY_ +MODULE_FLAG_NAMES = { + "__future__": "FUTURE", + "_bleio": "BLEIO", +} async def preprocess_and_split_defs(compiler, source_file, build_path, flags): @@ -338,6 +345,11 @@ async def build_circuitpython(): autogen_board_info_fn = mpconfigboard_fn.parent / "autogen_board_info.toml" + creator_id = mpconfigboard.get("CIRCUITPY_CREATOR_ID", mpconfigboard.get("USB_VID", 0x1209)) + creation_id = mpconfigboard.get("CIRCUITPY_CREATION_ID", mpconfigboard.get("USB_PID", 0x000C)) + circuitpython_flags.append(f"-DCIRCUITPY_CREATOR_ID=0x{creator_id:08x}") + circuitpython_flags.append(f"-DCIRCUITPY_CREATION_ID=0x{creation_id:08x}") + enabled_modules, module_reasons = determine_enabled_modules(board_info, portdir, srcdir) circuitpython_flags.extend(board_info["cflags"]) @@ -373,6 +385,7 @@ async def build_circuitpython(): supervisor_source = [pathlib.Path(p) for p in supervisor_source] supervisor_source.extend(board_info["source_files"]) supervisor_source.extend(top.glob("supervisor/shared/*.c")) + supervisor_source.append(top / "supervisor/shared/bluetooth/bluetooth.c") supervisor_source.append(top / "supervisor/shared/translate/translate.c") # if web_workflow: # supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c")) @@ -445,7 +458,8 @@ async def build_circuitpython(): if module.name in module_reasons: v.comment(module_reasons[module.name]) autogen_modules.add(module.name, v) - circuitpython_flags.append(f"-DCIRCUITPY_{module.name.upper()}={1 if enabled else 0}") + flag_name = MODULE_FLAG_NAMES.get(module.name, module.name.upper()) + circuitpython_flags.append(f"-DCIRCUITPY_{flag_name}={1 if enabled else 0}") if enabled: if module.name in EXTRA_FLAGS: diff --git a/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py b/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py index 95a07930e98c8..b147ae0605ed1 100644 --- a/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py +++ b/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py @@ -313,6 +313,45 @@ def test_memory_region_with_custom_name(self): assert label == "reserved_mem" assert start == "__CUSTOM_REGION_end" + def test_memory_region_requires_sram_or_device_type(self): + """Test memory regions require mmio-sram compatibility or device_type=memory.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + + reserved_mem: memory@30000000 { + compatible = "zephyr,memory-region"; + reg = <0x30000000 0x10000>; + zephyr,memory-region = "CUSTOM_REGION"; + }; + + external_mem: memory@40000000 { + compatible = "zephyr,memory-region"; + device_type = "memory"; + reg = <0x40000000 0x20000>; + zephyr,memory-region = "EXT_REGION"; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + + assert len(result) == 2 + assert result[0][0] == "sram0" + assert result[1][0] == "external_mem" + def test_disabled_ram_excluded(self): """Test that disabled RAM regions are excluded.""" dts = """ diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 0f09c21e83f70..d275a7ce960e2 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -143,6 +143,16 @@ "LCD_D14", "LCD_D15", ], + "nxp,lcd-pmod": [ + "LCD_WR", + "TOUCH_SCL", + "LCD_DC", + "TOUCH_SDA", + "LCD_MOSI", + "TOUCH_RESET", + "LCD_CS", + "TOUCH_INT", + ], "raspberrypi,csi-connector": [ "CSI_D0_N", "CSI_D0_P", @@ -354,6 +364,12 @@ def find_ram_regions(device_tree): if "zephyr,memory-region" not in compatible or "zephyr,memory-region" not in node.props: continue + is_mmio_sram = "mmio-sram" in compatible + device_type = node.props.get("device_type") + has_memory_device_type = device_type and device_type.to_string() == "memory" + if not (is_mmio_sram or has_memory_device_type): + continue + size = node.props["reg"].to_nums()[1] start = "__" + node.props["zephyr,memory-region"].to_string() + "_end" @@ -378,8 +394,26 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa board_info = { "wifi": False, "usb_device": False, + "_bleio": False, } + config_bt_enabled = False + config_bt_found = False + config_present = True + config = zephyrbuilddir / ".config" + if not config.exists(): + config_present = False + else: + for line in config.read_text().splitlines(): + if line.startswith("CONFIG_BT="): + config_bt_enabled = line.strip().endswith("=y") + config_bt_found = True + break + if line.startswith("# CONFIG_BT is not set"): + config_bt_enabled = False + config_bt_found = True + break + runners = zephyrbuilddir / "runners.yaml" runners = yaml.safe_load(runners.read_text()) zephyr_board_dir = pathlib.Path(runners["config"]["board_dir"]) @@ -433,6 +467,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa # Store active Zephyr device labels per-driver so that we can make them available via board. active_zephyr_devices = {} usb_num_endpoint_pairs = 0 + ble_hardware_present = False for k in device_tree.root.nodes["chosen"].props: value = device_tree.root.nodes["chosen"].props[k] path2chosen[value.to_path()] = k @@ -484,6 +519,8 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa usb_num_endpoint_pairs += min(single_direction_endpoints) elif driver.startswith("wifi"): board_info["wifi"] = True + elif driver == "bluetooth/hci": + ble_hardware_present = True elif driver in EXCEPTIONAL_DRIVERS: pass elif driver in BUSIO_CLASSES: @@ -580,7 +617,13 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa board_pin_names = board_names.get((ioport, num), []) for board_pin_name in board_pin_names: - board_pin_name = board_pin_name.upper().replace(" ", "_").replace("-", "_") + board_pin_name = ( + board_pin_name.upper() + .replace(" ", "_") + .replace("-", "_") + .replace("(", "") + .replace(")", "") + ) board_pin_mapping.append( f"{{ MP_ROM_QSTR(MP_QSTR_{board_pin_name}), MP_ROM_PTR(&pin_{pin_object_name}) }}," ) @@ -669,7 +712,8 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa device, start, end, size, path = ram max_size = max(max_size, size) # We always start at the end of a Zephyr linker section so we need the externs and &. - if board_id in ["native_sim"]: + # Native/simulated boards don't have real memory-mapped RAM, so we allocate static arrays. + if board_id in ["native_sim"] or "bsim" in board_id: ram_externs.append("// This is a native board so we provide all of RAM for our heaps.") ram_externs.append(f"static uint32_t _{device}[{size // 4}]; // {path}") start = f"(const uint32_t *) (_{device})" @@ -741,6 +785,17 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); """ board_c.write_text(new_board_c_content) + if ble_hardware_present: + if not config_present: + raise RuntimeError( + "Missing Zephyr .config; CONFIG_BT must be set explicitly when BLE hardware is present." + ) + if not config_bt_found: + raise RuntimeError( + "CONFIG_BT is missing from Zephyr .config; set it explicitly when BLE hardware is present." + ) + + board_info["_bleio"] = ble_hardware_present and config_bt_enabled board_info["source_files"] = [board_c] board_info["cflags"] = ("-I", board_dir) board_info["flash_count"] = len(flashes) diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 485559fe48073..121de2c4ae52d 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -14,11 +14,13 @@ CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_THREAD_STACK_INFO=y CONFIG_STACK_SENTINEL=y CONFIG_DEBUG_THREAD_INFO=y -# CONFIG_DEBUG_INFO=y +CONFIG_DEBUG_INFO=y +CONFIG_EXCEPTION_STACK_TRACE=y CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USBD_CDC_ACM_CLASS=y CONFIG_USBD_MAX_SPEED=1 +CONFIG_USBD_MSC_STACK_SIZE=1536 CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n CONFIG_USBD_MSC_CLASS=y @@ -39,3 +41,16 @@ CONFIG_UART_LINE_CTRL=y CONFIG_I2C=y CONFIG_SPI=y CONFIG_SPI_ASYNC=y + +CONFIG_LOG=y +CONFIG_LOG_MAX_LEVEL=2 +CONFIG_HW_STACK_PROTECTION=y +CONFIG_FRAME_POINTER=y + +CONFIG_BT_BUF_ACL_TX_COUNT=7 +CONFIG_BT_HCI_ERR_TO_STR=y + +CONFIG_NET_HOSTNAME_ENABLE=y +CONFIG_NET_HOSTNAME_DYNAMIC=y +CONFIG_NET_HOSTNAME="circuitpython" +CONFIG_NET_MGMT_EVENT_INFO=y diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 7be2beb1d2085..595bc6a3491e8 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -9,6 +9,10 @@ #include "mpconfigboard.h" #include "supervisor/shared/tick.h" +#if CIRCUITPY_BLEIO +#include "shared-bindings/_bleio/__init__.h" +#endif + #include #include #include @@ -24,6 +28,10 @@ extern const uint32_t *const ram_bounds[]; extern const size_t circuitpy_max_ram_size; static pool_t pools[CIRCUITPY_RAM_DEVICE_COUNT]; +static uint8_t valid_pool_count = 0; +static bool zephyr_malloc_active = false; +static void *zephyr_malloc_top = NULL; +static void *zephyr_malloc_bottom = NULL; static K_EVENT_DEFINE(main_needed); @@ -50,7 +58,9 @@ void reset_cpu(void) { } void reset_port(void) { - + #if CIRCUITPY_BLEIO + bleio_reset(); + #endif } void reset_to_bootloader(void) { @@ -135,19 +145,40 @@ void port_idle_until_interrupt(void) { // Zephyr doesn't maintain one multi-heap. So, make our own using TLSF. void port_heap_init(void) { + #ifdef CONFIG_COMMON_LIBC_MALLOC + // Do a test malloc to determine if Zephyr has an outer heap. + uint32_t *test_malloc = malloc(32); + free(test_malloc); // Free right away so we don't forget. We don't actually write it anyway. + zephyr_malloc_active = test_malloc != NULL; + #endif + for (size_t i = 0; i < CIRCUITPY_RAM_DEVICE_COUNT; i++) { uint32_t *heap_bottom = ram_bounds[2 * i]; uint32_t *heap_top = ram_bounds[2 * i + 1]; size_t size = (heap_top - heap_bottom) * sizeof(uint32_t); + if (size < 1024) { + printk("Skipping region because the linker filled it up.\n"); + continue; + } + #ifdef CONFIG_COMMON_LIBC_MALLOC + if (heap_bottom <= test_malloc && test_malloc < heap_top) { + zephyr_malloc_top = heap_top; + zephyr_malloc_bottom = heap_bottom; + printk("Skipping region because Zephyr malloc is within bounds\n"); + pools[i] = NULL; + continue; + } + #endif printk("Init heap at %p - %p with size %d\n", heap_bottom, heap_top, size); // If this crashes, then make sure you've enabled all of the Kconfig needed for the drivers. - if (i == 0) { + if (valid_pool_count == 0) { heap = tlsf_create_with_pool(heap_bottom, size, circuitpy_max_ram_size); pools[i] = tlsf_get_pool(heap); } else { pools[i] = tlsf_add_pool(heap, heap_bottom + 1, size - sizeof(uint32_t)); } + valid_pool_count++; } #if !DT_HAS_CHOSEN(zephyr_sram) #error "No SRAM!" @@ -155,16 +186,36 @@ void port_heap_init(void) { } void *port_malloc(size_t size, bool dma_capable) { - void *block = tlsf_malloc(heap, size); + void *block = NULL; + if (valid_pool_count > 0) { + block = tlsf_malloc(heap, size); + } + #ifdef CONFIG_COMMON_LIBC_MALLOC + if (block == NULL) { + block = malloc(size); + } + #endif return block; } void port_free(void *ptr) { - tlsf_free(heap, ptr); + if (valid_pool_count > 0 && !(ptr >= zephyr_malloc_bottom && ptr < zephyr_malloc_top)) { + tlsf_free(heap, ptr); + return; + } + #ifdef CONFIG_COMMON_LIBC_MALLOC + free(ptr); + #endif } void *port_realloc(void *ptr, size_t size, bool dma_capable) { - return tlsf_realloc(heap, ptr, size); + if (valid_pool_count > 0 && !(ptr >= zephyr_malloc_bottom && ptr < zephyr_malloc_top)) { + return tlsf_realloc(heap, ptr, size); + } + #ifdef CONFIG_COMMON_LIBC_MALLOC + return realloc(ptr, size); + #endif + return NULL; } static bool max_size_walker(void *ptr, size_t size, int used, void *user) { @@ -177,15 +228,21 @@ static bool max_size_walker(void *ptr, size_t size, int used, void *user) { size_t port_heap_get_largest_free_size(void) { size_t max_size = 0; - for (size_t i = 0; i < CIRCUITPY_RAM_DEVICE_COUNT; i++) { - tlsf_walk_pool(pools[i], max_size_walker, &max_size); + if (valid_pool_count > 0) { + for (size_t i = 0; i < CIRCUITPY_RAM_DEVICE_COUNT; i++) { + if (pools[i] == NULL) { + continue; + } + tlsf_walk_pool(pools[i], max_size_walker, &max_size); + } + // IDF does this. Not sure why. + return tlsf_fit_size(heap, max_size); } - // IDF does this. Not sure why. - return tlsf_fit_size(heap, max_size); + return 64 * 1024; } void assert_post_action(const char *file, unsigned int line) { - printk("Assertion failed at %s:%u\n", file, line); + // printk("Assertion failed at %s:%u\n", file, line); // Check that this is arm #if defined(__arm__) __asm__ ("bkpt"); diff --git a/ports/zephyr-cp/supervisor/usb.c b/ports/zephyr-cp/supervisor/usb.c index 844d4fae75934..a42a5192f4f30 100644 --- a/ports/zephyr-cp/supervisor/usb.c +++ b/ports/zephyr-cp/supervisor/usb.c @@ -22,7 +22,7 @@ #include "supervisor/shared/reload.h" #include -LOG_MODULE_REGISTER(usb, LOG_LEVEL_INF); +LOG_MODULE_REGISTER(cpusb, CONFIG_LOG_DEFAULT_LEVEL); #define USB_DEVICE DT_NODELABEL(zephyr_udc0) @@ -191,6 +191,20 @@ int _zephyr_disk_ioctl(struct disk_info *disk, uint8_t cmd, void *buff) { static void _msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) { LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); + + if (usbd_can_detect_vbus(ctx)) { + if (msg->type == USBD_MSG_VBUS_READY) { + if (usbd_enable(ctx)) { + LOG_ERR("Failed to enable device support"); + } + } + + if (msg->type == USBD_MSG_VBUS_REMOVED) { + if (usbd_disable(ctx)) { + LOG_ERR("Failed to disable device support"); + } + } + } } void usb_init(void) { @@ -341,12 +355,14 @@ void usb_init(void) { printk("USB initialized\n"); - err = usbd_enable(&main_usbd); - if (err) { - LOG_ERR("Failed to enable device support"); - return; + if (!usbd_can_detect_vbus(&main_usbd)) { + err = usbd_enable(&main_usbd); + if (err) { + LOG_ERR("Failed to enable device support"); + return; + } + printk("usbd enabled\n"); } - printk("usbd enabled\n"); } bool usb_connected(void) { diff --git a/ports/zephyr-cp/sysbuild.cmake b/ports/zephyr-cp/sysbuild.cmake index f0968e05b5c9b..3c3acf0a803c7 100644 --- a/ports/zephyr-cp/sysbuild.cmake +++ b/ports/zephyr-cp/sysbuild.cmake @@ -18,4 +18,6 @@ if(SB_CONFIG_NET_CORE_IMAGE_HCI_IPC) CACHE INTERNAL "" ) + native_simulator_set_child_images(${DEFAULT_IMAGE} ${NET_APP}) + native_simulator_set_final_executable(${DEFAULT_IMAGE}) endif() diff --git a/ports/zephyr-cp/tests/__init__.py b/ports/zephyr-cp/tests/__init__.py new file mode 100644 index 0000000000000..a560b2c40c1f2 --- /dev/null +++ b/ports/zephyr-cp/tests/__init__.py @@ -0,0 +1,136 @@ +import serial +import subprocess +import threading + + +class StdSerial: + def __init__(self, stdin, stdout): + self.stdin = stdin + self.stdout = stdout + + def read(self, amount=None): + data = self.stdout.read(amount) + if data == b"": + raise EOFError("stdout closed") + return data + + def write(self, buf): + if self.stdin is None: + return + self.stdin.write(buf) + self.stdin.flush() + + def close(self): + if self.stdin is not None: + self.stdin.close() + self.stdout.close() + + +class SerialSaver: + """Capture serial output in a background thread so output isn't missed.""" + + def __init__(self, serial_obj, name="serial"): + self.all_output = "" + self.all_input = "" + self.serial = serial_obj + self.name = name + + self._stop = threading.Event() + self._lock = threading.Lock() + self._cv = threading.Condition(self._lock) + self._reader = threading.Thread(target=self._reader_loop, daemon=True) + self._reader.start() + + def _reader_loop(self): + while not self._stop.is_set(): + try: + read = self.serial.read(1) + except Exception: + # Serial port closed or device disconnected. + break + + if read == b"": + # Timeout with no data — keep waiting. Only a real + # exception or an explicit stop should end the loop. + continue + + text = read.decode("utf-8", errors="replace") + with self._cv: + self.all_output += text + self._cv.notify_all() + + def wait_for(self, text, timeout=10): + with self._cv: + while text not in self.all_output and self._reader.is_alive(): + if not self._cv.wait(timeout=timeout): + break + if text not in self.all_output: + tail = self.all_output[-400:] + raise TimeoutError( + f"Timed out waiting for {text!r} on {self.name}. Output tail:\n{tail}" + ) + + def read(self, amount=None): + # Kept for compatibility with existing callers. + return + + def close(self): + if not self.serial: + return + + self._stop.set() + try: + self.serial.close() + except Exception: + pass + self._reader.join(timeout=1.0) + self.serial = None + + def write(self, text): + self.all_input += text + self.serial.write(text.encode("utf-8")) + + +class NativeSimProcess: + def __init__(self, cmd, timeout=5, trace_file=None, env=None): + if trace_file: + cmd.append(f"--trace-file={trace_file}") + + self._timeout = timeout + self.trace_file = trace_file + print("Running", " ".join(cmd)) + self._proc = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=None, + env=env, + ) + if self._proc.stdout is None: + raise RuntimeError("Failed to capture simulator stdout") + + # Discard the test warning + uart_pty_line = self._proc.stdout.readline().decode("utf-8") + if "connected to pseudotty:" not in uart_pty_line: + raise RuntimeError("Failed to connect to UART") + pty_path = uart_pty_line.strip().rsplit(":", maxsplit=1)[1].strip() + self.serial = SerialSaver( + serial.Serial(pty_path, baudrate=115200, timeout=0.05, write_timeout=0), + name="uart0", + ) + self.debug_serial = SerialSaver( + StdSerial(self._proc.stdin, self._proc.stdout), name="debug" + ) + + def shutdown(self): + if self._proc.poll() is None: + self._proc.terminate() + self._proc.wait(timeout=self._timeout) + + self.serial.close() + self.debug_serial.close() + + def wait_until_done(self): + self._proc.wait(timeout=self._timeout) + self.serial.close() + self.debug_serial.close() diff --git a/ports/zephyr-cp/tests/bsim/__init__.py b/ports/zephyr-cp/tests/bsim/__init__.py new file mode 100644 index 0000000000000..75136bccf43ae --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/__init__.py @@ -0,0 +1,3 @@ +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") diff --git a/ports/zephyr-cp/tests/bsim/conftest.py b/ports/zephyr-cp/tests/bsim/conftest.py new file mode 100644 index 0000000000000..505ff77bf7ec0 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/conftest.py @@ -0,0 +1,222 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Pytest fixtures for CircuitPython bsim testing.""" + +import logging +import os +import shutil +import subprocess +from pathlib import Path + +import pytest + +from .. import SerialSaver, StdSerial + +logger = logging.getLogger(__name__) + +ZEPHYR_CP = Path(__file__).resolve().parents[2] +BSIM_BUILD_DIR = ZEPHYR_CP / "build-native_nrf5340bsim" +BSIM_SYSBUILD_BINARY = BSIM_BUILD_DIR / "zephyr/zephyr.exe" +BSIM_BINARY = BSIM_BUILD_DIR / "zephyr-cp/zephyr/zephyr.exe" +BSIM_ROOT = ZEPHYR_CP / "tools/bsim" +BSIM_PHY_BINARY = BSIM_ROOT / "bin/bs_2G4_phy_v1" + + +@pytest.fixture +def native_sim_env() -> dict[str, str]: + env = os.environ.copy() + env["BSIM_OUT_PATH"] = str(BSIM_ROOT) + env["BSIM_COMPONENTS_PATH"] = str(BSIM_ROOT / "components") + lib_path = str(BSIM_ROOT / "lib") + existing = env.get("LD_LIBRARY_PATH", "") + env["LD_LIBRARY_PATH"] = f"{lib_path}:{existing}" if existing else lib_path + return env + + +@pytest.fixture +def bsim_binary(): + """Return path to nrf5340bsim binary, skip if not built.""" + if BSIM_SYSBUILD_BINARY.exists(): + return BSIM_SYSBUILD_BINARY + if not BSIM_BINARY.exists(): + pytest.skip(f"nrf5340bsim not built: {BSIM_BINARY}") + return BSIM_BINARY + + +@pytest.fixture +def bsim_phy_binary(): + """Return path to BabbleSim PHY binary, skip if not present.""" + if not BSIM_PHY_BINARY.exists(): + pytest.skip(f"bs_2G4_phy_v1 not found: {BSIM_PHY_BINARY}") + return BSIM_PHY_BINARY + + +class BsimPhyInstance: + def __init__(self, proc: subprocess.Popen, serial: SerialSaver, timeout: float): + self.proc = proc + self.serial = serial + self.timeout = timeout + + def finish_sim(self) -> None: + self.serial.wait_for("Cleaning up", timeout=self.timeout + 5) + + def shutdown(self) -> None: + if self.proc.poll() is None: + self.proc.terminate() + self.proc.wait(timeout=2) + self.serial.close() + + +class ZephyrSampleProcess: + def __init__(self, proc: subprocess.Popen, timeout: float): + self._proc = proc + self._timeout = timeout + if proc.stdout is None: + raise RuntimeError("Failed to capture Zephyr sample stdout") + self.serial = SerialSaver(StdSerial(None, proc.stdout), name="zephyr sample") + + def shutdown(self) -> None: + if self._proc.poll() is None: + self._proc.terminate() + self._proc.wait(timeout=self._timeout) + self.serial.close() + + +@pytest.fixture +def bsim_phy(request, bsim_phy_binary, native_sim_env, sim_id): + duration_marker = request.node.get_closest_marker("duration") + sim_length = float(duration_marker.args[0]) if duration_marker else 20.0 + + devices = 1 + if "circuitpython2" in request.fixturenames or "zephyr_sample" in request.fixturenames: + devices = 2 + + sample_marker = request.node.get_closest_marker("zephyr_sample") + if sample_marker is not None: + sample_device_id = int(sample_marker.kwargs.get("device_id", 1)) + devices = max(devices, sample_device_id + 1) + + bsim_marker = request.node.get_closest_marker("bsim") + if bsim_marker is not None: + devices = int(bsim_marker.kwargs.get("devices", devices)) + sim_length = float(bsim_marker.kwargs.get("sim_length", sim_length)) + + sim_length_us = int(sim_length * 1e6) + cmd = [ + "stdbuf", + "-oL", + str(bsim_phy_binary), + "-v=9", # Cleaning up level is on 9. Connecting is 7. + f"-s={sim_id}", + f"-D={devices}", + f"-sim_length={sim_length_us}", + ] + print("Running:", " ".join(cmd)) + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=native_sim_env, + cwd=BSIM_ROOT / "bin", + ) + if proc.stdout is None: + raise RuntimeError("Failed to capture bsim phy stdout") + + # stdbuf -oL forces line-buffered stdout so SerialSaver can + # stream-read PHY output in real time. Wrapping in StdSerial + # ensures the reader thread exits on EOF when the PHY process + # terminates, rather than spinning on empty timeout reads. + phy_output = SerialSaver(StdSerial(None, proc.stdout), name="bsim phy") + try: + phy_output.wait_for("Connecting", timeout=2) + except TimeoutError: + if proc.poll() is not None: + print(phy_output.all_output) + raise RuntimeError("bsim PHY exited immediately") + # Assume bsim is running + + phy = BsimPhyInstance(proc, phy_output, timeout=sim_length) + yield phy + phy.shutdown() + + print("bsim phy output:") + print(phy_output.all_output) + + +def _build_zephyr_sample(build_dir: Path, source_dir: Path, board: str) -> Path: + if shutil.which("west") is None: + raise RuntimeError("west not found") + + cmd = [ + "west", + "build", + "-b", + board, + "-d", + str(build_dir), + "-p=auto", + str(source_dir), + ] + logger.info("Building Zephyr sample: %s", " ".join(cmd)) + subprocess.run(cmd, check=True, cwd=ZEPHYR_CP) + + return build_dir / "zephyr/zephyr.exe" + + +@pytest.fixture +def zephyr_sample(request, bsim_phy, native_sim_env, sim_id): + marker = request.node.get_closest_marker("zephyr_sample") + if marker is None or len(marker.args) != 1: + raise RuntimeError( + "zephyr_sample fixture requires @pytest.mark.zephyr_sample('')" + ) + + sample = marker.args[0] + board = marker.kwargs.get("board", "nrf52_bsim") + device_id = int(marker.kwargs.get("device_id", 1)) + timeout = float(marker.kwargs.get("timeout", 10.0)) + + sample_rel = str(sample).removeprefix("zephyr/samples/") + source_dir = ZEPHYR_CP / "zephyr/samples" / sample_rel + if not source_dir.exists(): + pytest.skip(f"Zephyr sample not found: {source_dir}") + + build_name = f"build-bsim-sample-{sample_rel.replace('/', '_')}-{board}" + build_dir = ZEPHYR_CP / build_name + binary = build_dir / "zephyr/zephyr.exe" + + if not binary.exists(): + try: + binary = _build_zephyr_sample(build_dir, source_dir, board) + except (subprocess.CalledProcessError, RuntimeError) as exc: + pytest.skip(f"Failed to build Zephyr sample {sample_rel}: {exc}") + + if not binary.exists(): + pytest.skip(f"Zephyr sample binary not found: {binary}") + + cmd = [str(binary), f"-s={sim_id}", f"-d={device_id}"] + logger.info("Running: %s", " ".join(cmd)) + proc = subprocess.Popen( + cmd, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=native_sim_env, + ) + sample_proc = ZephyrSampleProcess(proc, timeout=timeout) + yield sample_proc + sample_proc.shutdown() + + print("Zephyr sample output:") + print(sample_proc.serial.all_output) + + +@pytest.fixture +def circuitpython1(circuitpython): + return circuitpython[0] + + +@pytest.fixture +def circuitpython2(circuitpython): + return circuitpython[1] diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_basics.py b/ports/zephyr-cp/tests/bsim/test_bsim_basics.py new file mode 100644 index 0000000000000..7f1ad036f6f5f --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_basics.py @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Basic BabbleSim connectivity tests for nrf5340bsim.""" + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_CODE = """\ +print("bsim ready") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_CODE}) +def test_bsim_dual_instance_connect(bsim_phy, circuitpython1, circuitpython2): + """Run two bsim instances on the same sim id and verify UART output.""" + print("in the test") + + # Wait for both devices to produce their expected output before + # tearing down the simulation. + circuitpython1.serial.wait_for("bsim ready") + circuitpython2.serial.wait_for("bsim ready") + + bsim_phy.finish_sim() + + output0 = circuitpython1.serial.all_output + output1 = circuitpython2.serial.all_output + + assert "Board ID:native_nrf5340bsim" in output0 + assert "Board ID:native_nrf5340bsim" in output1 + assert "bsim ready" in output0 + assert "bsim ready" in output1 diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py new file mode 100644 index 0000000000000..4822d42b9bbdd --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py @@ -0,0 +1,111 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE advertising tests for nrf5340bsim.""" + +import logging +import time + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +logger = logging.getLogger(__name__) + +BSIM_ADV_CODE = """\ +import _bleio +import time + +name = b"CPADV" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +adapter = _bleio.adapter +print("adv start") +adapter.start_advertising(advertisement, connectable=False) +print("adv started") +time.sleep(4) +adapter.stop_advertising() +print("adv stop") +""" + +BSIM_ADV_INTERRUPT_RELOAD_CODE = """\ +import _bleio +import time + +name = b"CPADV" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +adapter = _bleio.adapter +print("adv run start") +adapter.start_advertising(advertisement, connectable=False) +print("adv running") +while True: + time.sleep(0.2) +""" + + +@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.circuitpy_drive({"code.py": BSIM_ADV_CODE}) +def test_bsim_advertise_and_scan(bsim_phy, circuitpython, zephyr_sample): + """Advertise from CircuitPython and verify Zephyr observer sees traffic.""" + observer = zephyr_sample + + start_time = time.time() + while time.time() - start_time < 12.0: + observer_output = observer.serial.all_output + adv_ready = "adv started" in circuitpython.serial.all_output + if ( + "Device found:" in observer_output + and "AD data len 10" in observer_output + and adv_ready + ): + break + time.sleep(0.05) + + observer_output = observer.serial.all_output + assert "adv start" in circuitpython.serial.all_output + assert "adv started" in circuitpython.serial.all_output + assert "Device found:" in observer_output + assert "AD data len 10" in observer_output + + +@pytest.mark.zephyr_sample("bluetooth/observer", timeout=20.0) +@pytest.mark.circuitpy_drive({"code.py": BSIM_ADV_INTERRUPT_RELOAD_CODE}) +def test_bsim_advertise_ctrl_c_reload(bsim_phy, circuitpython, zephyr_sample): + """Ensure advertising resumes after Ctrl-C and a reload.""" + observer = zephyr_sample + + start_time = time.time() + sent_ctrl_c = False + sent_reload = False + observer_count_before = 0 + + while time.time() - start_time < 22.0: + cp_output = circuitpython.serial.all_output + observer_output = observer.serial.all_output + device_found_count = observer_output.count("Device found:") + + if not sent_ctrl_c and "adv running" in cp_output and device_found_count > 0: + circuitpython.serial.write("\x03") + sent_ctrl_c = True + observer_count_before = device_found_count + + if sent_ctrl_c and not sent_reload and "KeyboardInterrupt" in cp_output: + circuitpython.serial.write("\x04") + sent_reload = True + + if sent_reload and cp_output.count("adv running") >= 2: + break + + time.sleep(0.05) + + cp_output = circuitpython.serial.all_output + observer_output = observer.serial.all_output + logger.info(observer_output) + logger.info(cp_output) + + assert "adv run start" in cp_output + assert "KeyboardInterrupt" in cp_output + assert cp_output.count("adv running") >= 2 + assert observer_output.count("Device found:") >= observer_count_before + assert "Already advertising" not in cp_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py new file mode 100644 index 0000000000000..a7f76ca72b7b4 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE name tests for nrf5340bsim.""" + +import time + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_NAME_CODE = """\ +import _bleio + +adapter = _bleio.adapter +adapter.enabled = True +adapter.name = "CPNAME" +print("name", adapter.name) +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_NAME_CODE}) +def test_bsim_set_name(bsim_phy, circuitpython): + """Set the BLE name and read it back on bsim.""" + start_time = time.time() + while time.time() - start_time < 3.0: + if "name CPNAME" in circuitpython.serial.all_output: + break + time.sleep(0.05) + + assert "name CPNAME" in circuitpython.serial.all_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py new file mode 100644 index 0000000000000..8836669801676 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py @@ -0,0 +1,151 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE scanning tests for nrf5340bsim.""" + +import time + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_SCAN_CODE = """\ +import _bleio + +adapter = _bleio.adapter +print("scan start") +scan = adapter.start_scan(timeout=4.0, active=True) +found = False +for entry in scan: + if b"zephyrproject" in entry.advertisement_bytes: + print("found beacon") + found = True + break +adapter.stop_scan() +print("scan done", found) +""" + +BSIM_SCAN_RELOAD_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("scan run start") +for _ in range(10): + try: + scan = adapter.start_scan(timeout=4.0, active=True) + break + except OSError: + time.sleep(0.1) +else: + raise RuntimeError("scan start failed") +found = False +for entry in scan: + if b"zephyrproject" in entry.advertisement_bytes: + print("found beacon run") + found = True + break +adapter.stop_scan() +print("scan run done", found) +""" + +BSIM_SCAN_RELOAD_NO_STOP_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("scan run start") +for _ in range(10): + try: + scan = adapter.start_scan(timeout=4.0, active=True) + break + except OSError: + time.sleep(0.1) +else: + raise RuntimeError("scan start failed") +found = False +for entry in scan: + if b"zephyrproject" in entry.advertisement_bytes: + print("found beacon run") + found = True + break +print("scan run done", found) +""" + + +@pytest.mark.zephyr_sample("bluetooth/beacon") +@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_CODE}) +def test_bsim_scan_zephyr_beacon(bsim_phy, circuitpython, zephyr_sample): + """Scan for Zephyr beacon sample advertisement using bsim.""" + _ = zephyr_sample + + start_time = time.time() + while time.time() - start_time < 6.0: + if "found beacon" in circuitpython.serial.all_output: + break + time.sleep(0.05) + + assert "scan start" in circuitpython.serial.all_output + assert "found beacon" in circuitpython.serial.all_output + + +@pytest.mark.zephyr_sample("bluetooth/beacon", timeout=12.0) +@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_CODE}) +def test_bsim_scan_zephyr_beacon_reload(bsim_phy, circuitpython, zephyr_sample): + """Scan for Zephyr beacon, soft reload, and scan again.""" + _ = zephyr_sample + + start_time = time.time() + sent_reload = False + while time.time() - start_time < 12.0: + output = circuitpython.serial.all_output + + if ( + not sent_reload + and "scan run done" in output + and "Press any key to enter the REPL" in output + ): + time.sleep(0.2) + circuitpython.serial.write("\x04") + sent_reload = True + + if sent_reload and output.count("scan run done") >= 2: + break + time.sleep(0.05) + + output = circuitpython.serial.all_output + assert "scan run start" in output + assert output.count("found beacon run") >= 2 + assert output.count("scan run done") >= 2 + + +@pytest.mark.zephyr_sample("bluetooth/beacon", timeout=12.0) +@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_NO_STOP_CODE}) +def test_bsim_scan_zephyr_beacon_reload_no_stop(bsim_phy, circuitpython, zephyr_sample): + """Scan for Zephyr beacon without explicit stop, soft reload, and scan again.""" + _ = zephyr_sample + + start_time = time.time() + sent_reload = False + while time.time() - start_time < 12.0: + output = circuitpython.serial.all_output + + if ( + not sent_reload + and "scan run done" in output + and "Press any key to enter the REPL" in output + ): + time.sleep(0.2) + circuitpython.serial.write("\x04") + sent_reload = True + + if sent_reload and output.count("scan run done") >= 2: + break + time.sleep(0.05) + + output = circuitpython.serial.all_output + assert "scan run start" in output + assert output.count("found beacon run") >= 2 + assert output.count("scan run done") >= 2 diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py index f26c7f0f45f55..f6545a46b5563 100644 --- a/ports/zephyr-cp/tests/conftest.py +++ b/ports/zephyr-cp/tests/conftest.py @@ -4,7 +4,6 @@ """Pytest fixtures for CircuitPython native_sim testing.""" import logging -import os import re import select import subprocess @@ -13,59 +12,37 @@ from pathlib import Path import pytest +import serial +from . import NativeSimProcess + from perfetto.trace_processor import TraceProcessor logger = logging.getLogger(__name__) -ZEPHYR_CP = Path(__file__).parent.parent -BUILD_DIR = ZEPHYR_CP / "build-native_native_sim" -BINARY = BUILD_DIR / "zephyr-cp/zephyr/zephyr.exe" - - -@dataclass -class InputTrigger: - """A trigger for sending input to the simulator. - - Attributes: - trigger: Text to match in output to trigger input, or None for immediate send. - data: Bytes to send when triggered. - sent: Whether this trigger has been sent (set internally). - """ - trigger: str | None - data: bytes - sent: bool = False - - -@dataclass -class SimulatorResult: - """Result from running CircuitPython on the simulator.""" - - output: str - trace_file: Path - - -def parse_gpio_trace(trace_file: Path, pin_name: str = "gpio_emul.00") -> list[tuple[int, int]]: - """Parse GPIO trace from Perfetto trace file. +def pytest_configure(config): + config.addinivalue_line( + "markers", "circuitpy_drive(files): run CircuitPython with files in the flash image" + ) + config.addinivalue_line( + "markers", "disable_i2c_devices(*names): disable native_sim I2C emulator devices" + ) + config.addinivalue_line( + "markers", "circuitpython_board(board_id): which board id to use in the test" + ) + config.addinivalue_line( + "markers", + "zephyr_sample(sample, board='nrf52_bsim', device_id=1): build and run a Zephyr sample for bsim tests", + ) + config.addinivalue_line( + "markers", + "bsim(devices=1, sim_length=20.0): configure bsim PHY device count and simulation length", + ) - Args: - trace_file: Path to the Perfetto trace file. - pin_name: Name of the GPIO pin track (e.g., "gpio_emul.00"). - Returns: - List of (timestamp_ns, value) tuples for the specified GPIO pin. - """ - tp = TraceProcessor(file_path=str(trace_file)) - result = tp.query( - f''' - SELECT c.ts, c.value - FROM counter c - JOIN track t ON c.track_id = t.id - WHERE t.name = "{pin_name}" - ORDER BY c.ts - ''' - ) - return [(row.ts, int(row.value)) for row in result] +ZEPHYR_CP = Path(__file__).parent.parent +BUILD_DIR = ZEPHYR_CP / "build-native_native_sim" +BINARY = BUILD_DIR / "zephyr-cp/zephyr/zephyr.exe" def _iter_uart_tx_slices(trace_file: Path) -> list[tuple[int, int, str, str]]: @@ -134,179 +111,99 @@ def log_uart_trace_output(trace_file: Path) -> None: @pytest.fixture -def native_sim_binary(): - """Return path to native_sim binary, skip if not built.""" - if not BINARY.exists(): - pytest.skip(f"native_sim not built: {BINARY}") - return BINARY +def board(request): + board = request.node.get_closest_marker("circuitpython_board") + print("board", board) + if board is not None: + board = board.args[0] + else: + board = "native_native_sim" + return board @pytest.fixture -def create_flash_image(tmp_path): - """Factory fixture to create FAT flash images.""" - - def _create(files: dict[str, str]) -> Path: - flash = tmp_path / "flash.bin" +def native_sim_binary(request, board): + """Return path to native_sim binary, skip if not built.""" + ZEPHYR_CP = Path(__file__).parent.parent + build_dir = ZEPHYR_CP / f"build-{board}" + binary = build_dir / "zephyr-cp/zephyr/zephyr.exe" - # Create 2MB empty file - flash.write_bytes(b"\x00" * (2 * 1024 * 1024)) + if not binary.exists(): + pytest.skip(f"binary not built: {binary}") + return binary - # Format as FAT (mformat) - subprocess.run(["mformat", "-i", str(flash), "::"], check=True) - # Copy files (mcopy) - for name, content in files.items(): - src = tmp_path / name - src.write_text(content) - subprocess.run(["mcopy", "-i", str(flash), str(src), f"::{name}"], check=True) +@pytest.fixture +def native_sim_env() -> dict[str, str]: + return {} - return flash - return _create +@pytest.fixture +def sim_id(request) -> str: + return request.node.nodeid.replace("/", "_") @pytest.fixture -def run_circuitpython(native_sim_binary, create_flash_image, tmp_path): - """Run CircuitPython with given code string and return output from PTY. - - Args: - code: Python code to write to code.py, or None for no code.py. - timeout: Timeout in seconds for the simulation. - erase_flash: If True, erase flash before running. - input_sequence: List of InputTrigger objects. When trigger text is seen - in output, the corresponding data is written to the PTY. If trigger - is None, the data is sent immediately when PTY is opened. - """ - - def _run( - code: str | None, - timeout: float = 5.0, - erase_flash: bool = False, - input_sequence: list[InputTrigger] | None = None, - disabled_i2c_devices: list[str] | None = None, - ) -> SimulatorResult: - files = {"code.py": code} if code is not None else {} - flash = create_flash_image(files) - triggers = list(input_sequence) if input_sequence else [] - trace_file = tmp_path / "trace.perfetto" - - cmd = [ - str(native_sim_binary), - f"--flash={flash}", - "--flash_rm", - "-no-rt", - "-wait_uart", - f"-stop_at={timeout}", - f"--trace-file={trace_file}", - ] - if erase_flash: - cmd.append("--flash_erase") - if disabled_i2c_devices: - for device in disabled_i2c_devices: +def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp_path): + """Run CircuitPython with given code string and return PTY output.""" + + instance_count = 1 + if "circuitpython1" in request.fixturenames and "circuitpython2" in request.fixturenames: + instance_count = 2 + + drives = list(request.node.iter_markers_with_node("circuitpy_drive")) + if len(drives) != instance_count: + raise RuntimeError(f"not enough drives for {instance_count} instances") + + procs = [] + for i in range(instance_count): + flash = tmp_path / f"flash-{i}.bin" + flash.write_bytes(b"\xff" * (2 * 1024 * 1024)) + files = None + if len(drives[i][1].args) == 1: + files = drives[i][1].args[0] + if files is not None: + subprocess.run(["mformat", "-i", str(flash), "::"], check=True) + tmp_drive = tmp_path / f"drive{i}" + tmp_drive.mkdir(exist_ok=True) + + for name, content in files.items(): + src = tmp_drive / name + src.write_text(content) + subprocess.run(["mcopy", "-i", str(flash), str(src), f"::{name}"], check=True) + + trace_file = tmp_path / f"trace-{i}.perfetto" + + marker = request.node.get_closest_marker("duration") + if marker is None: + timeout = 10 + else: + timeout = marker.args[0] + if "bsim" in board: + cmd = [str(native_sim_binary), f"--flash_app={flash}"] + cmd.extend((f"-s={sim_id}", f"-d={i}", "-uart0_pty", "-uart_pty_wait")) + else: + cmd = [str(native_sim_binary), f"--flash={flash}"] + cmd.extend(("-no-rt", "-wait_uart", f"-stop_at={timeout}")) + + marker = request.node.get_closest_marker("disable_i2c_devices") + if marker and len(marker.args) > 0: + for device in marker.args: cmd.append(f"--disable-i2c={device}") logger.info("Running: %s", " ".join(cmd)) - # Start the process - proc = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - ) - - pty_path = None - pty_fd = None - output = [] - stdout_lines = [] - - try: - # Read stdout to find the PTY path - start_time = time.time() - while time.time() - start_time < timeout + 5: - if proc.poll() is not None: - # Process exited - break - - # Check if stdout has data - ready, _, _ = select.select([proc.stdout], [], [], 0.1) - if ready: - line = proc.stdout.readline() - if not line: - break - - stdout_lines.append(line.rstrip()) - - # Look for PTY path - match = re.search(r"uart connected to pseudotty: (/dev/pts/\d+)", line) - if match: - pty_path = match.group(1) - # Open the PTY for reading and writing - pty_fd = os.open(pty_path, os.O_RDWR | os.O_NONBLOCK) - - # Send any immediate triggers (trigger=None) - for t in triggers: - if t.trigger is None and not t.sent: - os.write(pty_fd, t.data) - logger.info("PTY input (immediate): %r", t.data) - t.sent = True - break - - if pty_fd is None: - raise RuntimeError("Failed to find PTY path in output") - - def check_triggers(accumulated_output: str) -> None: - """Check accumulated output against triggers and send input.""" - for t in triggers: - if t.trigger is not None and not t.sent: - if t.trigger in accumulated_output: - os.write(pty_fd, t.data) - logger.info("PTY input (trigger %r): %r", t.trigger, t.data) - t.sent = True - - # Read from PTY until process exits or timeout - while time.time() - start_time < timeout + 1: - if proc.poll() is not None: - # Process exited, do one final read - try: - ready, _, _ = select.select([pty_fd], [], [], 0.1) - if ready: - data = os.read(pty_fd, 4096) - if data: - output.append(data.decode("utf-8", errors="replace")) - except (OSError, BlockingIOError): - pass - break - - # Check if PTY has data - try: - ready, _, _ = select.select([pty_fd], [], [], 0.1) - if ready: - data = os.read(pty_fd, 4096) - if data: - output.append(data.decode("utf-8", errors="replace")) - check_triggers("".join(output)) - except (OSError, BlockingIOError): - pass - - # Read any remaining stdout - remaining_stdout = proc.stdout.read() - if remaining_stdout: - stdout_lines.extend(remaining_stdout.rstrip().split("\n")) - - # Log stdout - for line in stdout_lines: - logger.info("stdout: %s", line) - - pty_output = "".join(output) - for line in pty_output.split("\n"): - logger.info("PTY output: %s", repr(line.strip())) - log_uart_trace_output(trace_file) - return SimulatorResult(output=pty_output, trace_file=trace_file) - - finally: - if pty_fd is not None: - os.close(pty_fd) - proc.terminate() - proc.wait(timeout=1) - - return _run + procs.append(NativeSimProcess(cmd, timeout, trace_file, native_sim_env)) + if instance_count == 1: + yield procs[0] + else: + yield procs + for i, proc in enumerate(procs): + if instance_count > 1: + print(f"---------- Instance {i} -----------") + proc.shutdown() + + print("All serial output:") + print(proc.serial.all_output) + print() + print("All debug serial output:") + print(proc.debug_serial.all_output) diff --git a/ports/zephyr-cp/tests/docs/babblesim.md b/ports/zephyr-cp/tests/docs/babblesim.md new file mode 100644 index 0000000000000..39d60fa306cbd --- /dev/null +++ b/ports/zephyr-cp/tests/docs/babblesim.md @@ -0,0 +1,69 @@ +# BabbleSim testing + +This document describes how to build and run CircuitPython tests against the +BabbleSim (bsim) nRF5340 board. + +## Board target + +We use the Zephyr BabbleSim board for the nRF5340 application core: + +- Zephyr board: `nrf5340bsim/nrf5340/cpuapp` +- CircuitPython board alias: `native_nrf5340bsim` + +The tests expect two bsim instances to run in the same simulation, which allows +future BLE/802.15.4 multi-node tests. + +## Prerequisites + +BabbleSim needs to be available to Zephyr. Either: + +- Use the repo-provided `tools/bsim` checkout (if present) +- Or set environment variables: + +``` +export BSIM_COMPONENTS_PATH=/path/to/bsim/components +export BSIM_OUT_PATH=/path/to/bsim +``` + +## Build + +``` +CCACHE_TEMPDIR=/tmp/ccache-tmp make -j32 BOARD=native_nrf5340bsim +``` + +If you do not use ccache, you can omit `CCACHE_TEMPDIR`. + +## Run the bsim test + +``` +pytest tests/test_bsim_basics.py -v +``` + +## BLE scan + advertising tests + +The BLE tests run multiple bsim instances and build Zephyr samples on-demand: + +- `tests/test_bsim_ble_scan.py` scans for the Zephyr beacon sample +- `tests/test_bsim_ble_advertising.py` advertises from CircuitPython while the + Zephyr observer sample scans + +The fixtures build the Zephyr samples if missing: + +- Beacon: `zephyr/samples/bluetooth/beacon` (board `nrf52_bsim`) +- Observer: `zephyr/samples/bluetooth/observer` (board `nrf52_bsim`) + +Run the tests with: + +``` +pytest tests/test_bsim_ble_scan.py -v +pytest tests/test_bsim_ble_advertising.py -v +``` + +## Notes + +- The bsim test spawns two instances that share a sim id. It only checks UART + output for now, but is the base for BLE/Thread multi-node tests. +- The BLE tests rely on the sysbuild HCI IPC net-core image for the nRF5340 + simulator (enabled via `sysbuild.conf`). +- The board uses a custom devicetree overlay to provide the SRAM region and + CircuitPython flash partition expected by the port. diff --git a/ports/zephyr-cp/tests/test_basics.py b/ports/zephyr-cp/tests/test_basics.py index 9abb7f451b722..96f41d39795f0 100644 --- a/ports/zephyr-cp/tests/test_basics.py +++ b/ports/zephyr-cp/tests/test_basics.py @@ -3,18 +3,23 @@ """Test LED blink functionality on native_sim.""" -from conftest import InputTrigger, parse_gpio_trace +import pytest +from pathlib import Path +from perfetto.trace_processor import TraceProcessor -def test_blank_flash_hello_world(run_circuitpython): + +@pytest.mark.circuitpy_drive(None) +def test_blank_flash_hello_world(circuitpython): """Test that an erased flash shows code.py output header.""" - result = run_circuitpython(None, timeout=4, erase_flash=True) + circuitpython.wait_until_done() - assert "Board ID:native_native_sim" in result.output - assert "UID:" in result.output - assert "code.py output:" in result.output - assert "Hello World" in result.output - assert "done" in result.output + output = circuitpython.serial.all_output + assert "Board ID:native_native_sim" in output + assert "UID:" in output + assert "code.py output:" in output + assert "Hello World" in output + assert "done" in output BLINK_CODE = """\ @@ -37,19 +42,36 @@ def test_blank_flash_hello_world(run_circuitpython): """ -def test_blink_output(run_circuitpython): +def parse_gpio_trace(trace_file: Path, pin_name: str = "gpio_emul.00") -> list[tuple[int, int]]: + """Parse GPIO trace from Perfetto trace file.""" + tp = TraceProcessor(file_path=str(trace_file)) + result = tp.query( + f''' + SELECT c.ts, c.value + FROM counter c + JOIN track t ON c.track_id = t.id + WHERE t.name = "{pin_name}" + ORDER BY c.ts + ''' + ) + return [(row.ts, int(row.value)) for row in result] + + +@pytest.mark.circuitpy_drive({"code.py": BLINK_CODE}) +def test_blink_output(circuitpython): """Test blink program produces expected output and GPIO traces.""" - result = run_circuitpython(BLINK_CODE, timeout=5) + circuitpython.wait_until_done() # Check serial output - assert "LED on 0" in result.output - assert "LED off 0" in result.output - assert "LED on 2" in result.output - assert "LED off 2" in result.output - assert "done" in result.output + output = circuitpython.serial.all_output + assert "LED on 0" in output + assert "LED off 0" in output + assert "LED on 2" in output + assert "LED off 2" in output + assert "done" in output # Check GPIO traces - LED is on gpio_emul.00 - gpio_trace = parse_gpio_trace(result.trace_file, "gpio_emul.00") + gpio_trace = parse_gpio_trace(circuitpython.trace_file, "gpio_emul.00") # Deduplicate by timestamp (keep last value at each timestamp) by_timestamp = {} @@ -115,17 +137,17 @@ def test_blink_output(run_circuitpython): """ -def test_basic_serial_input(run_circuitpython): +@pytest.mark.circuitpy_drive({"code.py": INPUT_CODE}) +def test_basic_serial_input(circuitpython): """Test reading single character from serial via PTY write.""" - result = run_circuitpython( - INPUT_CODE, - timeout=5.0, - input_sequence=[InputTrigger(trigger="ready", data=b"A")], - ) + circuitpython.serial.wait_for("ready") + circuitpython.serial.write("A") + circuitpython.wait_until_done() - assert "ready" in result.output - assert "received: 'A'" in result.output - assert "done" in result.output + output = circuitpython.serial.all_output + assert "ready" in output + assert "received: 'A'" in output + assert "done" in output INPUT_FUNC_CODE = """\ @@ -136,18 +158,18 @@ def test_basic_serial_input(run_circuitpython): """ -def test_input_function(run_circuitpython): +@pytest.mark.circuitpy_drive({"code.py": INPUT_FUNC_CODE}) +def test_input_function(circuitpython): """Test the built-in input() function with PTY input.""" - result = run_circuitpython( - INPUT_FUNC_CODE, - timeout=5.0, - input_sequence=[InputTrigger(trigger="Enter name:", data=b"World\r")], - ) + circuitpython.serial.wait_for("Enter name:") + circuitpython.serial.write("World\r") + circuitpython.wait_until_done() - assert "ready" in result.output - assert "Enter name:" in result.output - assert "hello World" in result.output - assert "done" in result.output + output = circuitpython.serial.all_output + assert "ready" in output + assert "Enter name:" in output + assert "hello World" in output + assert "done" in output INTERRUPT_CODE = """\ @@ -161,18 +183,18 @@ def test_input_function(run_circuitpython): """ -def test_ctrl_c_interrupt(run_circuitpython): +@pytest.mark.circuitpy_drive({"code.py": INTERRUPT_CODE}) +def test_ctrl_c_interrupt(circuitpython): """Test sending Ctrl+C (0x03) to interrupt running code.""" - result = run_circuitpython( - INTERRUPT_CODE, - timeout=15.0, - input_sequence=[InputTrigger(trigger="loop 5", data=b"\x03")], - ) + circuitpython.serial.wait_for("loop 5") + circuitpython.serial.write("\x03") + circuitpython.wait_until_done() - assert "starting" in result.output - assert "loop 5" in result.output - assert "KeyboardInterrupt" in result.output - assert "completed" not in result.output + output = circuitpython.serial.all_output + assert "starting" in output + assert "loop 5" in output + assert "KeyboardInterrupt" in output + assert "completed" not in output RELOAD_CODE = """\ @@ -183,17 +205,17 @@ def test_ctrl_c_interrupt(run_circuitpython): """ -def test_ctrl_d_soft_reload(run_circuitpython): +@pytest.mark.circuitpy_drive({"code.py": RELOAD_CODE}) +def test_ctrl_d_soft_reload(circuitpython): """Test sending Ctrl+D (0x04) to trigger soft reload.""" - result = run_circuitpython( - RELOAD_CODE, - timeout=10.0, - input_sequence=[InputTrigger(trigger="first run", data=b"\x04")], - ) + circuitpython.serial.wait_for("first run") + circuitpython.serial.write("\x04") + circuitpython.wait_until_done() # Should see "first run" appear multiple times due to reload # or see a soft reboot message - assert "first run" in result.output + output = circuitpython.serial.all_output + assert "first run" in output # The soft reload should restart the code before "done" is printed - assert "done" in result.output - assert result.output.count("first run") > 1 + assert "done" in output + assert output.count("first run") > 1 diff --git a/ports/zephyr-cp/tests/test_i2c.py b/ports/zephyr-cp/tests/test_i2c.py index 594dfcc8f4d1c..ec5229faa2f26 100644 --- a/ports/zephyr-cp/tests/test_i2c.py +++ b/ports/zephyr-cp/tests/test_i2c.py @@ -3,6 +3,8 @@ """Test I2C functionality on native_sim.""" +import pytest + I2C_SCAN_CODE = """\ import board @@ -17,18 +19,20 @@ """ -def test_i2c_scan(run_circuitpython): +@pytest.mark.circuitpy_drive({"code.py": I2C_SCAN_CODE}) +def test_i2c_scan(circuitpython): """Test I2C bus scanning finds emulated devices. The AT24 EEPROM emulator responds to zero-length probe writes, so it should appear in scan results at address 0x50. """ - result = run_circuitpython(I2C_SCAN_CODE, timeout=5.0) + circuitpython.wait_until_done() - assert "I2C devices:" in result.output + output = circuitpython.serial.all_output + assert "I2C devices:" in output # AT24 EEPROM should be at address 0x50 - assert "0x50" in result.output - assert "done" in result.output + assert "0x50" in output + assert "done" in output AT24_READ_CODE = """\ @@ -61,38 +65,38 @@ def test_i2c_scan(run_circuitpython): """ -def test_i2c_at24_read(run_circuitpython): +@pytest.mark.circuitpy_drive({"code.py": AT24_READ_CODE}) +def test_i2c_at24_read(circuitpython): """Test reading from AT24 EEPROM emulator.""" - result = run_circuitpython(AT24_READ_CODE, timeout=5.0) + circuitpython.wait_until_done() - assert "AT24 byte 0: 0xFF" in result.output - assert "eeprom_valid" in result.output - assert "done" in result.output + output = circuitpython.serial.all_output + assert "AT24 byte 0: 0xFF" in output + assert "eeprom_valid" in output + assert "done" in output -def test_i2c_device_disabled(run_circuitpython): +@pytest.mark.circuitpy_drive({"code.py": I2C_SCAN_CODE}) +@pytest.mark.disable_i2c_devices("eeprom@50") +def test_i2c_device_disabled(circuitpython): """Test that disabled I2C device doesn't appear in scan.""" - result = run_circuitpython( - I2C_SCAN_CODE, - timeout=5.0, - disabled_i2c_devices=["eeprom@50"], - ) + circuitpython.wait_until_done() - assert "I2C devices:" in result.output + output = circuitpython.serial.all_output + assert "I2C devices:" in output # AT24 at 0x50 should NOT appear when disabled - assert "0x50" not in result.output - assert "done" in result.output + assert "0x50" not in output + assert "done" in output -def test_i2c_device_disabled_communication_fails(run_circuitpython): +@pytest.mark.circuitpy_drive({"code.py": AT24_READ_CODE}) +@pytest.mark.disable_i2c_devices("eeprom@50") +def test_i2c_device_disabled_communication_fails(circuitpython): """Test that communication with disabled I2C device fails.""" - result = run_circuitpython( - AT24_READ_CODE, - timeout=5.0, - disabled_i2c_devices=["eeprom@50"], - ) + circuitpython.wait_until_done() + output = circuitpython.serial.all_output # Should get an I2C error when trying to communicate - assert "I2C error" in result.output - assert "eeprom_valid" not in result.output - assert "done" in result.output + assert "I2C error" in output + assert "eeprom_valid" not in output + assert "done" in output diff --git a/shared-bindings/_bleio/PacketBuffer.h b/shared-bindings/_bleio/PacketBuffer.h index dd0c1ae9e2548..24fb24bce78d3 100644 --- a/shared-bindings/_bleio/PacketBuffer.h +++ b/shared-bindings/_bleio/PacketBuffer.h @@ -6,6 +6,7 @@ #pragma once +#include "shared-bindings/_bleio/Characteristic.h" #include "common-hal/_bleio/PacketBuffer.h" extern const mp_obj_type_t bleio_packet_buffer_type; From aff436cee40790ca6e532af9dfbc3ea48a83a5f6 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 18 Feb 2026 11:05:19 -0800 Subject: [PATCH 337/517] Exclude AGENTS.md from docs build --- conf.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/conf.py b/conf.py index 34436c09020e3..e2a8cbbd82faa 100644 --- a/conf.py +++ b/conf.py @@ -192,7 +192,12 @@ def autoapi_prepare_jinja_env(jinja_env): # Port READMEs in various formats "ports/*/README*", ] -exclude_patterns = ["docs/autoapi/templates/**", "docs/README.md"] +exclude_patterns = [ + "docs/autoapi/templates/**", + "docs/README.md", + "AGENTS.md", + "**/AGENTS.md", +] # The reST default role (used for this markup: `text`) to use for all # documents. From 84e8f359cf78e0dca8a01bbb3d3731cf5054a675 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 18 Feb 2026 12:19:24 -0800 Subject: [PATCH 338/517] Install babblesim --- ports/zephyr-cp/zephyr-config/west.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index 3eb38f7bea63b..98caa4a0b2b39 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -1,4 +1,6 @@ manifest: + group-filter: + - +babblesim projects: - name: zephyr url: https://github.com/adafruit/zephyr From 0a244eeb2ffc4cd957c3a535b9eb3baa9bd3d762 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 18 Feb 2026 15:58:34 -0800 Subject: [PATCH 339/517] Remove extra yields for Zephyr Instead, delay in a couple spots within Zephyr. --- ports/zephyr-cp/background.c | 9 ++++----- ports/zephyr-cp/mphalport.h | 7 ++++++- ports/zephyr-cp/supervisor/port.c | 8 ++++---- supervisor/shared/background_callback.c | 8 -------- supervisor/shared/safe_mode.c | 4 ---- 5 files changed, 14 insertions(+), 22 deletions(-) diff --git a/ports/zephyr-cp/background.c b/ports/zephyr-cp/background.c index 9afade8913697..1abc034e878ae 100644 --- a/ports/zephyr-cp/background.c +++ b/ports/zephyr-cp/background.c @@ -36,10 +36,9 @@ void port_background_tick(void) { #endif } -// Allow boards to override this. -MP_WEAK void board_background_task(void) { -} - void port_background_task(void) { - board_background_task(); + // Make sure time advances in the simulator. + #if defined(CONFIG_ARCH_POSIX) + k_busy_wait(100); + #endif } diff --git a/ports/zephyr-cp/mphalport.h b/ports/zephyr-cp/mphalport.h index b3adf5830d059..ee696a4f04370 100644 --- a/ports/zephyr-cp/mphalport.h +++ b/ports/zephyr-cp/mphalport.h @@ -13,7 +13,12 @@ #include "py/mpconfig.h" #include "supervisor/shared/tick.h" +#include + #define mp_hal_ticks_ms() ((mp_uint_t)supervisor_ticks_ms32()) -#define mp_hal_delay_us(us) NRFX_DELAY_US((uint32_t)(us)) + +static inline void mp_hal_delay_us(mp_uint_t us) { + k_busy_wait((uint32_t)us); +} bool mp_hal_stdin_any(void); diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 7be2beb1d2085..786e6c2f2f554 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -67,10 +67,6 @@ void port_wake_main_task_from_isr(void) { void port_yield(void) { k_yield(); - // Make sure time advances in the simulator. - #if defined(CONFIG_ARCH_POSIX) - k_busy_wait(100); - #endif } void port_boot_info(void) { @@ -97,6 +93,10 @@ uint32_t port_get_saved_word(void) { } uint64_t port_get_raw_ticks(uint8_t *subticks) { + // Make sure time advances in the simulator. + #if defined(CONFIG_ARCH_POSIX) + k_busy_wait(100); + #endif int64_t uptime = k_uptime_ticks() * 32768 / CONFIG_SYS_CLOCK_TICKS_PER_SEC; if (subticks != NULL) { *subticks = uptime % 32; diff --git a/supervisor/shared/background_callback.c b/supervisor/shared/background_callback.c index 79b18f7a13753..ffeb78bbb8371 100644 --- a/supervisor/shared/background_callback.c +++ b/supervisor/shared/background_callback.c @@ -61,10 +61,6 @@ static int background_prevention_count; void PLACE_IN_ITCM(background_callback_run_all)(void) { port_background_task(); if (!background_callback_pending()) { - // TEMPORARY to fix #10822 - #ifdef __ZEPHYR__ - port_yield(); - #endif return; } CALLBACK_CRITICAL_BEGIN; @@ -91,10 +87,6 @@ void PLACE_IN_ITCM(background_callback_run_all)(void) { } --background_prevention_count; CALLBACK_CRITICAL_END; - // TEMPORARY to fix #10822 - #ifdef __ZEPHYR__ - port_yield(); - #endif } void background_callback_prevent(void) { diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index bfe27ac7c6513..3860de6621f71 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -82,10 +82,6 @@ safe_mode_t wait_for_safe_mode_reset(void) { boot_in_safe_mode = true; break; } - // TEMPORARY to fix #10822 - #ifdef __ZEPHYR__ - port_yield(); - #endif diff = supervisor_ticks_ms64() - start_ticks; } #if CIRCUITPY_STATUS_LED From e6d863a60a17729c2c5002f27f1425848d0320ba Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 18 Feb 2026 16:07:27 -0800 Subject: [PATCH 340/517] Handle bsim and blobs --- .../boards/renesas/da14695_dk_usb/circuitpython.toml | 1 + ports/zephyr-cp/cptools/pre_zephyr_build_prep.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml index 3272dd4c5f319..f7fad6bc4443a 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml @@ -1 +1,2 @@ CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +BLOBS=["hal_renesas"] diff --git a/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py b/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py index 0ed280cbc6775..acc3ae786196d 100644 --- a/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py +++ b/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py @@ -19,5 +19,14 @@ mpconfigboard = tomllib.load(f) blobs = mpconfigboard.get("BLOBS", []) +blob_fetch_args = mpconfigboard.get("blob_fetch_args", {}) for blob in blobs: - subprocess.run(["west", "blobs", "fetch", blob], check=True) + args = blob_fetch_args.get(blob, []) + subprocess.run(["west", "blobs", "fetch", blob, *args], check=True) + +if board.endswith("bsim"): + subprocess.run( + ["make", "everything", "-j", "8"], + cwd=portdir / "tools" / "bsim", + check=True, + ) From 35a7e433ac5a2e01bc15ddebfc2bcf05f2ed3ba0 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 18 Feb 2026 19:34:10 -0500 Subject: [PATCH 341/517] MagTag: autodetect display type --- .../adafruit_magtag_2.9_grayscale/board.c | 84 ++++++++++++------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index dc117deee632e..1437f75165d89 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -135,10 +135,23 @@ const uint8_t ssd1680_display_refresh_sequence[] = { 0x20, 0x00, 0x00 }; -static bool detect_ssd1680(void) { - // Bitbang 4-wire SPI with a bidirectional data line to read register 0x71. - // On the IL0373 it will return 0x13 or similar. On the SSD1680 it is - // unsupported and will be 0xff. + +typedef enum { + DISPLAY_IL0373, + DISPLAY_SSD1680_COLSTART_0, + DISPLAY_SSD1680_COLSTART_8, +} display_type_t; + +static display_type_t detect_display_type(void) { + // Bitbang 4-wire SPI with a bidirectional data line to read the first word of register 0x2e, + // which is the 10-byte USER ID. + // On the IL0373 it will return 0xff because it's not a valid register. + // With SSD1680, we have seen two types: + // 1. The first batch of displays, labeled "FPC-A005 20.06.15 TRX", which needs colstart=0. + // These have 10 byes of zeros in the User ID + // 2. Second batch, labeled "FPC-7619rev.b", which needs colstart=8. + // The USER ID for these boards is [0x44, 0x0, 0x4, 0x0, 0x25, 0x0, 0x1, 0x78, 0x2b, 0xe] + // So let's distinguish just by the first byte. digitalio_digitalinout_obj_t data; digitalio_digitalinout_obj_t clock; digitalio_digitalinout_obj_t chip_select; @@ -163,7 +176,7 @@ static bool detect_ssd1680(void) { common_hal_digitalio_digitalinout_switch_to_output(&reset, true, DRIVE_MODE_PUSH_PULL); common_hal_digitalio_digitalinout_switch_to_output(&clock, false, DRIVE_MODE_PUSH_PULL); - uint8_t status_read = 0x71; + uint8_t status_read = 0x2e; // SSD1680 User ID register. Not a valid register on IL0373. for (int i = 0; i < 8; i++) { common_hal_digitalio_digitalinout_set_value(&data, (status_read & (1 << (7 - i))) != 0); common_hal_digitalio_digitalinout_set_value(&clock, true); @@ -192,11 +205,20 @@ static bool detect_ssd1680(void) { common_hal_digitalio_digitalinout_deinit(&chip_select); common_hal_digitalio_digitalinout_deinit(&data_command); common_hal_digitalio_digitalinout_deinit(&reset); - return status == 0xff; + + switch (status) { + case 0xff: + return DISPLAY_IL0373; + default: // who knows? Just guess. + case 0x00: + return DISPLAY_SSD1680_COLSTART_0; + case 0x44: + return DISPLAY_SSD1680_COLSTART_8; + } } void board_init(void) { - bool is_ssd1680 = detect_ssd1680(); + display_type_t display_type = detect_display_type(); fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; busio_spi_obj_t *spi = &bus->inline_bus; @@ -216,8 +238,33 @@ void board_init(void) { epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - if (is_ssd1680) { + if (display_type == DISPLAY_IL0373) { + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = il0373_display_start_sequence; + args.start_sequence_len = sizeof(il0373_display_start_sequence); + args.stop_sequence = il0373_display_stop_sequence; + args.stop_sequence_len = sizeof(il0373_display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 160; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = 0x10; + args.write_color_ram_command = 0x13; + args.refresh_sequence = il0373_display_refresh_sequence; + args.refresh_sequence_len = sizeof(il0373_display_refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO5; + args.seconds_per_frame = 5.0; + args.grayscale = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); + } else { epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + // Default colstart is 0. + if (display_type == DISPLAY_SSD1680_COLSTART_8) { + args.colstart = 8; + } args.bus = bus; args.start_sequence = ssd1680_display_start_sequence; args.start_sequence_len = sizeof(ssd1680_display_start_sequence); @@ -244,27 +291,6 @@ void board_init(void) { args.two_byte_sequence_length = true; args.address_little_endian = true; common_hal_epaperdisplay_epaperdisplay_construct(display, &args); - } else { - epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; - args.bus = bus; - args.start_sequence = il0373_display_start_sequence; - args.start_sequence_len = sizeof(il0373_display_start_sequence); - args.stop_sequence = il0373_display_stop_sequence; - args.stop_sequence_len = sizeof(il0373_display_stop_sequence); - args.width = 296; - args.height = 128; - args.ram_width = 160; - args.ram_height = 296; - args.rotation = 270; - args.write_black_ram_command = 0x10; - args.write_color_ram_command = 0x13; - args.refresh_sequence = il0373_display_refresh_sequence; - args.refresh_sequence_len = sizeof(il0373_display_refresh_sequence); - args.refresh_time = 1.0; - args.busy_pin = &pin_GPIO5; - args.seconds_per_frame = 5.0; - args.grayscale = true; - common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } } From b59a3e59706abd0f4a6247381d1bcf7389b665cc Mon Sep 17 00:00:00 2001 From: Bernhard Bablok Date: Sat, 14 Feb 2026 09:54:54 +0100 Subject: [PATCH 342/517] support user-configuration files --- .gitignore | 3 +++ py/circuitpy_mkenv.mk | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index f996911d8d746..80b045f5ec536 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,9 @@ __pycache__/ ###################### GNUmakefile user.props +mpconfiguser1.mk +mpconfiguser2.mk +user_defns.mk # Sphinx output ############### diff --git a/py/circuitpy_mkenv.mk b/py/circuitpy_mkenv.mk index 7bdf943a14d04..49ce0978b208e 100644 --- a/py/circuitpy_mkenv.mk +++ b/py/circuitpy_mkenv.mk @@ -43,6 +43,10 @@ ifneq ($(VALID_BOARD),) include boards/$(BOARD)/mpconfigboard.mk endif +# user-specific settings that mpconfigport does not override +# (i.e. mpconfigport.mk uses "foo ?= bar") +-include mpconfiguser1.mk + # Port-specific include mpconfigport.mk @@ -52,6 +56,10 @@ ifneq ($(VALID_BOARD),) include $(TOP)/py/circuitpy_mpconfig.mk endif +# user-specific overrides of hard-coded settings +# (i.e. xxx.mk uses "foo = bar") +-include mpconfiguser2.mk + # qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h @@ -62,3 +70,6 @@ include $(TOP)/supervisor/supervisor.mk # Include make rules and variables common across CircuitPython builds. include $(TOP)/py/circuitpy_defns.mk + +# user specific +-include user_defns.mk From 387a45c99aa2b9a786e04a6a6fe1aa1729b0c961 Mon Sep 17 00:00:00 2001 From: Bernhard Bablok Date: Mon, 16 Feb 2026 08:12:02 +0100 Subject: [PATCH 343/517] changed filenames as requested --- .gitignore | 6 +++--- py/circuitpy_mkenv.mk | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 80b045f5ec536..3e9ee009cb3c3 100644 --- a/.gitignore +++ b/.gitignore @@ -55,9 +55,9 @@ __pycache__/ ###################### GNUmakefile user.props -mpconfiguser1.mk -mpconfiguser2.mk -user_defns.mk +user_pre_mpconfigport.mk +user_post_mpconfigport.mk +user_post_circuitpy_defns.mk # Sphinx output ############### diff --git a/py/circuitpy_mkenv.mk b/py/circuitpy_mkenv.mk index 49ce0978b208e..6416d6bd19cfb 100644 --- a/py/circuitpy_mkenv.mk +++ b/py/circuitpy_mkenv.mk @@ -45,7 +45,7 @@ endif # user-specific settings that mpconfigport does not override # (i.e. mpconfigport.mk uses "foo ?= bar") --include mpconfiguser1.mk +-include user_pre_mpconfigport.mk # Port-specific include mpconfigport.mk @@ -58,7 +58,7 @@ endif # user-specific overrides of hard-coded settings # (i.e. xxx.mk uses "foo = bar") --include mpconfiguser2.mk +-include user_post_mpconfigport.mk # qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h @@ -72,4 +72,4 @@ include $(TOP)/supervisor/supervisor.mk include $(TOP)/py/circuitpy_defns.mk # user specific --include user_defns.mk +-include user_post_circuitpy_defns.mk From e46458705e1c1cf4e8a054aa9f5e3c7c35b618a3 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Thu, 19 Feb 2026 10:43:37 +0100 Subject: [PATCH 344/517] Lower SPI speed on uGame S3 to avoid display glitches --- ports/espressif/boards/deshipu_ugame_s3/board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/boards/deshipu_ugame_s3/board.c b/ports/espressif/boards/deshipu_ugame_s3/board.c index 677cab7f07655..dc09786e74ce7 100644 --- a/ports/espressif/boards/deshipu_ugame_s3/board.c +++ b/ports/espressif/boards/deshipu_ugame_s3/board.c @@ -80,7 +80,7 @@ void board_init(void) { MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_DC Command or data MP_OBJ_FROM_PTR(&pin_GPIO10), // TFT_CS Chip select MP_OBJ_FROM_PTR(&pin_GPIO13), // TFT_RESET Reset - 80000000L, // Baudrate + 48000000L, // Baudrate 0, // Polarity 0); // Phase From 3dfba6b8b7d4d7e21165dd77bb8285a7bc6894e0 Mon Sep 17 00:00:00 2001 From: Bernhard Bablok Date: Thu, 19 Feb 2026 15:21:48 +0100 Subject: [PATCH 345/517] added documentation for user config files --- BUILDING.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/BUILDING.md b/BUILDING.md index 34cd544d73658..6778965bed099 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -64,6 +64,52 @@ If you aren't sure what boards exist, have a peek in the boards subdirectory of If you have a fast computer with many cores, consider adding `-j` to your build flags, such as `-j17` on a 6-core 12-thread machine. +## Configuration + +Ports and boards are preconfigured, thus make knows how to build a specific +board. Power users can change the configuration of a specific board or port, +either by passing compile-time options to make, or by creating appropriate +make include files. + +The configuration system is hierarchical. A higher level will typically only +set an option that a lower level hasn't configured: + +* board configuration: `mpconfigport.mk` +* pre-port user configuration: `user_pre_mpconfigport.mk` +* port configuration: `mpconfigport.mk` +* post-port user configuration: `user_post_mpconfigport.mk` +* global configuration: `py/circuitpython_mpconfig.mk` + +The board configuration is within the board-directory, e.g. +`ports/raspberrypi/boards/raspberry_pi_pico/`, the port configuration is +in the port-directory, e.g. `ports/raspberrypi/`. + +Editing these configuration files is the way to go if you want to change +the default behavior and ultimately create a pull-request. Otherwise, +changes should go into one of the user configuration files. + +User specific configurations are optional and should be maintained out of +tree. Passing `-I directory` tells make where to search for the additional +configuration files. E.g. to speed up boots by removing the wait-time for +the save-mode button press, you would: + +* create a directory: `mkdir -p ~/my_cp_config` +* create the config file: `echo 'CIRCUITPY_SKIP_SAFE_MODE_WAIT=0' > ~/my_cp_config/user_pre_mpconfigport.mk` +* run make with: `make -I ~/my_cp_config BOARD=raspberry_pi_pico` + +Besides the `user*mpconfigport.mk` files, there is another optional file +named `user_post_circuitpy_defns.mk`. This file is included at the end +and can be used to tweak compiler-definitions that are not covered by +one of the compile time options `CIRCUITPY_*`. + +Example: to create a build for the Pico2-W with an integrated saves-partition, +you would create a `user_post_circuitpy_defns.mk` with the following content: + + $(info ===> processing user_post_circuitpy_defns.mk) + ifeq (${BOARD},raspberry_pi_pico2_w) + CFLAGS += -DCIRCUITPY_SAVES_PARTITION_SIZE=1048576 + endif + ## Testing If you are working on changes to the core language, you might find it useful to run the test suite. From 3abe916d5fa93a092864a86837170b406517d58b Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 19 Feb 2026 09:24:58 -0800 Subject: [PATCH 346/517] Fix the builds --- ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf | 4 ++++ .../boards/nxp/frdm_rw612/autogen_board_info.toml | 1 + .../boards/renesas/da14695_dk_usb/autogen_board_info.toml | 1 + ports/zephyr-cp/cptools/zephyr2cp.py | 7 +++++-- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf index 9829d4a15817e..afb546a980d2f 100644 --- a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf @@ -14,3 +14,7 @@ CONFIG_BT_CENTRAL=y CONFIG_BT_BROADCASTER=y CONFIG_BT_OBSERVER=y CONFIG_BT_EXT_ADV=y + +CONFIG_LOG=n +CONFIG_ASSERT=n +CONFIG_TEST_RANDOM_GENERATOR=y diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index c9bd2729b9e6d..f7ad0289cc75e 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index e5adbfd582d3f..77d93aa10f095 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -51,6 +51,7 @@ gifio = false gnss = false hashlib = false i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index d275a7ce960e2..86b0b0fe40386 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -588,15 +588,18 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa if len(all_ioports) > 1: a, b = all_ioports[:2] i = 0 - while a[i] == b[i]: + max_i = min(len(a), len(b)) + while i < max_i and a[i] == b[i]: i += 1 shared_prefix = a[:i] for ioport in ioports: if not ioport.startswith(shared_prefix): shared_prefix = "" break - else: + elif all_ioports: shared_prefix = all_ioports[0] + else: + shared_prefix = "" pin_defs = [] pin_declarations = ["#pragma once"] From 839236ea263fb49b0fab2bbdd4c71d68743662f6 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 19 Feb 2026 13:36:58 -0800 Subject: [PATCH 347/517] Address PR review feedback from dhalbert Done mostly automatically: https://pi.dev/session/#f06760de11a3f7b5f80620088e37bd6b --- ports/zephyr-cp/common-hal/_bleio/Adapter.c | 4 ++-- ports/zephyr-cp/common-hal/_bleio/Adapter.h | 1 + ports/zephyr-cp/common-hal/_bleio/Attribute.c | 1 + ports/zephyr-cp/common-hal/_bleio/Attribute.h | 1 + ports/zephyr-cp/common-hal/_bleio/Characteristic.c | 1 + ports/zephyr-cp/common-hal/_bleio/Characteristic.h | 1 + .../zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c | 1 + .../zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h | 1 + ports/zephyr-cp/common-hal/_bleio/Connection.c | 1 + ports/zephyr-cp/common-hal/_bleio/Connection.h | 1 + ports/zephyr-cp/common-hal/_bleio/Descriptor.c | 1 + ports/zephyr-cp/common-hal/_bleio/Descriptor.h | 1 + ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c | 1 + ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h | 1 + ports/zephyr-cp/common-hal/_bleio/Service.c | 1 + ports/zephyr-cp/common-hal/_bleio/Service.h | 1 + ports/zephyr-cp/common-hal/_bleio/UUID.c | 1 + ports/zephyr-cp/common-hal/_bleio/UUID.h | 1 + ports/zephyr-cp/common-hal/_bleio/__init__.c | 1 + ports/zephyr-cp/common-hal/_bleio/__init__.h | 1 + ports/zephyr-cp/common-hal/wifi/__init__.c | 9 ++------- ports/zephyr-cp/supervisor/port.c | 10 +++++++++- ports/zephyr-cp/tests/docs/babblesim.md | 2 +- 23 files changed, 33 insertions(+), 11 deletions(-) diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.c b/ports/zephyr-cp/common-hal/_bleio/Adapter.c index 7257f54c35f9e..d72a7bed865af 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.c +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT @@ -132,9 +133,8 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable if (enabled) { if (!bt_is_ready()) { int err = bt_enable(NULL); - raise_zephyr_error(err); if (err != 0) { - return; + raise_zephyr_error(err); } } ble_adapter_enabled = true; diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.h b/ports/zephyr-cp/common-hal/_bleio/Adapter.h index 7cb3dc402c609..dda9075776e6e 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.h +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Attribute.c b/ports/zephyr-cp/common-hal/_bleio/Attribute.c index cf8a818e6a74e..6312f3d46b80f 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Attribute.c +++ b/ports/zephyr-cp/common-hal/_bleio/Attribute.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Attribute.h b/ports/zephyr-cp/common-hal/_bleio/Attribute.h index 417ac3c454955..24b0f78a106e5 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Attribute.h +++ b/ports/zephyr-cp/common-hal/_bleio/Attribute.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Characteristic.c b/ports/zephyr-cp/common-hal/_bleio/Characteristic.c index 6e71f84862781..386be6004d2a1 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Characteristic.c +++ b/ports/zephyr-cp/common-hal/_bleio/Characteristic.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Characteristic.h b/ports/zephyr-cp/common-hal/_bleio/Characteristic.h index b9021c1329523..b710a9f2662b8 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Characteristic.h +++ b/ports/zephyr-cp/common-hal/_bleio/Characteristic.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c index e6ae3bf9b724e..17e000e905eb7 100644 --- a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c +++ b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h index 53b10413775da..91ea262945af5 100644 --- a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h +++ b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.c b/ports/zephyr-cp/common-hal/_bleio/Connection.c index b0cac6443678a..2e93b6ab127b6 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Connection.c +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.h b/ports/zephyr-cp/common-hal/_bleio/Connection.h index b3faef63f8c18..f8f9581ad00bb 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Connection.h +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Descriptor.c b/ports/zephyr-cp/common-hal/_bleio/Descriptor.c index 847546b1cedf0..a3e65a5e006f6 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Descriptor.c +++ b/ports/zephyr-cp/common-hal/_bleio/Descriptor.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Descriptor.h b/ports/zephyr-cp/common-hal/_bleio/Descriptor.h index 8abf1dcfaad4e..1d29cb27a509b 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Descriptor.h +++ b/ports/zephyr-cp/common-hal/_bleio/Descriptor.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c index 6cba213515658..82fe8a3d1760c 100644 --- a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c +++ b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h index c96dd9f2eb6b0..c8cd763fd6146 100644 --- a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h +++ b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Service.c b/ports/zephyr-cp/common-hal/_bleio/Service.c index da2bf48e16f5f..cefc85b6df655 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Service.c +++ b/ports/zephyr-cp/common-hal/_bleio/Service.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/Service.h b/ports/zephyr-cp/common-hal/_bleio/Service.h index 03ab8b5d7fd8d..86727d3b0f73b 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Service.h +++ b/ports/zephyr-cp/common-hal/_bleio/Service.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/UUID.c b/ports/zephyr-cp/common-hal/_bleio/UUID.c index 3bfbffb5a2b95..916eedb2c4745 100644 --- a/ports/zephyr-cp/common-hal/_bleio/UUID.c +++ b/ports/zephyr-cp/common-hal/_bleio/UUID.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/UUID.h b/ports/zephyr-cp/common-hal/_bleio/UUID.h index d58016cffefc9..386f5a7b8b971 100644 --- a/ports/zephyr-cp/common-hal/_bleio/UUID.h +++ b/ports/zephyr-cp/common-hal/_bleio/UUID.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.c b/ports/zephyr-cp/common-hal/_bleio/__init__.c index 5b8f8ece8988c..fca1eae98278d 100644 --- a/ports/zephyr-cp/common-hal/_bleio/__init__.c +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.c @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.h b/ports/zephyr-cp/common-hal/_bleio/__init__.h index 9c30c7c8063b3..1502767c61597 100644 --- a/ports/zephyr-cp/common-hal/_bleio/__init__.h +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.h @@ -1,6 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT diff --git a/ports/zephyr-cp/common-hal/wifi/__init__.c b/ports/zephyr-cp/common-hal/wifi/__init__.c index f043734ebfb4d..da26d56072444 100644 --- a/ports/zephyr-cp/common-hal/wifi/__init__.c +++ b/ports/zephyr-cp/common-hal/wifi/__init__.c @@ -48,18 +48,15 @@ static struct net_mgmt_event_callback ipv4_cb; static void _event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface) { wifi_radio_obj_t *self = &common_hal_wifi_radio_obj; - printk("_event_handler cb %p event 0x%" PRIx64 " if %p\n", cb, mgmt_event, iface); + (void)iface; switch (mgmt_event) { case NET_EVENT_WIFI_SCAN_RESULT: { - printk("NET_EVENT_WIFI_SCAN_RESULT\n"); #if defined(CONFIG_NET_MGMT_EVENT_INFO) const struct wifi_scan_result *result = cb->info; if (result != NULL && self->current_scan != NULL) { wifi_scannednetworks_scan_result(self->current_scan, result); } - #else - printk("Scan result info unavailable (CONFIG_NET_MGMT_EVENT_INFO disabled)\n"); #endif break; } @@ -306,9 +303,7 @@ void common_hal_wifi_init(bool user_initiated) { } snprintf(cpy_default_hostname, sizeof(cpy_default_hostname), "cpy-%s-%02x%02x%02x%02x%02x%02x", CIRCUITPY_BOARD_ID + board_trim, mac->addr[0], mac->addr[1], mac->addr[2], mac->addr[3], mac->addr[4], mac->addr[5]); - if (net_hostname_set(cpy_default_hostname, strlen(cpy_default_hostname)) != 0) { - printk("setting hostname failed\n"); - } + CHECK_ZEPHYR_RESULT(net_hostname_set(cpy_default_hostname, strlen(cpy_default_hostname))); #else printk("Hostname support disabled in Zephyr config\n"); #endif diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 595bc6a3491e8..5a7a982392a52 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -145,8 +145,10 @@ void port_idle_until_interrupt(void) { // Zephyr doesn't maintain one multi-heap. So, make our own using TLSF. void port_heap_init(void) { + // Do a test malloc to determine if Zephyr has an outer heap that may + // overlap with a memory region we've identified in ram_bounds. We'll + // corrupt each other if we both use it. #ifdef CONFIG_COMMON_LIBC_MALLOC - // Do a test malloc to determine if Zephyr has an outer heap. uint32_t *test_malloc = malloc(32); free(test_malloc); // Free right away so we don't forget. We don't actually write it anyway. zephyr_malloc_active = test_malloc != NULL; @@ -156,11 +158,17 @@ void port_heap_init(void) { uint32_t *heap_bottom = ram_bounds[2 * i]; uint32_t *heap_top = ram_bounds[2 * i + 1]; size_t size = (heap_top - heap_bottom) * sizeof(uint32_t); + // The linker script may fill up a region we thought we could use at + // build time. (The ram_bounds values are sometimes determined by the + // linker.) So, we need to guard against regions that aren't actually + // free. if (size < 1024) { printk("Skipping region because the linker filled it up.\n"); continue; } #ifdef CONFIG_COMMON_LIBC_MALLOC + // Skip a ram region if our test malloc is within it. We'll use Zephyr's + // malloc to share that space with Zephyr. if (heap_bottom <= test_malloc && test_malloc < heap_top) { zephyr_malloc_top = heap_top; zephyr_malloc_bottom = heap_bottom; diff --git a/ports/zephyr-cp/tests/docs/babblesim.md b/ports/zephyr-cp/tests/docs/babblesim.md index 39d60fa306cbd..abf68b2b1de19 100644 --- a/ports/zephyr-cp/tests/docs/babblesim.md +++ b/ports/zephyr-cp/tests/docs/babblesim.md @@ -28,7 +28,7 @@ export BSIM_OUT_PATH=/path/to/bsim ## Build ``` -CCACHE_TEMPDIR=/tmp/ccache-tmp make -j32 BOARD=native_nrf5340bsim +CCACHE_TEMPDIR=/tmp/ccache-tmp make -j BOARD=native_nrf5340bsim ``` If you do not use ccache, you can omit `CCACHE_TEMPDIR`. From f7ee5cf90a1f8fb9398a9f4e847cdedd63bea5c2 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 19 Feb 2026 14:30:21 -0800 Subject: [PATCH 348/517] Use a git sha for revision --- ports/zephyr-cp/zephyr-config/west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index 98caa4a0b2b39..159467ae7e601 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -4,6 +4,6 @@ manifest: projects: - name: zephyr url: https://github.com/adafruit/zephyr - revision: circuitpython-v4.3.0 + revision: 3c5a3a72daa3ca6462cd8bc9c8c7c6a41fbf3b2e clone-depth: 100 import: true From 2b0e36802752f4ba3b53d563feae8ef2ee0581e1 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 20 Feb 2026 09:54:15 -0500 Subject: [PATCH 349/517] espressif: check for deinit in common_hal_busio_i2c_unlock() I2C bus is unlocked in reset_board_buses(), but it might have been deinited already. --- ports/espressif/common-hal/busio/I2C.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ports/espressif/common-hal/busio/I2C.c b/ports/espressif/common-hal/busio/I2C.c index 8ff608717c78f..890f9339c2254 100644 --- a/ports/espressif/common-hal/busio/I2C.c +++ b/ports/espressif/common-hal/busio/I2C.c @@ -132,6 +132,7 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { xSemaphoreGive(self->xSemaphore); vSemaphoreDelete(self->xSemaphore); self->xSemaphore = NULL; + self->has_lock = false; common_hal_reset_pin(self->sda_pin); common_hal_reset_pin(self->scl_pin); @@ -165,6 +166,9 @@ bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { } void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { + if (common_hal_busio_i2c_deinited(self)) { + return; + } xSemaphoreGive(self->xSemaphore); self->has_lock = false; } From 473ec3862b3db8dc4ef4b1c4370306259a956377 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 20 Feb 2026 10:23:13 -0800 Subject: [PATCH 350/517] ports/espressif: update esp-idf to 5.5.3 Signed-off-by: Scott Shawcroft --- ports/espressif/esp-idf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/esp-idf b/ports/espressif/esp-idf index f4fddd2d05b44..1d2c73f641af7 160000 --- a/ports/espressif/esp-idf +++ b/ports/espressif/esp-idf @@ -1 +1 @@ -Subproject commit f4fddd2d05b44d1d606b546b596d17de0a73e9eb +Subproject commit 1d2c73f641af70c24274e2d3e74641592bee97e5 From 0a18d1d364ebe7f2904b51fcc0401367ff96efd9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 20 Feb 2026 13:44:06 -0500 Subject: [PATCH 351/517] pin click<8.2.0 to avoid esp-idf build problem --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 566618d2c4721..6505a98290689 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ jinja2 typer sh -click +click<8.2.0 cpp-coveralls requests From 8147243cc748330b5c158a3301011ba94672ce2b Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 20 Feb 2026 11:49:23 -0800 Subject: [PATCH 352/517] ci: apply ESP-IDF constraints to requirements-dev install Signed-off-by: Scott Shawcroft --- .github/actions/deps/external/action.yml | 12 +++++- requirements-dev.txt | 3 +- tools/ci_set_idf_constraint.py | 47 ++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 tools/ci_set_idf_constraint.py diff --git a/.github/actions/deps/external/action.yml b/.github/actions/deps/external/action.yml index 564c851cfc913..81e73f871efc6 100644 --- a/.github/actions/deps/external/action.yml +++ b/.github/actions/deps/external/action.yml @@ -56,6 +56,16 @@ runs: uses: ./.github/actions/deps/python with: action: ${{ inputs.action }} + - name: Set ESP-IDF constraints path + if: inputs.port == 'espressif' + run: python3 -u tools/ci_set_idf_constraint.py + shell: bash + - name: Install python dependencies - run: pip install -r requirements-dev.txt + run: | + if [ -n "${IDF_CONSTRAINT_FILE:-}" ] && [ -f "$IDF_CONSTRAINT_FILE" ]; then + pip install -c "$IDF_CONSTRAINT_FILE" -r requirements-dev.txt + else + pip install -r requirements-dev.txt + fi shell: bash diff --git a/requirements-dev.txt b/requirements-dev.txt index 566618d2c4721..3c7e18b7bf04d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -25,8 +25,7 @@ intelhex # for building & testing natmods pyelftools -# newer versions break ESP-IDF now -cryptography<45 +cryptography # for web workflow minify minify_html diff --git a/tools/ci_set_idf_constraint.py b/tools/ci_set_idf_constraint.py new file mode 100644 index 0000000000000..49c187f458782 --- /dev/null +++ b/tools/ci_set_idf_constraint.py @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Adafruit Industries LLC +# +# SPDX-License-Identifier: MIT + +"""Set IDF_CONSTRAINT_FILE for CI. + +CI installs requirements-dev.txt for all ports, but on Espressif builds we must +also apply the matching ESP-IDF constraints file so pip does not upgrade shared +packages (for example click) beyond what ESP-IDF allows. This script derives the +active ESP-IDF major.minor version from version.cmake and exports the exact +constraints file path into GITHUB_ENV for later install steps. +""" + +import os +import pathlib +import re + +TOP = pathlib.Path(__file__).resolve().parent.parent + + +def main() -> None: + version_cmake = TOP / "ports" / "espressif" / "esp-idf" / "tools" / "cmake" / "version.cmake" + data = version_cmake.read_text(encoding="utf-8") + + major = re.search(r"IDF_VERSION_MAJOR\s+(\d+)", data) + minor = re.search(r"IDF_VERSION_MINOR\s+(\d+)", data) + if major is None or minor is None: + raise RuntimeError(f"Unable to parse IDF version from {version_cmake}") + + idf_tools_path = os.environ.get("IDF_TOOLS_PATH") + if not idf_tools_path: + raise RuntimeError("IDF_TOOLS_PATH is not set") + + constraint = ( + pathlib.Path(idf_tools_path) / f"espidf.constraints.v{major.group(1)}.{minor.group(1)}.txt" + ) + + github_env = os.environ.get("GITHUB_ENV") + if github_env: + with open(github_env, "a", encoding="utf-8") as f: + f.write(f"IDF_CONSTRAINT_FILE={constraint}\n") + + print(f"Set IDF_CONSTRAINT_FILE={constraint}") + + +if __name__ == "__main__": + main() From d2d6094cd852ac28ee3e13654cc8bffdb1962b64 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 22 Feb 2026 14:23:32 -0500 Subject: [PATCH 353/517] call reset_all_pins() in main() after port_init() Before #10699, `reset_all_pins()` was called in `reset_port()`. That changed to reset all pins in main after the finalizers ran. However, this mean that pins were not all reset on power-up or hard reset. This caused #1841, because the power control pin for Feather S3 TFT was not powering the display as required before the display was reset. Add a call to `reset_all_pins()` early in `main()` to serve the same purpose. --- main.c | 4 ++++ ports/atmel-samd/supervisor/port.c | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index 7b936488fb794..846e5ece0964a 100644 --- a/main.c +++ b/main.c @@ -1020,8 +1020,12 @@ int __attribute__((used)) main(void) { // initialise the cpu and peripherals set_safe_mode(port_init()); + // All ports need pins reset, after never-reset pins are marked in port_init(); + reset_all_pins(); + port_heap_init(); + // Turn on RX and TX LEDs if we have them. init_rxtx_leds(); diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 31bc5faf82703..fe7b4a3676c79 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -339,11 +339,9 @@ safe_mode_t port_init(void) { init_shared_dma(); // Reset everything into a known state before board_init. + // Pins are reset in main() after this routine returns. reset_port(); - // Reset the pins too. - reset_all_pins(); - #ifdef SAMD21 if (PM->RCAUSE.bit.BOD33 == 1 || PM->RCAUSE.bit.BOD12 == 1) { return SAFE_MODE_BROWNOUT; From 1e707b78021367ef359c6845e5905c3c56990a16 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 23 Feb 2026 10:45:58 -0800 Subject: [PATCH 354/517] Fix P4 and remove canio from c3s-2m --- ports/espressif/Makefile | 3 +++ .../espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk | 1 + 2 files changed, 4 insertions(+) diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index ebc4fa3a4e1f3..9e331740dd6a7 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -123,6 +123,9 @@ INC += \ -isystem esp-idf/components/soc/include \ -isystem esp-idf/components/soc/$(IDF_TARGET)/include \ -isystem esp-idf/components/soc/$(IDF_TARGET)/register \ + -isystem esp-idf/components/soc/$(IDF_TARGET)/register/hw_ver3 \ + -isystem esp-idf/components/soc/$(IDF_TARGET)/register/hw_ver2 \ + -isystem esp-idf/components/soc/$(IDF_TARGET)/register/hw_ver1 \ -isystem esp-idf/components/spi_flash/include \ -isystem esp-idf/components/usb/include \ -isystem esp-idf/components/ulp/ulp_fsm/include \ diff --git a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk index 9ca2eee157748..da3e6a14496a8 100644 --- a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk +++ b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk @@ -10,5 +10,6 @@ CIRCUITPY_ESP_FLASH_SIZE = 2MB CIRCUITPY_DUALBANK = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_CANIO = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 From fde84b9449171404537ed240b95b75952abea52d Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Mon, 23 Feb 2026 17:10:18 -0600 Subject: [PATCH 355/517] Fix pin assignments on Thumby Color --- .../boards/tinycircuits_thumby_color/mpconfigboard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h index ce5d015db5867..fc76138bb8a0b 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h @@ -20,8 +20,8 @@ #define CIRCUITPY_BOARD_LCD_DC (&pin_GPIO16) #define CIRCUITPY_BOARD_LCD_CS (&pin_GPIO17) -#define CIRCUITPY_BOARD_LCD_RESET (&pin_GPIO11) -#define CIRCUITPY_BOARD_LCD_BACKLIGHT (&pin_GPIO20) +#define CIRCUITPY_BOARD_LCD_RESET (&pin_GPIO4) +#define CIRCUITPY_BOARD_LCD_BACKLIGHT (&pin_GPIO7) #define CIRCUITPY_BOARD_SPI (1) #define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL}} From b705adedf1778a7d4ac3b9e306696104b4106350 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 23 Feb 2026 12:44:53 -0800 Subject: [PATCH 356/517] Exit the sim after the desired number of code.py runs Make UART PTY reliable by waiting for all bytes to be read from the subsidiary PTY. --- ports/zephyr-cp/supervisor/port.c | 45 ++++++++++ ports/zephyr-cp/tests/__init__.py | 23 ++++- ports/zephyr-cp/tests/bsim/conftest.py | 18 ++-- .../zephyr-cp/tests/bsim/test_bsim_basics.py | 11 +-- .../tests/bsim/test_bsim_ble_advertising.py | 59 +++++------- .../tests/bsim/test_bsim_ble_name.py | 8 +- .../tests/bsim/test_bsim_ble_scan.py | 90 ++++++------------- ports/zephyr-cp/tests/conftest.py | 29 +++++- ports/zephyr-cp/tests/test_basics.py | 1 + ports/zephyr-cp/zephyr-config/west.yml | 4 + 10 files changed, 154 insertions(+), 134 deletions(-) diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index a473e1664d8a2..08a84043e8eeb 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -17,6 +17,15 @@ #include #include +#if defined(CONFIG_ARCH_POSIX) +#include +#include + +#include "cmdline.h" +#include "posix_board_if.h" +#include "posix_native_task.h" +#endif + #include "lib/tlsf/tlsf.h" #include @@ -37,6 +46,32 @@ static K_EVENT_DEFINE(main_needed); static struct k_timer tick_timer; +#if defined(CONFIG_ARCH_POSIX) +// Number of VM runs before exiting. +// <= 0 means run forever. +// INT32_MAX means option was not provided. +static int32_t native_sim_vm_runs = INT32_MAX; +static uint32_t native_sim_reset_port_count = 0; + +static struct args_struct_t native_sim_reset_port_args[] = { + { + .option = "vm-runs", + .name = "count", + .type = 'i', + .dest = &native_sim_vm_runs, + .descript = "Exit native_sim after this many VM runs. " + "Example: --vm-runs=2" + }, + ARG_TABLE_ENDMARKER +}; + +static void native_sim_register_cmdline_opts(void) { + native_add_command_line_opts(native_sim_reset_port_args); +} + +NATIVE_TASK(native_sim_register_cmdline_opts, PRE_BOOT_1, 0); +#endif + static void _tick_function(struct k_timer *timer_id) { supervisor_tick(); } @@ -61,6 +96,16 @@ void reset_port(void) { #if CIRCUITPY_BLEIO bleio_reset(); #endif + + #if defined(CONFIG_ARCH_POSIX) + native_sim_reset_port_count++; + if (native_sim_vm_runs != INT32_MAX && + native_sim_vm_runs > 0 && + native_sim_reset_port_count >= (uint32_t)(native_sim_vm_runs + 1)) { + printk("posix: exiting after %d VM runs\n", native_sim_vm_runs); + posix_exit(0); + } + #endif } void reset_to_bootloader(void) { diff --git a/ports/zephyr-cp/tests/__init__.py b/ports/zephyr-cp/tests/__init__.py index a560b2c40c1f2..18e596e8e7046 100644 --- a/ports/zephyr-cp/tests/__init__.py +++ b/ports/zephyr-cp/tests/__init__.py @@ -1,6 +1,7 @@ import serial import subprocess import threading +import time class StdSerial: @@ -25,6 +26,12 @@ def close(self): self.stdin.close() self.stdout.close() + @property + def in_waiting(self): + if self.stdout is None: + return 0 + return len(self.stdout.peek()) + class SerialSaver: """Capture serial output in a background thread so output isn't missed.""" @@ -58,6 +65,13 @@ def _reader_loop(self): with self._cv: self.all_output += text self._cv.notify_all() + in_waiting = 0 + try: + in_waiting = self.serial.in_waiting + except OSError: + pass + if in_waiting > 0: + self.all_output += self.serial.read().decode("utf-8", errors="replace") def wait_for(self, text, timeout=10): with self._cv: @@ -79,11 +93,11 @@ def close(self): return self._stop.set() + self._reader.join(timeout=1.0) try: self.serial.close() except Exception: pass - self._reader.join(timeout=1.0) self.serial = None def write(self, text): @@ -131,6 +145,7 @@ def shutdown(self): self.debug_serial.close() def wait_until_done(self): - self._proc.wait(timeout=self._timeout) - self.serial.close() - self.debug_serial.close() + start_time = time.monotonic() + while self._proc.poll() is None and time.monotonic() - start_time < self._timeout: + time.sleep(0.01) + self.shutdown() diff --git a/ports/zephyr-cp/tests/bsim/conftest.py b/ports/zephyr-cp/tests/bsim/conftest.py index 505ff77bf7ec0..b7a66346ce269 100644 --- a/ports/zephyr-cp/tests/bsim/conftest.py +++ b/ports/zephyr-cp/tests/bsim/conftest.py @@ -86,7 +86,7 @@ def shutdown(self) -> None: @pytest.fixture def bsim_phy(request, bsim_phy_binary, native_sim_env, sim_id): duration_marker = request.node.get_closest_marker("duration") - sim_length = float(duration_marker.args[0]) if duration_marker else 20.0 + duration = float(duration_marker.args[0]) if duration_marker else 20.0 devices = 1 if "circuitpython2" in request.fixturenames or "zephyr_sample" in request.fixturenames: @@ -97,12 +97,7 @@ def bsim_phy(request, bsim_phy_binary, native_sim_env, sim_id): sample_device_id = int(sample_marker.kwargs.get("device_id", 1)) devices = max(devices, sample_device_id + 1) - bsim_marker = request.node.get_closest_marker("bsim") - if bsim_marker is not None: - devices = int(bsim_marker.kwargs.get("devices", devices)) - sim_length = float(bsim_marker.kwargs.get("sim_length", sim_length)) - - sim_length_us = int(sim_length * 1e6) + sim_length_us = int(duration * 1e6) cmd = [ "stdbuf", "-oL", @@ -136,7 +131,7 @@ def bsim_phy(request, bsim_phy_binary, native_sim_env, sim_id): raise RuntimeError("bsim PHY exited immediately") # Assume bsim is running - phy = BsimPhyInstance(proc, phy_output, timeout=sim_length) + phy = BsimPhyInstance(proc, phy_output, timeout=duration) yield phy phy.shutdown() @@ -195,7 +190,12 @@ def zephyr_sample(request, bsim_phy, native_sim_env, sim_id): if not binary.exists(): pytest.skip(f"Zephyr sample binary not found: {binary}") - cmd = [str(binary), f"-s={sim_id}", f"-d={device_id}"] + cmd = [ + str(binary), + f"-s={sim_id}", + f"-d={device_id}", + "-disconnect_on_exit=1", + ] logger.info("Running: %s", " ".join(cmd)) proc = subprocess.Popen( cmd, diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_basics.py b/ports/zephyr-cp/tests/bsim/test_bsim_basics.py index 7f1ad036f6f5f..477292ddd5465 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_basics.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_basics.py @@ -14,16 +14,13 @@ @pytest.mark.circuitpy_drive({"code.py": BSIM_CODE}) @pytest.mark.circuitpy_drive({"code.py": BSIM_CODE}) +@pytest.mark.duration(3) def test_bsim_dual_instance_connect(bsim_phy, circuitpython1, circuitpython2): """Run two bsim instances on the same sim id and verify UART output.""" - print("in the test") - # Wait for both devices to produce their expected output before - # tearing down the simulation. - circuitpython1.serial.wait_for("bsim ready") - circuitpython2.serial.wait_for("bsim ready") - - bsim_phy.finish_sim() + # Wait for both devices to complete before checking output. + circuitpython1.wait_until_done() + circuitpython2.wait_until_done() output0 = circuitpython1.serial.all_output output1 = circuitpython2.serial.all_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py index 4822d42b9bbdd..35d85b416d627 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py @@ -4,7 +4,6 @@ """BLE advertising tests for nrf5340bsim.""" import logging -import time import pytest @@ -39,8 +38,9 @@ print("adv run start") adapter.start_advertising(advertisement, connectable=False) print("adv running") -while True: - time.sleep(0.2) +time.sleep(10) +adapter.stop_advertising() +print("adv run done") """ @@ -50,54 +50,34 @@ def test_bsim_advertise_and_scan(bsim_phy, circuitpython, zephyr_sample): """Advertise from CircuitPython and verify Zephyr observer sees traffic.""" observer = zephyr_sample - start_time = time.time() - while time.time() - start_time < 12.0: - observer_output = observer.serial.all_output - adv_ready = "adv started" in circuitpython.serial.all_output - if ( - "Device found:" in observer_output - and "AD data len 10" in observer_output - and adv_ready - ): - break - time.sleep(0.05) + circuitpython.wait_until_done() + cp_output = circuitpython.serial.all_output observer_output = observer.serial.all_output - assert "adv start" in circuitpython.serial.all_output - assert "adv started" in circuitpython.serial.all_output + assert "adv start" in cp_output + assert "adv started" in cp_output + assert "adv stop" in cp_output assert "Device found:" in observer_output assert "AD data len 10" in observer_output -@pytest.mark.zephyr_sample("bluetooth/observer", timeout=20.0) +@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.code_py_runs(2) +@pytest.mark.duration(25) @pytest.mark.circuitpy_drive({"code.py": BSIM_ADV_INTERRUPT_RELOAD_CODE}) def test_bsim_advertise_ctrl_c_reload(bsim_phy, circuitpython, zephyr_sample): """Ensure advertising resumes after Ctrl-C and a reload.""" observer = zephyr_sample - start_time = time.time() - sent_ctrl_c = False - sent_reload = False - observer_count_before = 0 - - while time.time() - start_time < 22.0: - cp_output = circuitpython.serial.all_output - observer_output = observer.serial.all_output - device_found_count = observer_output.count("Device found:") - - if not sent_ctrl_c and "adv running" in cp_output and device_found_count > 0: - circuitpython.serial.write("\x03") - sent_ctrl_c = True - observer_count_before = device_found_count - - if sent_ctrl_c and not sent_reload and "KeyboardInterrupt" in cp_output: - circuitpython.serial.write("\x04") - sent_reload = True + circuitpython.serial.wait_for("adv running") + observer.serial.wait_for("Device found:") + observer_count_before = observer.serial.all_output.count("Device found:") - if sent_reload and cp_output.count("adv running") >= 2: - break + circuitpython.serial.write("\x03") + circuitpython.serial.wait_for("KeyboardInterrupt") - time.sleep(0.05) + circuitpython.serial.write("\x04") + circuitpython.wait_until_done() cp_output = circuitpython.serial.all_output observer_output = observer.serial.all_output @@ -107,5 +87,6 @@ def test_bsim_advertise_ctrl_c_reload(bsim_phy, circuitpython, zephyr_sample): assert "adv run start" in cp_output assert "KeyboardInterrupt" in cp_output assert cp_output.count("adv running") >= 2 - assert observer_output.count("Device found:") >= observer_count_before + assert cp_output.count("adv run done") >= 1 + assert observer_output.count("Device found:") >= observer_count_before + 1 assert "Already advertising" not in cp_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py index a7f76ca72b7b4..69435d3825624 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py @@ -3,8 +3,6 @@ """BLE name tests for nrf5340bsim.""" -import time - import pytest pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") @@ -22,10 +20,6 @@ @pytest.mark.circuitpy_drive({"code.py": BSIM_NAME_CODE}) def test_bsim_set_name(bsim_phy, circuitpython): """Set the BLE name and read it back on bsim.""" - start_time = time.time() - while time.time() - start_time < 3.0: - if "name CPNAME" in circuitpython.serial.all_output: - break - time.sleep(0.05) + circuitpython.wait_until_done() assert "name CPNAME" in circuitpython.serial.all_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py index 8836669801676..95888ecccb335 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py @@ -3,8 +3,6 @@ """BLE scanning tests for nrf5340bsim.""" -import time - import pytest pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") @@ -32,16 +30,8 @@ adapter = _bleio.adapter print("scan run start") -for _ in range(10): - try: - scan = adapter.start_scan(timeout=4.0, active=True) - break - except OSError: - time.sleep(0.1) -else: - raise RuntimeError("scan start failed") found = False -for entry in scan: +for entry in adapter.start_scan(active=True): if b"zephyrproject" in entry.advertisement_bytes: print("found beacon run") found = True @@ -57,16 +47,8 @@ adapter = _bleio.adapter print("scan run start") -for _ in range(10): - try: - scan = adapter.start_scan(timeout=4.0, active=True) - break - except OSError: - time.sleep(0.1) -else: - raise RuntimeError("scan start failed") found = False -for entry in scan: +for entry in adapter.start_scan(active=True): if b"zephyrproject" in entry.advertisement_bytes: print("found beacon run") found = True @@ -81,71 +63,49 @@ def test_bsim_scan_zephyr_beacon(bsim_phy, circuitpython, zephyr_sample): """Scan for Zephyr beacon sample advertisement using bsim.""" _ = zephyr_sample - start_time = time.time() - while time.time() - start_time < 6.0: - if "found beacon" in circuitpython.serial.all_output: - break - time.sleep(0.05) + circuitpython.wait_until_done() - assert "scan start" in circuitpython.serial.all_output - assert "found beacon" in circuitpython.serial.all_output + output = circuitpython.serial.all_output + assert "scan start" in output + assert "found beacon" in output + assert "scan done True" in output -@pytest.mark.zephyr_sample("bluetooth/beacon", timeout=12.0) +@pytest.mark.zephyr_sample("bluetooth/beacon") +@pytest.mark.code_py_runs(2) +@pytest.mark.duration(4) @pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_CODE}) def test_bsim_scan_zephyr_beacon_reload(bsim_phy, circuitpython, zephyr_sample): """Scan for Zephyr beacon, soft reload, and scan again.""" _ = zephyr_sample - start_time = time.time() - sent_reload = False - while time.time() - start_time < 12.0: - output = circuitpython.serial.all_output - - if ( - not sent_reload - and "scan run done" in output - and "Press any key to enter the REPL" in output - ): - time.sleep(0.2) - circuitpython.serial.write("\x04") - sent_reload = True + circuitpython.serial.wait_for("scan run done") + circuitpython.serial.wait_for("Press any key to enter the REPL") + circuitpython.serial.write("\x04") - if sent_reload and output.count("scan run done") >= 2: - break - time.sleep(0.05) + circuitpython.wait_until_done() output = circuitpython.serial.all_output - assert "scan run start" in output + assert output.count("scan run start") >= 2 assert output.count("found beacon run") >= 2 - assert output.count("scan run done") >= 2 + assert output.count("scan run done True") >= 2 -@pytest.mark.zephyr_sample("bluetooth/beacon", timeout=12.0) +@pytest.mark.zephyr_sample("bluetooth/beacon") +@pytest.mark.code_py_runs(2) +@pytest.mark.duration(4) @pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_NO_STOP_CODE}) def test_bsim_scan_zephyr_beacon_reload_no_stop(bsim_phy, circuitpython, zephyr_sample): """Scan for Zephyr beacon without explicit stop, soft reload, and scan again.""" _ = zephyr_sample - start_time = time.time() - sent_reload = False - while time.time() - start_time < 12.0: - output = circuitpython.serial.all_output - - if ( - not sent_reload - and "scan run done" in output - and "Press any key to enter the REPL" in output - ): - time.sleep(0.2) - circuitpython.serial.write("\x04") - sent_reload = True + circuitpython.serial.wait_for("scan run done") + circuitpython.serial.wait_for("Press any key to enter the REPL") + circuitpython.serial.write("\x04") - if sent_reload and output.count("scan run done") >= 2: - break - time.sleep(0.05) + circuitpython.wait_until_done() output = circuitpython.serial.all_output - assert "scan run start" in output + assert output.count("scan run start") >= 2 assert output.count("found beacon run") >= 2 - assert output.count("scan run done") >= 2 + assert output.count("scan run done True") >= 2 diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py index f6545a46b5563..0bc296cf43feb 100644 --- a/ports/zephyr-cp/tests/conftest.py +++ b/ports/zephyr-cp/tests/conftest.py @@ -36,7 +36,11 @@ def pytest_configure(config): ) config.addinivalue_line( "markers", - "bsim(devices=1, sim_length=20.0): configure bsim PHY device count and simulation length", + "duration(seconds): native_sim timeout and bsim PHY simulation duration", + ) + config.addinivalue_line( + "markers", + "code_py_runs(count): stop native_sim after count code.py runs (default: 1)", ) @@ -179,12 +183,31 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp timeout = 10 else: timeout = marker.args[0] + + runs_marker = request.node.get_closest_marker("code_py_runs") + if runs_marker is None: + code_py_runs = 1 + else: + code_py_runs = int(runs_marker.args[0]) + if "bsim" in board: cmd = [str(native_sim_binary), f"--flash_app={flash}"] - cmd.extend((f"-s={sim_id}", f"-d={i}", "-uart0_pty", "-uart_pty_wait")) + if instance_count > 1: + cmd.append("-disconnect_on_exit=1") + cmd.extend( + ( + f"-s={sim_id}", + f"-d={i}", + "-uart0_pty", + "-uart0_pty_wait_for_readers", + "-uart_pty_wait", + f"--vm-runs={code_py_runs + 1}", + ) + ) else: cmd = [str(native_sim_binary), f"--flash={flash}"] - cmd.extend(("-no-rt", "-wait_uart", f"-stop_at={timeout}")) + # native_sim vm-runs includes the boot VM setup run. + cmd.extend(("-no-rt", "-wait_uart", f"--vm-runs={code_py_runs + 1}")) marker = request.node.get_closest_marker("disable_i2c_devices") if marker and len(marker.args) > 0: diff --git a/ports/zephyr-cp/tests/test_basics.py b/ports/zephyr-cp/tests/test_basics.py index 96f41d39795f0..8ed9cc08a590d 100644 --- a/ports/zephyr-cp/tests/test_basics.py +++ b/ports/zephyr-cp/tests/test_basics.py @@ -206,6 +206,7 @@ def test_ctrl_c_interrupt(circuitpython): @pytest.mark.circuitpy_drive({"code.py": RELOAD_CODE}) +@pytest.mark.code_py_runs(2) def test_ctrl_d_soft_reload(circuitpython): """Test sending Ctrl+D (0x04) to trigger soft reload.""" circuitpython.serial.wait_for("first run") diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index 159467ae7e601..a8b05fabd05cc 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -2,6 +2,10 @@ manifest: group-filter: - +babblesim projects: + - name: nrf_hw_models + url: https://github.com/tannewt/ext_nRF_hw_models + revision: 24de78c485dce1a6048f8ae1c69a8d70c93b8cdd + path: modules/bsim_hw_models/nrf_hw_models - name: zephyr url: https://github.com/adafruit/zephyr revision: 3c5a3a72daa3ca6462cd8bc9c8c7c6a41fbf3b2e From 1303767f6d673259880a6dc357b052478b7969b0 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 23 Feb 2026 16:43:30 -0800 Subject: [PATCH 357/517] Extend timeout --- ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py index 95888ecccb335..3a022944e0068 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py @@ -93,7 +93,7 @@ def test_bsim_scan_zephyr_beacon_reload(bsim_phy, circuitpython, zephyr_sample): @pytest.mark.zephyr_sample("bluetooth/beacon") @pytest.mark.code_py_runs(2) -@pytest.mark.duration(4) +@pytest.mark.duration(8) @pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_NO_STOP_CODE}) def test_bsim_scan_zephyr_beacon_reload_no_stop(bsim_phy, circuitpython, zephyr_sample): """Scan for Zephyr beacon without explicit stop, soft reload, and scan again.""" From 2428afaf8ce05154f6f946a6125a4ecb30a4ae52 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 24 Feb 2026 18:17:47 +0100 Subject: [PATCH 358/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 6 ++++++ locale/el.po | 6 ++++++ locale/hi.po | 6 ++++++ locale/ko.po | 6 ++++++ locale/ru.po | 6 ++++++ locale/tr.po | 6 ++++++ 6 files changed, 36 insertions(+) diff --git a/locale/cs.po b/locale/cs.po index 24dc429275341..319fe64bc7522 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -1351,6 +1351,10 @@ msgstr "Chybná MAC adresa" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "Neplatný argument" @@ -3830,6 +3834,7 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "nenulový timeout musí být > 0.01" @@ -4313,6 +4318,7 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "timeout překročil maximální podporovanou hodnotu" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "timeout musí být < 655.35 s" diff --git a/locale/el.po b/locale/el.po index 016fb3a381e03..da44530ba7e17 100644 --- a/locale/el.po +++ b/locale/el.po @@ -1357,6 +1357,10 @@ msgstr "" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "" @@ -3829,6 +3833,7 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4312,6 +4317,7 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index be00c7cf1c906..f0a35d43e799f 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -1333,6 +1333,10 @@ msgstr "" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "" @@ -3803,6 +3807,7 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4286,6 +4291,7 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 8e96781ed270b..31ec5a50294ba 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -1384,6 +1384,10 @@ msgstr "잘못된 MAC 주소" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "잘못된 인수" @@ -3877,6 +3881,7 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4360,6 +4365,7 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" diff --git a/locale/ru.po b/locale/ru.po index cff7a97b39609..3efafa9e46438 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -1372,6 +1372,10 @@ msgstr "Неверный MAC-адрес" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "Недопустимый аргумент" @@ -3892,6 +3896,7 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "Ненулевое время ожидания должно быть > 0,01" @@ -4376,6 +4381,7 @@ msgstr "" "Продолжительность таймаута превысила максимальное поддерживаемое значение" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "таймаут должен быть < 655.35 сек" diff --git a/locale/tr.po b/locale/tr.po index 0449d8d1261e8..737eaff9b558c 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -1351,6 +1351,10 @@ msgstr "Geçersiz MAC adresi" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "Geçersiz argüman" @@ -3825,6 +3829,7 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4308,6 +4313,7 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" From 83510b114cd3d5d7d1a53781fb6170f4194a356a Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Feb 2026 12:02:10 -0800 Subject: [PATCH 359/517] Add input testing based on perfetto traces Tests digital input and new rotaryio. --- ports/zephyr-cp/AGENTS.md | 1 + ports/zephyr-cp/CMakeLists.txt | 4 + ports/zephyr-cp/Makefile | 4 + .../autogen_board_info.toml | 115 +++++ .../circuitpython.toml | 3 + .../boards/adafruit_feather_nrf52840_uf2.conf | 9 + .../adafruit_feather_nrf52840_uf2.overlay | 24 + ports/zephyr-cp/boards/board_aliases.cmake | 1 + .../native/native_sim/autogen_board_info.toml | 2 +- .../nrf5340bsim/autogen_board_info.toml | 2 +- .../nordic/nrf5340dk/autogen_board_info.toml | 2 +- .../nordic/nrf54h20dk/autogen_board_info.toml | 2 +- .../nordic/nrf54l15dk/autogen_board_info.toml | 2 +- .../nordic/nrf7002dk/autogen_board_info.toml | 2 +- .../nxp/frdm_mcxn947/autogen_board_info.toml | 2 +- .../nxp/frdm_rw612/autogen_board_info.toml | 2 +- .../mimxrt1170_evk/autogen_board_info.toml | 2 +- .../da14695_dk_usb/autogen_board_info.toml | 2 +- .../renesas/ek_ra6m5/autogen_board_info.toml | 2 +- .../renesas/ek_ra8d1/autogen_board_info.toml | 2 +- .../nucleo_n657x0_q/autogen_board_info.toml | 2 +- .../nucleo_u575zi_q/autogen_board_info.toml | 2 +- .../st/stm32h7b3i_dk/autogen_board_info.toml | 2 +- .../stm32wba65i_dk1/autogen_board_info.toml | 2 +- .../common-hal/rotaryio/IncrementalEncoder.c | 129 +++++ .../common-hal/rotaryio/IncrementalEncoder.h | 31 ++ .../zephyr-cp/common-hal/rotaryio/__init__.c | 7 + ports/zephyr-cp/cptools/build_all_boards.py | 446 ++++++++++++++++-- .../zephyr-cp/cptools/build_circuitpython.py | 8 +- ports/zephyr-cp/cptools/zephyr2cp.py | 3 + ports/zephyr-cp/tests/conftest.py | 22 +- ports/zephyr-cp/tests/docs/babblesim.md | 12 + ports/zephyr-cp/tests/perfetto_input_trace.py | 128 +++++ ports/zephyr-cp/tests/test_basics.py | 107 +---- ports/zephyr-cp/tests/test_digitalio.py | 171 +++++++ ports/zephyr-cp/tests/test_rotaryio.py | 132 ++++++ ports/zephyr-cp/zephyr-config/west.yml | 2 +- 37 files changed, 1215 insertions(+), 176 deletions(-) create mode 100644 ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf create mode 100644 ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay create mode 100644 ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.c create mode 100644 ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.h create mode 100644 ports/zephyr-cp/common-hal/rotaryio/__init__.c create mode 100644 ports/zephyr-cp/tests/perfetto_input_trace.py create mode 100644 ports/zephyr-cp/tests/test_digitalio.py create mode 100644 ports/zephyr-cp/tests/test_rotaryio.py diff --git a/ports/zephyr-cp/AGENTS.md b/ports/zephyr-cp/AGENTS.md index a2413e64e428f..47813886804e9 100644 --- a/ports/zephyr-cp/AGENTS.md +++ b/ports/zephyr-cp/AGENTS.md @@ -3,3 +3,4 @@ - The files (not folders) in `boards/` directory are used by Zephyr. - To flash it on a board do `make BOARD=_ flash`. - Zephyr board docs are at `zephyr/boards//`. +- Run zephyr-cp tests with `make test`. diff --git a/ports/zephyr-cp/CMakeLists.txt b/ports/zephyr-cp/CMakeLists.txt index 0ba4a3c48b3de..9d115f1e17688 100644 --- a/ports/zephyr-cp/CMakeLists.txt +++ b/ports/zephyr-cp/CMakeLists.txt @@ -10,6 +10,10 @@ if(CONFIG_BOARD_NATIVE_SIM) target_sources(app PRIVATE native_sim_i2c_emul_control.c) endif() +if(CONFIG_TRACING_PERFETTO) + zephyr_include_directories(${ZEPHYR_BINARY_DIR}/subsys/tracing/perfetto/proto) +endif() + # From: https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/application_development/external_lib/CMakeLists.txt # The external static library that we are linking with does not know # how to build for this platform so we export all the flags used in diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index c4feead23b9b5..24c6bf08da653 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -8,6 +8,7 @@ BUILD ?= build-$(BOARD) TRANSLATION ?= en_US +.DEFAULT_GOAL := $(BUILD)/zephyr-cp/zephyr/zephyr.elf .PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug run clean menuconfig all clean-all test fetch-port-submodules @@ -24,6 +25,9 @@ $(BUILD)/firmware.hex: $(BUILD)/zephyr-cp/zephyr/zephyr.elf $(BUILD)/firmware.exe: $(BUILD)/zephyr-cp/zephyr/zephyr.elf cp $(BUILD)/zephyr-cp/zephyr/zephyr.exe $@ +$(BUILD)/firmware.uf2: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + cp $(BUILD)/zephyr-cp/zephyr/zephyr.uf2 $@ + flash: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west flash -d $(BUILD) diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..db83e1b74481d --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -0,0 +1,115 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Adafruit Industries LLC Feather nRF52840 (Express, Sense)" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..e1a16cb74aa46 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml @@ -0,0 +1,3 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] +USB_VID=0x239A +USB_PID=0x802A diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf new file mode 100644 index 0000000000000..c30c8c19fcddb --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf @@ -0,0 +1,9 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n +CONFIG_BOARD_REQUIRES_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay new file mode 100644 index 0000000000000..a61cbf2047de2 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay @@ -0,0 +1,24 @@ +/ { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + zephyr,bt-mon-uart = &uart0; + zephyr,bt-c2h-uart = &uart0; + }; +}; + +&zephyr_udc0 { + /delete-node/ board_cdc_acm_uart; +}; + + +&gd25q16 { + /delete-node/ partitions; +}; + +&uart0 { + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index ddf1627a924db..ee46145a1127f 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -1,4 +1,5 @@ set(pca10056_BOARD_ALIAS nrf52840dk/nrf52840) +set(adafruit_feather_nrf52840_zephyr_BOARD_ALIAS adafruit_feather_nrf52840/nrf52840/uf2) set(renesas_ek_ra6m5_BOARD_ALIAS ek_ra6m5) set(renesas_ek_ra8d1_BOARD_ALIAS ek_ra8d1) set(renesas_da14695_dk_usb_BOARD_ALIAS da14695_dk_usb) diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 73897f7162082..447a6ab2d5bea 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 9ddbb2153fb63..1e69862044a85 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 52beeda076fc3..c2d1b48f94294 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index 2759dfb89c1c7..0068cb9601da7 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 6df832f607b2c..df63cff8f7c03 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index d713552d87be7..6a60bfd84f128 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 233796fc6f443..c8890700681f6 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index f7ad0289cc75e..7413c21be9a89 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index 4926b5c9a6cce..f5262b2823646 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index 77d93aa10f095..8f054774d556c 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 2ea2cea90b775..e9fbec0cdce1f 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 3be48a8b72c3f..dd3d0869625ca 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index 4b9c1053f2289..e044cef97ca32 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index ec78a62f066e2..53deb5b22e9b5 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index 24e44662e4016..da3aa7e30b794 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index e26084cc43ee3..c943b80affe9b 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -82,7 +82,7 @@ rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false sdcardio = true # Zephyr board has busio sdioio = false diff --git a/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.c b/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.c new file mode 100644 index 0000000000000..d36b571535afe --- /dev/null +++ b/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.c @@ -0,0 +1,129 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// +// SPDX-License-Identifier: MIT + +#include "common-hal/rotaryio/IncrementalEncoder.h" +#include "shared-bindings/rotaryio/IncrementalEncoder.h" +#include "shared-module/rotaryio/IncrementalEncoder.h" + +#include "bindings/zephyr_kernel/__init__.h" +#include "py/runtime.h" + +#include +#include +#include +#include + +static void incrementalencoder_gpio_callback(const struct device *port, + struct gpio_callback *cb, gpio_port_pins_t pins) { + (void)port; + (void)pins; + rotaryio_incrementalencoder_gpio_callback_t *callback = + CONTAINER_OF(cb, rotaryio_incrementalencoder_gpio_callback_t, callback); + rotaryio_incrementalencoder_obj_t *self = callback->encoder; + if (self == NULL || self->pin_a == NULL) { + return; + } + + int a = gpio_pin_get(self->pin_a->port, self->pin_a->number); + int b = gpio_pin_get(self->pin_b->port, self->pin_b->number); + if (a < 0 || b < 0) { + return; + } + uint8_t new_state = ((uint8_t)a << 1) | (uint8_t)b; + shared_module_softencoder_state_update(self, new_state); +} + +void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, + const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + + self->pin_a = pin_a; + self->pin_b = pin_b; + self->divisor = 4; + + if (!device_is_ready(pin_a->port) || !device_is_ready(pin_b->port)) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(-ENODEV); + } + + int result = gpio_pin_configure(pin_a->port, pin_a->number, GPIO_INPUT | GPIO_PULL_UP); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + result = gpio_pin_configure(pin_b->port, pin_b->number, GPIO_INPUT | GPIO_PULL_UP); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + self->callback_a.encoder = self; + gpio_init_callback(&self->callback_a.callback, incrementalencoder_gpio_callback, + BIT(pin_a->number)); + result = gpio_add_callback(pin_a->port, &self->callback_a.callback); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + self->callback_b.encoder = self; + gpio_init_callback(&self->callback_b.callback, incrementalencoder_gpio_callback, + BIT(pin_b->number)); + result = gpio_add_callback(pin_b->port, &self->callback_b.callback); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + result = gpio_pin_interrupt_configure(pin_a->port, pin_a->number, GPIO_INT_EDGE_BOTH); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + result = gpio_pin_interrupt_configure(pin_b->port, pin_b->number, GPIO_INT_EDGE_BOTH); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + int a = gpio_pin_get(pin_a->port, pin_a->number); + int b = gpio_pin_get(pin_b->port, pin_b->number); + uint8_t quiescent_state = ((uint8_t)(a > 0) << 1) | (uint8_t)(b > 0); + shared_module_softencoder_state_init(self, quiescent_state); + + claim_pin(pin_a); + claim_pin(pin_b); +} + +bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) { + return self->pin_a == NULL; +} + +void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self) { + if (common_hal_rotaryio_incrementalencoder_deinited(self)) { + return; + } + + // Best-effort cleanup. During failed construct(), some of these may not be + // initialized yet. Ignore cleanup errors. + gpio_pin_interrupt_configure(self->pin_a->port, self->pin_a->number, GPIO_INT_DISABLE); + gpio_pin_interrupt_configure(self->pin_b->port, self->pin_b->number, GPIO_INT_DISABLE); + gpio_remove_callback(self->pin_a->port, &self->callback_a.callback); + gpio_remove_callback(self->pin_b->port, &self->callback_b.callback); + + reset_pin(self->pin_a); + reset_pin(self->pin_b); + + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { + self->pin_a = NULL; + self->pin_b = NULL; +} diff --git a/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.h b/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.h new file mode 100644 index 0000000000000..a0d2bb392e264 --- /dev/null +++ b/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.h @@ -0,0 +1,31 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +typedef struct rotaryio_incrementalencoder_obj rotaryio_incrementalencoder_obj_t; + +typedef struct { + struct gpio_callback callback; + rotaryio_incrementalencoder_obj_t *encoder; +} rotaryio_incrementalencoder_gpio_callback_t; + +struct rotaryio_incrementalencoder_obj { + mp_obj_base_t base; + const mcu_pin_obj_t *pin_a; + const mcu_pin_obj_t *pin_b; + rotaryio_incrementalencoder_gpio_callback_t callback_a; + rotaryio_incrementalencoder_gpio_callback_t callback_b; + uint8_t state; // + int8_t sub_count; // count intermediate transitions between detents + int8_t divisor; // Number of quadrature edges required per count + mp_int_t position; +}; diff --git a/ports/zephyr-cp/common-hal/rotaryio/__init__.c b/ports/zephyr-cp/common-hal/rotaryio/__init__.c new file mode 100644 index 0000000000000..67cae26a8b7fe --- /dev/null +++ b/ports/zephyr-cp/common-hal/rotaryio/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// +// SPDX-License-Identifier: MIT + +// No rotaryio module functions. diff --git a/ports/zephyr-cp/cptools/build_all_boards.py b/ports/zephyr-cp/cptools/build_all_boards.py index 8ad6bd05ea9c7..da9f45ead1e71 100755 --- a/ports/zephyr-cp/cptools/build_all_boards.py +++ b/ports/zephyr-cp/cptools/build_all_boards.py @@ -3,17 +3,113 @@ Build all CircuitPython boards for the Zephyr port. This script discovers all boards by finding circuitpython.toml files -and builds each one sequentially, passing through the jobserver for -parallelism within each build. +and builds them in parallel while sharing a single jobserver across +all builds. + +This is agent generated and works. Don't bother reading too closely because it +is just a tool for us. """ import argparse +import concurrent.futures +import os import pathlib +import shlex import subprocess import sys import time +class Jobserver: + def __init__(self, read_fd, write_fd, jobs=None, owns_fds=False): + self.read_fd = read_fd + self.write_fd = write_fd + self.jobs = jobs + self.owns_fds = owns_fds + + def acquire(self): + while True: + try: + os.read(self.read_fd, 1) + return + except InterruptedError: + continue + + def release(self): + while True: + try: + os.write(self.write_fd, b"+") + return + except InterruptedError: + continue + + def pass_fds(self): + return (self.read_fd, self.write_fd) + + def close(self): + if self.owns_fds: + os.close(self.read_fd) + os.close(self.write_fd) + + +def _parse_makeflags_jobserver(makeflags): + jobserver_auth = None + jobs = None + + for token in shlex.split(makeflags): + if token == "-j" or token == "--jobs": + continue + if token.startswith("-j") and token != "-j": + try: + jobs = int(token[2:]) + except ValueError: + pass + elif token.startswith("--jobs="): + try: + jobs = int(token.split("=", 1)[1]) + except ValueError: + pass + elif token.startswith("--jobserver-auth=") or token.startswith("--jobserver-fds="): + jobserver_auth = token.split("=", 1)[1] + + if not jobserver_auth: + return None, jobs, False + + if jobserver_auth.startswith("fifo:"): + fifo_path = jobserver_auth[len("fifo:") :] + read_fd = os.open(fifo_path, os.O_RDONLY) + write_fd = os.open(fifo_path, os.O_WRONLY) + os.set_inheritable(read_fd, True) + os.set_inheritable(write_fd, True) + return (read_fd, write_fd), jobs, True + + if "," in jobserver_auth: + read_fd, write_fd = jobserver_auth.split(",", 1) + return (int(read_fd), int(write_fd)), jobs, False + + return None, jobs, False + + +def _create_jobserver(jobs): + read_fd, write_fd = os.pipe() + os.set_inheritable(read_fd, True) + os.set_inheritable(write_fd, True) + for _ in range(jobs): + os.write(write_fd, b"+") + return Jobserver(read_fd, write_fd, jobs=jobs, owns_fds=True) + + +def _jobserver_from_env(): + makeflags = os.environ.get("MAKEFLAGS", "") + fds, jobs, owns_fds = _parse_makeflags_jobserver(makeflags) + if not fds: + return None, jobs + read_fd, write_fd = fds + os.set_inheritable(read_fd, True) + os.set_inheritable(write_fd, True) + return Jobserver(read_fd, write_fd, jobs=jobs, owns_fds=owns_fds), jobs + + def discover_boards(port_dir): """ Discover all boards by finding circuitpython.toml files. @@ -35,7 +131,15 @@ def discover_boards(port_dir): return sorted(boards) -def build_board(port_dir, vendor, board, extra_args=None): +def build_board( + port_dir, + vendor, + board, + extra_args=None, + jobserver=None, + env=None, + log_dir=None, +): """ Build a single board using make. @@ -44,12 +148,16 @@ def build_board(port_dir, vendor, board, extra_args=None): vendor: Board vendor name board: Board name extra_args: Additional arguments to pass to make + jobserver: Jobserver instance to limit parallel builds + env: Environment variables for the subprocess + log_dir: Directory to write build logs Returns: - (success: bool, elapsed_time: float) + (success: bool, elapsed_time: float, output: str, log_path: pathlib.Path) """ board_id = f"{vendor}_{board}" start_time = time.time() + log_path = None cmd = ["make", f"BOARD={board_id}"] @@ -57,25 +165,240 @@ def build_board(port_dir, vendor, board, extra_args=None): if extra_args: cmd.extend(extra_args) + if jobserver: + jobserver.acquire() + try: - subprocess.run( + result = subprocess.run( cmd, cwd=port_dir, - check=True, - # Inherit stdin to pass through jobserver file descriptors + # Inherit stdin alongside jobserver file descriptors stdin=sys.stdin, - # Show output in real-time - stdout=None, - stderr=None, - capture_output=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + env=env, + pass_fds=jobserver.pass_fds() if jobserver else (), ) elapsed = time.time() - start_time - return True, elapsed - except subprocess.CalledProcessError: - elapsed = time.time() - start_time - return False, elapsed + output = result.stdout or "" + if log_dir: + log_path = log_dir / f"{board_id}.log" + log_path.write_text(output) + return result.returncode == 0, elapsed, output, log_path except KeyboardInterrupt: raise + finally: + if jobserver: + jobserver.release() + + +def _format_status(status): + state = status["state"] + elapsed = status.get("elapsed") + if state == "queued": + return "QUEUED" + if state == "running": + return f"RUNNING {elapsed:.1f}s" + if state == "success": + return f"SUCCESS {elapsed:.1f}s" + if state == "failed": + return f"FAILED {elapsed:.1f}s" + if state == "skipped": + return "SKIPPED" + return state.upper() + + +def _build_status_table(boards, statuses, start_time, stop_submitting): + from rich.table import Table + from rich.text import Text + + elapsed = time.time() - start_time + title = f"Building {len(boards)} boards | Elapsed: {elapsed:.1f}s" + if stop_submitting: + title += " | STOPPING AFTER FAILURE" + + table = Table(title=title) + table.add_column("#", justify="right") + table.add_column("Board", no_wrap=True) + table.add_column("Status", no_wrap=True) + + for i, (vendor, board) in enumerate(boards): + board_id = f"{vendor}_{board}" + status_text = _format_status(statuses[i]) + state = statuses[i]["state"] + style = None + if state == "success": + style = "green" + elif state == "failed": + style = "red" + table.add_row( + f"{i + 1}/{len(boards)}", + board_id, + Text(status_text, style=style) if style else status_text, + ) + + return table + + +def _run_builds_tui( + port_dir, + boards, + extra_args, + jobserver, + env, + log_dir, + max_workers, + continue_on_error, +): + from rich.live import Live + + statuses = [ + {"state": "queued", "elapsed": 0.0, "start": None, "log_path": None} for _ in boards + ] + results = [] + futures = {} + next_index = 0 + stop_submitting = False + start_time = time.time() + + with Live( + _build_status_table(boards, statuses, start_time, stop_submitting), + refresh_per_second=4, + transient=False, + ) as live: + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: + while next_index < len(boards) and len(futures) < max_workers: + vendor, board = boards[next_index] + statuses[next_index]["state"] = "running" + statuses[next_index]["start"] = time.time() + future = executor.submit( + build_board, + port_dir, + vendor, + board, + extra_args, + jobserver, + env, + log_dir, + ) + futures[future] = next_index + next_index += 1 + + while futures: + for status in statuses: + if status["state"] == "running": + status["elapsed"] = time.time() - status["start"] + + live.update(_build_status_table(boards, statuses, start_time, stop_submitting)) + + done, _ = concurrent.futures.wait( + futures, + timeout=0.1, + return_when=concurrent.futures.FIRST_COMPLETED, + ) + for future in done: + index = futures.pop(future) + vendor, board = boards[index] + success, elapsed, _output, log_path = future.result() + statuses[index]["elapsed"] = elapsed + statuses[index]["log_path"] = log_path + statuses[index]["state"] = "success" if success else "failed" + results.append((vendor, board, success, elapsed)) + + if not success and not continue_on_error: + stop_submitting = True + + if not stop_submitting and next_index < len(boards): + vendor, board = boards[next_index] + statuses[next_index]["state"] = "running" + statuses[next_index]["start"] = time.time() + future = executor.submit( + build_board, + port_dir, + vendor, + board, + extra_args, + jobserver, + env, + log_dir, + ) + futures[future] = next_index + next_index += 1 + + if stop_submitting: + for index in range(next_index, len(boards)): + if statuses[index]["state"] == "queued": + statuses[index]["state"] = "skipped" + + live.update(_build_status_table(boards, statuses, start_time, stop_submitting)) + + return results, stop_submitting + + +def _run_builds_plain( + port_dir, + boards, + extra_args, + jobserver, + env, + log_dir, + max_workers, + continue_on_error, +): + results = [] + futures = {} + next_index = 0 + stop_submitting = False + + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: + while next_index < len(boards) and len(futures) < max_workers: + vendor, board = boards[next_index] + future = executor.submit( + build_board, + port_dir, + vendor, + board, + extra_args, + jobserver, + env, + log_dir, + ) + futures[future] = (vendor, board) + next_index += 1 + + while futures: + done, _ = concurrent.futures.wait( + futures, + return_when=concurrent.futures.FIRST_COMPLETED, + ) + for future in done: + vendor, board = futures.pop(future) + success, elapsed, _output, _log_path = future.result() + board_id = f"{vendor}_{board}" + status = "SUCCESS" if success else "FAILURE" + print(f"{board_id}: {status} ({elapsed:.1f}s)") + results.append((vendor, board, success, elapsed)) + + if not success and not continue_on_error: + stop_submitting = True + + if not stop_submitting and next_index < len(boards): + vendor, board = boards[next_index] + future = executor.submit( + build_board, + port_dir, + vendor, + board, + extra_args, + jobserver, + env, + log_dir, + ) + futures[future] = (vendor, board) + next_index += 1 + + return results, stop_submitting def main(): @@ -84,7 +407,7 @@ def main(): formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: - # Build all boards sequentially with 32 parallel jobs per board + # Build all boards in parallel with 32 jobserver slots %(prog)s -j32 # Build all boards using make's jobserver (recommended) @@ -94,9 +417,9 @@ def main(): parser.add_argument( "-j", "--jobs", - type=str, + type=int, default=None, - help="Number of parallel jobs for each board build (passed to make)", + help="Number of shared jobserver slots across all board builds", ) parser.add_argument( "--continue-on-error", @@ -106,6 +429,10 @@ def main(): args = parser.parse_args() + if args.jobs is not None and args.jobs < 1: + print("ERROR: --jobs must be at least 1") + return 2 + # Get the port directory port_dir = pathlib.Path(__file__).parent.resolve().parent @@ -116,48 +443,67 @@ def main(): print("ERROR: No boards found!") return 1 - # Prepare extra make arguments + # Prepare jobserver and extra make arguments + jobserver, detected_jobs = _jobserver_from_env() + env = os.environ.copy() + extra_args = [] - if args.jobs: - extra_args.append(f"-j{args.jobs}") + jobserver_jobs = detected_jobs + + if not jobserver: + jobserver_jobs = args.jobs if args.jobs else (os.cpu_count() or 1) + jobserver = _create_jobserver(jobserver_jobs) + env["MAKEFLAGS"] = ( + f"-j{jobserver_jobs} --jobserver-auth={jobserver.read_fd},{jobserver.write_fd}" + ) + + max_workers = jobserver_jobs + if max_workers is None: + max_workers = min(len(boards), os.cpu_count() or 1) + max_workers = max(1, min(len(boards), max_workers)) # Build all boards - start_time = time.time() - results = [] + log_dir = port_dir / "build-logs" + log_dir.mkdir(parents=True, exist_ok=True) try: - for index, (vendor, board) in enumerate(boards): - board_id = f"{vendor}_{board}" - print(f"{index + 1}/{len(boards)} {board_id}: ", end="", flush=True) - - success, elapsed = build_board(port_dir, vendor, board, extra_args) - if success: - print(f"✅ SUCCESS ({elapsed:.1f}s)") - else: - print(f"❌ FAILURE ({elapsed:.1f}s)") - results.append((vendor, board, success, elapsed)) - - if not success and not args.continue_on_error: - print("\nStopping due to build failure.") - break + use_tui = sys.stdout.isatty() + if use_tui: + try: + import rich # noqa: F401 + except ImportError: + use_tui = False + + if use_tui: + results, stop_submitting = _run_builds_tui( + port_dir, + boards, + extra_args, + jobserver, + env, + log_dir, + max_workers, + args.continue_on_error, + ) + else: + results, stop_submitting = _run_builds_plain( + port_dir, + boards, + extra_args, + jobserver, + env, + log_dir, + max_workers, + args.continue_on_error, + ) except KeyboardInterrupt: print("\n\nBuild interrupted by user.") return 130 # Standard exit code for SIGINT + finally: + if jobserver: + jobserver.close() - # Print summary - total_time = time.time() - start_time - print(f"\n{'=' * 80}") - print("Build Summary") - print(f"{'=' * 80}") - - successful = [r for r in results if r[2]] failed = [r for r in results if not r[2]] - - print(f"\nTotal boards: {len(results)}/{len(boards)}") - print(f"Successful: {len(successful)}") - print(f"Failed: {len(failed)}") - print(f"Total time: {total_time:.1f}s") - return 0 if len(failed) == 0 else 1 diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 3a007661c77d9..dcd49640a8d53 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -52,6 +52,7 @@ "json", "random", "digitalio", + "rotaryio", "rainbowio", "traceback", "warnings", @@ -81,9 +82,12 @@ } # Other flags to set when a module is enabled -EXTRA_FLAGS = {"busio": ["BUSIO_SPI", "BUSIO_I2C"]} +EXTRA_FLAGS = { + "busio": ["BUSIO_SPI", "BUSIO_I2C"], + "rotaryio": ["ROTARYIO_SOFTENCODER"], +} -SHARED_MODULE_AND_COMMON_HAL = ["_bleio", "os"] +SHARED_MODULE_AND_COMMON_HAL = ["_bleio", "os", "rotaryio"] # Mapping from module directory name to the flag name used in CIRCUITPY_ MODULE_FLAG_NAMES = { diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 86b0b0fe40386..f24bdfddadc43 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -20,7 +20,9 @@ "nordic_nrf_uarte": "serial", "nordic_nrf_uart": "serial", "nordic_nrf_twim": "i2c", + "nordic_nrf_twi": "i2c", "nordic_nrf_spim": "spi", + "nordic_nrf_spi": "spi", } # These are controllers, not the flash devices themselves. @@ -802,5 +804,6 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa board_info["source_files"] = [board_c] board_info["cflags"] = ("-I", board_dir) board_info["flash_count"] = len(flashes) + board_info["rotaryio"] = bool(ioports) board_info["usb_num_endpoint_pairs"] = usb_num_endpoint_pairs return board_info diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py index 0bc296cf43feb..cb7c61ed68af0 100644 --- a/ports/zephyr-cp/tests/conftest.py +++ b/ports/zephyr-cp/tests/conftest.py @@ -14,6 +14,7 @@ import pytest import serial from . import NativeSimProcess +from .perfetto_input_trace import write_input_trace from perfetto.trace_processor import TraceProcessor @@ -42,6 +43,10 @@ def pytest_configure(config): "markers", "code_py_runs(count): stop native_sim after count code.py runs (default: 1)", ) + config.addinivalue_line( + "markers", + "input_trace(trace): inject input signal trace data into native_sim", + ) ZEPHYR_CP = Path(__file__).parent.parent @@ -117,7 +122,6 @@ def log_uart_trace_output(trace_file: Path) -> None: @pytest.fixture def board(request): board = request.node.get_closest_marker("circuitpython_board") - print("board", board) if board is not None: board = board.args[0] else: @@ -159,6 +163,14 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp if len(drives) != instance_count: raise RuntimeError(f"not enough drives for {instance_count} instances") + input_trace_markers = list(request.node.iter_markers_with_node("input_trace")) + if len(input_trace_markers) > 1: + raise RuntimeError("expected at most one input_trace marker") + + input_trace = None + if input_trace_markers and len(input_trace_markers[0][1].args) == 1: + input_trace = input_trace_markers[0][1].args[0] + procs = [] for i in range(instance_count): flash = tmp_path / f"flash-{i}.bin" @@ -178,6 +190,11 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp trace_file = tmp_path / f"trace-{i}.perfetto" + input_trace_file = None + if input_trace is not None: + input_trace_file = tmp_path / f"input-{i}.perfetto" + write_input_trace(input_trace_file, input_trace) + marker = request.node.get_closest_marker("duration") if marker is None: timeout = 10 @@ -209,6 +226,9 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp # native_sim vm-runs includes the boot VM setup run. cmd.extend(("-no-rt", "-wait_uart", f"--vm-runs={code_py_runs + 1}")) + if input_trace_file is not None: + cmd.append(f"--input-trace={input_trace_file}") + marker = request.node.get_closest_marker("disable_i2c_devices") if marker and len(marker.args) > 0: for device in marker.args: diff --git a/ports/zephyr-cp/tests/docs/babblesim.md b/ports/zephyr-cp/tests/docs/babblesim.md index abf68b2b1de19..75d45079b2e68 100644 --- a/ports/zephyr-cp/tests/docs/babblesim.md +++ b/ports/zephyr-cp/tests/docs/babblesim.md @@ -59,6 +59,18 @@ pytest tests/test_bsim_ble_scan.py -v pytest tests/test_bsim_ble_advertising.py -v ``` +## Pytest markers + +For bsim-specific test tuning: + +- `@pytest.mark.duration(seconds)` controls simulation runtime/timeout. + +Example: + +```py +pytestmark = pytest.mark.duration(30.0) +``` + ## Notes - The bsim test spawns two instances that share a sim id. It only checks UART diff --git a/ports/zephyr-cp/tests/perfetto_input_trace.py b/ports/zephyr-cp/tests/perfetto_input_trace.py new file mode 100644 index 0000000000000..d0cde49be087a --- /dev/null +++ b/ports/zephyr-cp/tests/perfetto_input_trace.py @@ -0,0 +1,128 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries LLC +# SPDX-License-Identifier: MIT + +"""Utilities for creating Perfetto input trace files for native_sim tests. + +This module can be used directly from Python or from the command line: + + python -m tests.perfetto_input_trace input_trace.json output.perfetto + +Input JSON format: + +{ + "gpio_emul.01": [[8000000000, 0], [9000000000, 1], [10000000000, 0]], + "gpio_emul.02": [[8000000000, 0], [9200000000, 1]] +} +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Mapping, Sequence + +InputTraceData = Mapping[str, Sequence[tuple[int, int]]] + + +def _load_perfetto_pb2(): + from perfetto.protos.perfetto.trace import perfetto_trace_pb2 as perfetto_pb2 + + return perfetto_pb2 + + +def build_input_trace(trace_data: InputTraceData, *, sequence_id: int = 1): + """Build a Perfetto Trace protobuf for input replay counter tracks.""" + perfetto_pb2 = _load_perfetto_pb2() + trace = perfetto_pb2.Trace() + + seq_incremental_state_cleared = 1 + seq_needs_incremental_state = 2 + + for idx, (track_name, events) in enumerate(trace_data.items()): + track_uuid = 1001 + idx + + desc_packet = trace.packet.add() + desc_packet.timestamp = 0 + desc_packet.trusted_packet_sequence_id = sequence_id + if idx == 0: + desc_packet.sequence_flags = seq_incremental_state_cleared + desc_packet.track_descriptor.uuid = track_uuid + desc_packet.track_descriptor.name = track_name + desc_packet.track_descriptor.counter.unit = perfetto_pb2.CounterDescriptor.Unit.UNIT_COUNT + + for ts, value in events: + event_packet = trace.packet.add() + event_packet.timestamp = ts + event_packet.trusted_packet_sequence_id = sequence_id + event_packet.sequence_flags = seq_needs_incremental_state + event_packet.track_event.type = perfetto_pb2.TrackEvent.Type.TYPE_COUNTER + event_packet.track_event.track_uuid = track_uuid + event_packet.track_event.counter_value = value + + return trace + + +def write_input_trace( + trace_file: Path, trace_data: InputTraceData, *, sequence_id: int = 1 +) -> None: + """Write input replay data to a Perfetto trace file.""" + trace = build_input_trace(trace_data, sequence_id=sequence_id) + trace_file.parent.mkdir(parents=True, exist_ok=True) + trace_file.write_bytes(trace.SerializeToString()) + + +def _parse_trace_json(data: object) -> dict[str, list[tuple[int, int]]]: + if not isinstance(data, dict): + raise ValueError("top-level JSON value must be an object") + + parsed: dict[str, list[tuple[int, int]]] = {} + for track_name, events in data.items(): + if not isinstance(track_name, str): + raise ValueError("track names must be strings") + if not isinstance(events, list): + raise ValueError( + f"track {track_name!r} must map to a list of [timestamp, value] events" + ) + + parsed_events: list[tuple[int, int]] = [] + for event in events: + if not isinstance(event, (list, tuple)) or len(event) != 2: + raise ValueError(f"track {track_name!r} events must be [timestamp, value] pairs") + timestamp_ns, value = event + parsed_events.append((int(timestamp_ns), int(value))) + + parsed[track_name] = parsed_events + + return parsed + + +def _build_arg_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Generate a Perfetto input trace file used by native_sim --input-trace" + ) + parser.add_argument("input_json", type=Path, help="Path to input trace JSON") + parser.add_argument("output_trace", type=Path, help="Output .perfetto file path") + parser.add_argument( + "--sequence-id", + type=int, + default=1, + help="trusted_packet_sequence_id to use (default: 1)", + ) + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + parser = _build_arg_parser() + args = parser.parse_args(argv) + + trace_json = json.loads(args.input_json.read_text()) + trace_data = _parse_trace_json(trace_json) + write_input_trace(args.output_trace, trace_data, sequence_id=args.sequence_id) + + print(f"Wrote {args.output_trace} ({len(trace_data)} tracks)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/ports/zephyr-cp/tests/test_basics.py b/ports/zephyr-cp/tests/test_basics.py index 8ed9cc08a590d..84b31849a8e81 100644 --- a/ports/zephyr-cp/tests/test_basics.py +++ b/ports/zephyr-cp/tests/test_basics.py @@ -1,13 +1,10 @@ # SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries # SPDX-License-Identifier: MIT -"""Test LED blink functionality on native_sim.""" +"""Test basic native_sim functionality.""" import pytest -from pathlib import Path -from perfetto.trace_processor import TraceProcessor - @pytest.mark.circuitpy_drive(None) def test_blank_flash_hello_world(circuitpython): @@ -22,108 +19,6 @@ def test_blank_flash_hello_world(circuitpython): assert "done" in output -BLINK_CODE = """\ -import time -import board -import digitalio - -led = digitalio.DigitalInOut(board.LED) -led.direction = digitalio.Direction.OUTPUT - -for i in range(3): - print(f"LED on {i}") - led.value = True - time.sleep(0.1) - print(f"LED off {i}") - led.value = False - time.sleep(0.1) - -print("done") -""" - - -def parse_gpio_trace(trace_file: Path, pin_name: str = "gpio_emul.00") -> list[tuple[int, int]]: - """Parse GPIO trace from Perfetto trace file.""" - tp = TraceProcessor(file_path=str(trace_file)) - result = tp.query( - f''' - SELECT c.ts, c.value - FROM counter c - JOIN track t ON c.track_id = t.id - WHERE t.name = "{pin_name}" - ORDER BY c.ts - ''' - ) - return [(row.ts, int(row.value)) for row in result] - - -@pytest.mark.circuitpy_drive({"code.py": BLINK_CODE}) -def test_blink_output(circuitpython): - """Test blink program produces expected output and GPIO traces.""" - circuitpython.wait_until_done() - - # Check serial output - output = circuitpython.serial.all_output - assert "LED on 0" in output - assert "LED off 0" in output - assert "LED on 2" in output - assert "LED off 2" in output - assert "done" in output - - # Check GPIO traces - LED is on gpio_emul.00 - gpio_trace = parse_gpio_trace(circuitpython.trace_file, "gpio_emul.00") - - # Deduplicate by timestamp (keep last value at each timestamp) - by_timestamp = {} - for ts, val in gpio_trace: - by_timestamp[ts] = val - sorted_trace = sorted(by_timestamp.items()) - - # Find transition points (where value changes), skipping initialization at ts=0 - transitions = [] - for i in range(1, len(sorted_trace)): - prev_ts, prev_val = sorted_trace[i - 1] - curr_ts, curr_val = sorted_trace[i] - if prev_val != curr_val and curr_ts > 0: - transitions.append((curr_ts, curr_val)) - - # We expect at least 6 transitions (3 on + 3 off) from the blink loop - assert len(transitions) >= 6, f"Expected at least 6 transitions, got {len(transitions)}" - - # Verify timing between consecutive transitions - # Each sleep is 0.1s = 100ms = 100,000,000 ns - expected_interval_ns = 100_000_000 - tolerance_ns = 20_000_000 # 20ms tolerance - - # Find a sequence of 6 consecutive transitions with ~100ms intervals (the blink loop) - # This filters out initialization and cleanup noise - blink_transitions = [] - for i in range(len(transitions) - 1): - interval = transitions[i + 1][0] - transitions[i][0] - if abs(interval - expected_interval_ns) < tolerance_ns: - if not blink_transitions: - blink_transitions.append(transitions[i]) - blink_transitions.append(transitions[i + 1]) - elif blink_transitions: - # Found end of blink sequence - break - - assert len(blink_transitions) >= 6, ( - f"Expected at least 6 blink transitions with ~100ms intervals, got {len(blink_transitions)}" - ) - - # Verify timing between blink transitions - for i in range(1, min(6, len(blink_transitions))): - prev_ts = blink_transitions[i - 1][0] - curr_ts = blink_transitions[i][0] - interval = curr_ts - prev_ts - assert abs(interval - expected_interval_ns) < tolerance_ns, ( - f"Transition interval {interval / 1_000_000:.1f}ms deviates from " - f"expected {expected_interval_ns / 1_000_000:.1f}ms by more than " - f"{tolerance_ns / 1_000_000:.1f}ms tolerance" - ) - - # --- PTY Input Tests --- diff --git a/ports/zephyr-cp/tests/test_digitalio.py b/ports/zephyr-cp/tests/test_digitalio.py new file mode 100644 index 0000000000000..22c64f7b83fd7 --- /dev/null +++ b/ports/zephyr-cp/tests/test_digitalio.py @@ -0,0 +1,171 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries LLC +# SPDX-License-Identifier: MIT + +"""Test digitalio functionality on native_sim.""" + +import re +from pathlib import Path + +import pytest +from perfetto.trace_processor import TraceProcessor + + +DIGITALIO_INPUT_TRACE_READ_CODE = """\ +import time +import digitalio +import microcontroller + +pin = digitalio.DigitalInOut(microcontroller.pin.P_01) +pin.direction = digitalio.Direction.INPUT + +start = time.monotonic() +last = pin.value +print(f"t_abs={time.monotonic():.3f} initial={last}") + +# Poll long enough to observe a high pulse injected through input trace. +while time.monotonic() - start < 8.0: + value = pin.value + if value != last: + print(f"t_abs={time.monotonic():.3f} edge={value}") + last = value + time.sleep(0.05) + +print(f"t_abs={time.monotonic():.3f} done") +""" + + +DIGITALIO_INPUT_TRACE = { + "gpio_emul.01": [ + (8_000_000_000, 0), + (9_000_000_000, 1), + (10_000_000_000, 0), + ], +} + + +@pytest.mark.duration(14.0) +@pytest.mark.circuitpy_drive({"code.py": DIGITALIO_INPUT_TRACE_READ_CODE}) +@pytest.mark.input_trace(DIGITALIO_INPUT_TRACE) +def test_digitalio_reads_input_trace(circuitpython): + """Test DigitalInOut input reads values injected via input trace.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + + initial_match = re.search(r"t_abs=([0-9]+\.[0-9]+) initial=False", output) + edge_match = re.search(r"t_abs=([0-9]+\.[0-9]+) edge=True", output) + done_match = re.search(r"t_abs=([0-9]+\.[0-9]+) done", output) + + assert initial_match is not None + assert edge_match is not None + assert done_match is not None + + initial_abs = float(initial_match.group(1)) + edge_abs = float(edge_match.group(1)) + done_abs = float(done_match.group(1)) + + # Input trace edge is at 9.0s for gpio_emul.01. + assert 8.5 <= edge_abs <= 9.5 + assert initial_abs <= edge_abs <= done_abs + + +BLINK_CODE = """\ +import time +import board +import digitalio + +led = digitalio.DigitalInOut(board.LED) +led.direction = digitalio.Direction.OUTPUT + +for i in range(3): + print(f"LED on {i}") + led.value = True + time.sleep(0.1) + print(f"LED off {i}") + led.value = False + time.sleep(0.1) + +print("done") +""" + + +def parse_gpio_trace(trace_file: Path, pin_name: str = "gpio_emul.00") -> list[tuple[int, int]]: + """Parse GPIO trace from Perfetto trace file.""" + tp = TraceProcessor(file_path=str(trace_file)) + result = tp.query( + f''' + SELECT c.ts, c.value + FROM counter c + JOIN track t ON c.track_id = t.id + WHERE t.name = "{pin_name}" + ORDER BY c.ts + ''' + ) + return [(row.ts, int(row.value)) for row in result] + + +@pytest.mark.circuitpy_drive({"code.py": BLINK_CODE}) +def test_digitalio_blink_output(circuitpython): + """Test blink program produces expected output and GPIO traces.""" + circuitpython.wait_until_done() + + # Check serial output + output = circuitpython.serial.all_output + assert "LED on 0" in output + assert "LED off 0" in output + assert "LED on 2" in output + assert "LED off 2" in output + assert "done" in output + + # Check GPIO traces - LED is on gpio_emul.00 + gpio_trace = parse_gpio_trace(circuitpython.trace_file, "gpio_emul.00") + + # Deduplicate by timestamp (keep last value at each timestamp) + by_timestamp = {} + for ts, val in gpio_trace: + by_timestamp[ts] = val + sorted_trace = sorted(by_timestamp.items()) + + # Find transition points (where value changes), skipping initialization at ts=0 + transitions = [] + for i in range(1, len(sorted_trace)): + prev_ts, prev_val = sorted_trace[i - 1] + curr_ts, curr_val = sorted_trace[i] + if prev_val != curr_val and curr_ts > 0: + transitions.append((curr_ts, curr_val)) + + # We expect at least 6 transitions (3 on + 3 off) from the blink loop + assert len(transitions) >= 6, f"Expected at least 6 transitions, got {len(transitions)}" + + # Verify timing between consecutive transitions + # Each sleep is 0.1s = 100ms = 100,000,000 ns + expected_interval_ns = 100_000_000 + tolerance_ns = 20_000_000 # 20ms tolerance + + # Find a sequence of 6 consecutive transitions with ~100ms intervals (the blink loop) + # This filters out initialization and cleanup noise + blink_transitions = [] + for i in range(len(transitions) - 1): + interval = transitions[i + 1][0] - transitions[i][0] + if abs(interval - expected_interval_ns) < tolerance_ns: + if not blink_transitions: + blink_transitions.append(transitions[i]) + blink_transitions.append(transitions[i + 1]) + elif blink_transitions: + # Found end of blink sequence + break + + assert len(blink_transitions) >= 6, ( + f"Expected at least 6 blink transitions with ~100ms intervals, got {len(blink_transitions)}" + ) + + # Verify timing between blink transitions + for i in range(1, min(6, len(blink_transitions))): + prev_ts = blink_transitions[i - 1][0] + curr_ts = blink_transitions[i][0] + interval = curr_ts - prev_ts + assert abs(interval - expected_interval_ns) < tolerance_ns, ( + f"Transition interval {interval / 1_000_000:.1f}ms deviates from " + f"expected {expected_interval_ns / 1_000_000:.1f}ms by more than " + f"{tolerance_ns / 1_000_000:.1f}ms tolerance" + ) diff --git a/ports/zephyr-cp/tests/test_rotaryio.py b/ports/zephyr-cp/tests/test_rotaryio.py new file mode 100644 index 0000000000000..e9a5c1913cb20 --- /dev/null +++ b/ports/zephyr-cp/tests/test_rotaryio.py @@ -0,0 +1,132 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries LLC +# SPDX-License-Identifier: MIT + +"""Test rotaryio functionality on native_sim.""" + +import pytest + + +ROTARY_CODE_5S = """\ +import time +import microcontroller +import rotaryio + +encoder = rotaryio.IncrementalEncoder(microcontroller.pin.P_01, microcontroller.pin.P_02) + +time.sleep(5.0) # Sleep long enough for trace events to complete +print(f"position={encoder.position}") +print("done") +""" + + +ROTARY_CODE_7S = """\ +import time +import microcontroller +import rotaryio + +encoder = rotaryio.IncrementalEncoder(microcontroller.pin.P_01, microcontroller.pin.P_02) + +time.sleep(7.0) # Sleep long enough for trace events to complete +print(f"position={encoder.position}") +print("done") +""" + + +CLOCKWISE_TRACE = { + "gpio_emul.01": [ + (4_000_000_000, 0), # 4.0s: initial state (low) + (4_100_000_000, 1), # 4.1s: A goes high (A leads) + (4_300_000_000, 0), # 4.3s: A goes low + ], + "gpio_emul.02": [ + (4_000_000_000, 0), # 4.0s: initial state (low) + (4_200_000_000, 1), # 4.2s: B goes high (B follows) + (4_400_000_000, 0), # 4.4s: B goes low + ], +} + +COUNTERCLOCKWISE_TRACE = { + "gpio_emul.01": [ + (4_000_000_000, 0), # 4.0s: initial state (low) + (4_200_000_000, 1), # 4.2s: A goes high (A follows) + (4_400_000_000, 0), # 4.4s: A goes low + ], + "gpio_emul.02": [ + (4_000_000_000, 0), # 4.0s: initial state (low) + (4_100_000_000, 1), # 4.1s: B goes high (B leads) + (4_300_000_000, 0), # 4.3s: B goes low + ], +} + +BOTH_DIRECTIONS_TRACE = { + "gpio_emul.01": [ + (4_000_000_000, 0), # Initial state + # First clockwise detent + (4_100_000_000, 1), # A rises (leads) + (4_300_000_000, 0), # A falls + # Second clockwise detent + (4_500_000_000, 1), # A rises (leads) + (4_700_000_000, 0), # A falls + # First counter-clockwise detent + (5_000_000_000, 1), # A rises (follows) + (5_200_000_000, 0), # A falls + # Second counter-clockwise detent + (5_400_000_000, 1), # A rises (follows) + (5_600_000_000, 0), # A falls + # Third counter-clockwise detent + (5_800_000_000, 1), # A rises (follows) + (6_000_000_000, 0), # A falls + ], + "gpio_emul.02": [ + (4_000_000_000, 0), # Initial state + # First clockwise detent + (4_200_000_000, 1), # B rises (follows) + (4_400_000_000, 0), # B falls + # Second clockwise detent + (4_600_000_000, 1), # B rises (follows) + (4_800_000_000, 0), # B falls + # First counter-clockwise detent + (4_900_000_000, 1), # B rises (leads) + (5_100_000_000, 0), # B falls + # Second counter-clockwise detent + (5_300_000_000, 1), # B rises (leads) + (5_500_000_000, 0), # B falls + # Third counter-clockwise detent + (5_700_000_000, 1), # B rises (leads) + (5_900_000_000, 0), # B falls + ], +} + + +@pytest.mark.circuitpy_drive({"code.py": ROTARY_CODE_5S}) +@pytest.mark.input_trace(CLOCKWISE_TRACE) +def test_rotaryio_incrementalencoder_clockwise(circuitpython): + """Test clockwise rotation increments position.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "position=1" in output + assert "done" in output + + +@pytest.mark.circuitpy_drive({"code.py": ROTARY_CODE_5S}) +@pytest.mark.input_trace(COUNTERCLOCKWISE_TRACE) +def test_rotaryio_incrementalencoder_counterclockwise(circuitpython): + """Test counter-clockwise rotation decrements position.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "position=-1" in output + assert "done" in output + + +@pytest.mark.duration(12.0) +@pytest.mark.circuitpy_drive({"code.py": ROTARY_CODE_7S}) +@pytest.mark.input_trace(BOTH_DIRECTIONS_TRACE) +def test_rotaryio_incrementalencoder_both_directions(circuitpython): + """Test rotation in both directions: 2 clockwise, then 3 counter-clockwise.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "position=-1" in output + assert "done" in output diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index a8b05fabd05cc..736cea31de1ca 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -8,6 +8,6 @@ manifest: path: modules/bsim_hw_models/nrf_hw_models - name: zephyr url: https://github.com/adafruit/zephyr - revision: 3c5a3a72daa3ca6462cd8bc9c8c7c6a41fbf3b2e + revision: 8801b409ec554cfd217c159c00f91280ea1331db clone-depth: 100 import: true From 4f015eee197074feb983473765c23b81520c3412 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Tue, 24 Feb 2026 23:15:45 -0500 Subject: [PATCH 360/517] wip --- .../adafruit_metro_esp32s3/mpconfigboard.mk | 2 + shared-module/sdcardio/SDCard.c | 136 +++++++++++------- 2 files changed, 85 insertions(+), 53 deletions(-) diff --git a/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk b/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk index 2fd5a40f25913..57ccf4a4074e9 100644 --- a/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk @@ -12,3 +12,5 @@ CIRCUITPY_ESP_FLASH_SIZE = 16MB CIRCUITPY_ESP_PSRAM_MODE = opi CIRCUITPY_ESP_PSRAM_FREQ = 80m CIRCUITPY_ESP_PSRAM_SIZE = 8MB + +CIRCUITPY_ULAB = 0 diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c index 878fed7a13c9b..f9b937686b75f 100644 --- a/shared-module/sdcardio/SDCard.c +++ b/shared-module/sdcardio/SDCard.c @@ -7,7 +7,7 @@ // This implementation largely follows the structure of adafruit_sdcard.py #include "extmod/vfs.h" - +#include "esp_log.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/sdcardio/SDCard.h" @@ -23,7 +23,12 @@ #define DEBUG_PRINT(...) ((void)0) #endif -#define CMD_TIMEOUT (200) +// https://www.taterli.com/wp-content/uploads/2017/05/Physical-Layer-Simplified-SpecificationV6.0.pdf +// specifies timeouts for read (100 ms), write (250 ms), erase (depends on size), and other operations. +// But the document also suggests allowing 500 ms even if a shorter timeout is specified. +// So let's allow a nice long time, but don't wait in a tight loop: allow background tasks to run. +#define CMD_TIMEOUT_MS (500) +#define TIMEOUT_MS (500) #define R1_IDLE_STATE (1 << 0) #define R1_ILLEGAL_COMMAND (1 << 2) @@ -106,21 +111,38 @@ static uint8_t CRC7(const uint8_t *data, uint8_t n) { return (crc << 1) | 1; } -#define READY_TIMEOUT_NS (300 * 1000 * 1000) // 300ms -static int wait_for_ready(sdcardio_sdcard_obj_t *self) { - uint64_t deadline = common_hal_time_monotonic_ns() + READY_TIMEOUT_NS; - while (common_hal_time_monotonic_ns() < deadline) { +// Assumes that the spi lock has been acquired. +// +// Mask the incoming value with mask. Use 0xff to not mask. +// if not_match is true, wait for something NOT matching the value. +// Return the response as an int32_t (which is always >= 0), or -1 if timed out. +static int32_t wait_for_masked_response(sdcardio_sdcard_obj_t *self, uint8_t mask, uint8_t response, bool not_match, uint32_t timeout_ms) { + uint64_t deadline = supervisor_ticks_ms64() + timeout_ms; + while (supervisor_ticks_ms64() < deadline) { uint8_t b; common_hal_busio_spi_read(self->bus, &b, 1, 0xff); - if (b == 0xff) { - return 0; + if (((b & mask) == response) ^ not_match) { + return b; } + RUN_BACKGROUND_TASKS; } - return -ETIMEDOUT; + return -1; +} + +// Wait for the given response byte. +static bool wait_for_response(sdcardio_sdcard_obj_t *self, uint8_t response) { + return wait_for_masked_response(self, 0xff, response, false, TIMEOUT_MS) != -1; +} + +#define READY_TIMEOUT_MS (300) + +// Wait for 0xff, with a shorter timeout. +static bool wait_for_ready(sdcardio_sdcard_obj_t *self) { + return wait_for_masked_response(self, 0xff, 0xff, false, READY_TIMEOUT_MS) != -1; } // Note: this is never called while "in cmd25" (in fact, it's only used by `exit_cmd25`) -static int cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { +static mp_negative_errno_t cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { uint8_t cmdbuf[2] = {cmd, 0xff}; assert(!self->in_cmd25); @@ -128,17 +150,14 @@ static int cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf)); // Wait for the response (response[7] == response) - for (int i = 0; i < CMD_TIMEOUT; i++) { - common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff); - if (cmdbuf[0] == response) { - return 0; - } + if (wait_for_response(self, response)) { + return 0; } return -MP_EIO; } -static int exit_cmd25(sdcardio_sdcard_obj_t *self) { +static mp_negative_errno_t exit_cmd25(sdcardio_sdcard_obj_t *self) { if (self->in_cmd25) { DEBUG_PRINT("exit cmd25\n"); self->in_cmd25 = false; @@ -149,7 +168,7 @@ static int exit_cmd25(sdcardio_sdcard_obj_t *self) { // In Python API, defaults are response=None, data_block=True, wait=True static int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf, size_t response_len, bool data_block, bool wait) { - int r = exit_cmd25(self); + mp_negative_errno_t r = exit_cmd25(self); if (r < 0) { return r; } @@ -164,25 +183,17 @@ static int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf cmdbuf[5] = CRC7(cmdbuf, 5); if (wait) { - r = wait_for_ready(self); - if (r < 0) { - return r; + if (!wait_for_ready(self)) { + return -MP_ETIMEDOUT; } } common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf)); // Wait for the response (response[7] == 0) - bool response_received = false; - for (int i = 0; i < CMD_TIMEOUT; i++) { - common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff); - if ((cmdbuf[0] & 0x80) == 0) { - response_received = true; - break; - } - } - - if (!response_received) { + // Now wait for cmd response, which is the high bit being 0. + int32_t response = wait_for_masked_response(self, 0x80, 0, false, CMD_TIMEOUT_MS); + if (response == -1) { return -MP_EIO; } @@ -190,22 +201,25 @@ static int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf if (data_block) { cmdbuf[1] = 0xff; - do { - // Wait for the start block byte - common_hal_busio_spi_read(self->bus, cmdbuf + 1, 1, 0xff); - } while (cmdbuf[1] != 0xfe); + if (!wait_for_response(self, 0xfe)) { + return -MP_EIO; + } } - common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff); + if (!common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff)) { + return -MP_EIO; + } if (data_block) { // Read and discard the CRC-CCITT checksum - common_hal_busio_spi_read(self->bus, cmdbuf + 1, 2, 0xff); + if (!common_hal_busio_spi_read(self->bus, cmdbuf + 1, 2, 0xff)) { + return -MP_EIO; + } } } - return cmdbuf[0]; + return response; } static int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *response_buf, size_t response_len, bool data_block, bool wait) { @@ -213,16 +227,19 @@ static int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *res } static mp_rom_error_text_t init_card_v1(sdcardio_sdcard_obj_t *self) { - for (int i = 0; i < CMD_TIMEOUT; i++) { + uint64_t deadline = supervisor_ticks_ms64() + CMD_TIMEOUT_MS; + while (supervisor_ticks_ms64() < deadline) { if (cmd(self, 41, 0, NULL, 0, true, true) == 0) { return NULL; } + RUN_BACKGROUND_TASKS; } return MP_ERROR_TEXT("timeout waiting for v1 card"); } static mp_rom_error_text_t init_card_v2(sdcardio_sdcard_obj_t *self) { - for (int i = 0; i < CMD_TIMEOUT; i++) { + uint64_t deadline = supervisor_ticks_ms64() + CMD_TIMEOUT_MS; + while (supervisor_ticks_ms64() < deadline) { uint8_t ocr[4]; common_hal_time_delay_ms(50); cmd(self, 58, 0, ocr, sizeof(ocr), false, true); @@ -234,6 +251,7 @@ static mp_rom_error_text_t init_card_v2(sdcardio_sdcard_obj_t *self) { } return NULL; } + RUN_BACKGROUND_TASKS; } return MP_ERROR_TEXT("timeout waiting for v2 card"); } @@ -250,6 +268,7 @@ static mp_rom_error_text_t init_card(sdcardio_sdcard_obj_t *self) { { bool reached_idle_state = false; for (int i = 0; i < 5; i++) { + ESP_LOGW("init_card", "loop: %d", i); // do not call cmd with wait=true, because that will return // prematurely if the idle state is not reached. we can't depend on // this when the card is not yet in SPI mode @@ -366,23 +385,25 @@ int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self) { } static int readinto(sdcardio_sdcard_obj_t *self, void *buf, size_t size) { - uint8_t aux[2] = {0, 0}; - while (aux[0] != 0xfe) { - common_hal_busio_spi_read(self->bus, aux, 1, 0xff); + + if (!wait_for_response(self, 0xfe)) { + return -MP_EIO; } common_hal_busio_spi_read(self->bus, buf, size, 0xff); // Read checksum and throw it away - common_hal_busio_spi_read(self->bus, aux, sizeof(aux), 0xff); + uint8_t checksum[2]; + common_hal_busio_spi_read(self->bus, checksum, sizeof(checksum), 0xff); return 0; } +// The mp_uint_t is misleading; negative errors can be returned. mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t nblocks) { // deinit check is in lock_and_configure_bus() sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!lock_and_configure_bus(self)) { - return MP_EAGAIN; + return -MP_EAGAIN; } int r = 0; size_t buflen = 512 * nblocks; @@ -415,6 +436,7 @@ mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t st } } extraclock_and_unlock_bus(self); + // No caller actually uses this value. return r; } @@ -427,7 +449,9 @@ int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t } static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t size) { - wait_for_ready(self); + if (!wait_for_ready(self)) { + return -MP_ETIMEDOUT; + } uint8_t cmd[2]; cmd[0] = token; @@ -450,7 +474,8 @@ static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t // with STATUS 010 indicating "data accepted", and other status bit // combinations indicating failure. // In practice, I was seeing cmd[0] as 0xe5, indicating success - for (int i = 0; i < CMD_TIMEOUT; i++) { + uint64_t deadline = supervisor_ticks_ms64() + CMD_TIMEOUT_MS; + while (supervisor_ticks_ms64() < deadline) { common_hal_busio_spi_read(self->bus, cmd, 1, 0xff); DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]); if ((cmd[0] & 0b00010001) == 0b00000001) { @@ -460,12 +485,15 @@ static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t break; } } + RUN_BACKGROUND_TASKS; } // Wait for the write to finish - do { - common_hal_busio_spi_read(self->bus, cmd, 1, 0xff); - } while (cmd[0] == 0); + + // Wait for a non-zero value. + if (wait_for_masked_response(self, 0xff /*mask*/, 0, true /*not_match*/, TIMEOUT_MS) == -1) { + return -MP_EIO; + } // Success return 0; @@ -475,7 +503,7 @@ mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t s // deinit check is in lock_and_configure_bus() sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!lock_and_configure_bus(self)) { - return MP_EAGAIN; + return -MP_EAGAIN; } if (!self->in_cmd25 || start_block != self->next_block) { @@ -507,15 +535,17 @@ mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t s return 0; } -int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self) { +mp_negative_errno_t common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self) { // deinit check is in lock_and_configure_bus() - lock_and_configure_bus(self); + if (!lock_and_configure_bus(self)) { + return -MP_EAGAIN; + } int r = exit_cmd25(self); extraclock_and_unlock_bus(self); return r; } -int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { +mp_negative_errno_t common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { if (buf->len % 512 != 0) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Buffer must be a multiple of %d bytes"), 512); } From 73aa39f23c87a9061422b02bf7177d0829df67d9 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 11 Feb 2026 10:18:28 -0800 Subject: [PATCH 361/517] Add networking support to the Zephyr native_sim It uses the host's sockets directly to test our zsock integration. This doesn't test network management. It does also test the web workflow. --- .gitignore | 4 + locale/circuitpython.pot | 5 +- ports/zephyr-cp/Makefile | 13 +- ports/zephyr-cp/README.md | 14 + .../bindings/hostnetwork/HostNetwork.c | 37 ++ .../bindings/hostnetwork/HostNetwork.h | 19 + .../zephyr-cp/bindings/hostnetwork/__init__.c | 26 + .../zephyr-cp/bindings/hostnetwork/__init__.h | 11 + ports/zephyr-cp/boards/frdm_rw612.conf | 1 + .../native/native_sim/autogen_board_info.toml | 5 +- .../nrf5340bsim/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/native_sim.conf | 16 + .../nordic/nrf5340dk/autogen_board_info.toml | 1 + .../nordic/nrf54h20dk/autogen_board_info.toml | 1 + .../nordic/nrf54l15dk/autogen_board_info.toml | 1 + .../nordic/nrf7002dk/autogen_board_info.toml | 3 +- .../nxp/frdm_mcxn947/autogen_board_info.toml | 1 + .../nxp/frdm_rw612/autogen_board_info.toml | 3 +- .../mimxrt1170_evk/autogen_board_info.toml | 1 + .../da14695_dk_usb/autogen_board_info.toml | 1 + .../renesas/ek_ra6m5/autogen_board_info.toml | 1 + .../renesas/ek_ra8d1/autogen_board_info.toml | 1 + .../nucleo_n657x0_q/autogen_board_info.toml | 1 + .../nucleo_u575zi_q/autogen_board_info.toml | 1 + .../st/stm32h7b3i_dk/autogen_board_info.toml | 1 + .../stm32wba65i_dk1/autogen_board_info.toml | 1 + .../common-hal/hostnetwork/HostNetwork.c | 15 + .../common-hal/hostnetwork/HostNetwork.h | 11 + .../zephyr-cp/common-hal/socketpool/Socket.c | 518 +++++++++--------- .../common-hal/socketpool/SocketPool.c | 178 +++--- .../common-hal/socketpool/SocketPool.h | 3 + .../zephyr-cp/cptools/build_circuitpython.py | 57 +- ports/zephyr-cp/cptools/zephyr2cp.py | 16 + ports/zephyr-cp/prj.conf | 16 +- ports/zephyr-cp/tests/conftest.py | 9 +- ports/zephyr-cp/tests/docs/web_workflow.md | 37 ++ ports/zephyr-cp/tests/test_web_workflow.py | 142 +++++ py/circuitpy_mpconfig.mk | 3 + shared-bindings/socketpool/SocketPool.c | 7 +- supervisor/shared/web_workflow/web_workflow.c | 137 +++-- supervisor/shared/web_workflow/web_workflow.h | 5 + supervisor/shared/workflow.c | 12 +- 42 files changed, 911 insertions(+), 425 deletions(-) create mode 100644 ports/zephyr-cp/bindings/hostnetwork/HostNetwork.c create mode 100644 ports/zephyr-cp/bindings/hostnetwork/HostNetwork.h create mode 100644 ports/zephyr-cp/bindings/hostnetwork/__init__.c create mode 100644 ports/zephyr-cp/bindings/hostnetwork/__init__.h create mode 100644 ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.c create mode 100644 ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.h create mode 100644 ports/zephyr-cp/tests/docs/web_workflow.md create mode 100644 ports/zephyr-cp/tests/test_web_workflow.py diff --git a/.gitignore b/.gitignore index 5b3b1db2d33be..ca6872387dd27 100644 --- a/.gitignore +++ b/.gitignore @@ -113,3 +113,7 @@ TAGS # git-review-web outputs .review + +# Zephyr trace files +**/channel0_0 +**/*.perfetto-trace diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 48e0ab6fbfd85..e2eebdea0fe04 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -2067,10 +2067,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index c4feead23b9b5..5e320a328818c 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -8,8 +8,7 @@ BUILD ?= build-$(BOARD) TRANSLATION ?= en_US - -.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug run clean menuconfig all clean-all test fetch-port-submodules +.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug run run-sim clean menuconfig all clean-all test fetch-port-submodules $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) @@ -36,6 +35,16 @@ debug: $(BUILD)/zephyr-cp/zephyr/zephyr.elf run: $(BUILD)/firmware.exe $^ +run-sim: + $(MAKE) BOARD=native_native_sim BUILD=build-native_native_sim build-native_native_sim/firmware.exe + truncate -s 2M build-native_native_sim/flash.bin + mformat -i build-native_native_sim/flash.bin :: + @if [ -d CIRCUITPY ] && [ -n "$$(find CIRCUITPY -mindepth 1 -print -quit)" ]; then \ + echo "Populating build-native_native_sim/flash.bin from ./CIRCUITPY"; \ + mcopy -s -i build-native_native_sim/flash.bin CIRCUITPY/* ::; \ + fi + build-native_native_sim/firmware.exe --flash=build-native_native_sim/flash.bin --flash_rm -wait_uart -rt + menuconfig: west build --sysbuild -d $(BUILD) -t menuconfig diff --git a/ports/zephyr-cp/README.md b/ports/zephyr-cp/README.md index 1fba1d7d3f7f4..f4391fc4cb635 100644 --- a/ports/zephyr-cp/README.md +++ b/ports/zephyr-cp/README.md @@ -28,6 +28,20 @@ make BOARD=nordic_nrf7002dk This uses Zephyr's cmake to generate Makefiles that then delegate to `tools/cpbuild/build_circuitpython.py` to build the CircuitPython bits in parallel. +## Running the native simulator + +From `ports/zephyr-cp`, run: + +```sh +make run-sim +``` + +`run-sim` starts the native simulator in realtime. +It prints the PTY path to connect to the simulator REPL. +If a local `./CIRCUITPY/` folder exists, its files are used as the simulator's CIRCUITPY drive. + +Edit files in `./CIRCUITPY` (for example `code.py`) and rerun `make run-sim` to test changes. + ## Testing other boards [Any Zephyr board](https://docs.zephyrproject.org/latest/boards/index.html#) can diff --git a/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.c b/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.c new file mode 100644 index 0000000000000..fc0fb9ecc4f8f --- /dev/null +++ b/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.c @@ -0,0 +1,37 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/hostnetwork/HostNetwork.h" + +#include "py/runtime.h" + +//| class HostNetwork: +//| """Native networking for the host simulator.""" +//| +//| def __init__(self) -> None: +//| """Create a HostNetwork instance.""" +//| ... +//| +static mp_obj_t hostnetwork_hostnetwork_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + hostnetwork_hostnetwork_obj_t *self = mp_obj_malloc(hostnetwork_hostnetwork_obj_t, &hostnetwork_hostnetwork_type); + common_hal_hostnetwork_hostnetwork_construct(self); + return MP_OBJ_FROM_PTR(self); +} + +static const mp_rom_map_elem_t hostnetwork_hostnetwork_locals_dict_table[] = { +}; +static MP_DEFINE_CONST_DICT(hostnetwork_hostnetwork_locals_dict, hostnetwork_hostnetwork_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + hostnetwork_hostnetwork_type, + MP_QSTR_HostNetwork, + MP_TYPE_FLAG_NONE, + make_new, hostnetwork_hostnetwork_make_new, + locals_dict, &hostnetwork_hostnetwork_locals_dict + ); diff --git a/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.h b/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.h new file mode 100644 index 0000000000000..009d8c0608743 --- /dev/null +++ b/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.h @@ -0,0 +1,19 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} hostnetwork_hostnetwork_obj_t; + +extern const mp_obj_type_t hostnetwork_hostnetwork_type; + +void common_hal_hostnetwork_hostnetwork_construct(hostnetwork_hostnetwork_obj_t *self); diff --git a/ports/zephyr-cp/bindings/hostnetwork/__init__.c b/ports/zephyr-cp/bindings/hostnetwork/__init__.c new file mode 100644 index 0000000000000..92302b67d0e97 --- /dev/null +++ b/ports/zephyr-cp/bindings/hostnetwork/__init__.c @@ -0,0 +1,26 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" + +#include "bindings/hostnetwork/__init__.h" +#include "bindings/hostnetwork/HostNetwork.h" + +//| """Host networking support for the native simulator.""" +//| + +static const mp_rom_map_elem_t hostnetwork_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_hostnetwork) }, + { MP_ROM_QSTR(MP_QSTR_HostNetwork), MP_ROM_PTR(&hostnetwork_hostnetwork_type) }, +}; +static MP_DEFINE_CONST_DICT(hostnetwork_module_globals, hostnetwork_module_globals_table); + +const mp_obj_module_t hostnetwork_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&hostnetwork_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_hostnetwork, hostnetwork_module); diff --git a/ports/zephyr-cp/bindings/hostnetwork/__init__.h b/ports/zephyr-cp/bindings/hostnetwork/__init__.h new file mode 100644 index 0000000000000..a6731546bdef1 --- /dev/null +++ b/ports/zephyr-cp/bindings/hostnetwork/__init__.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "bindings/hostnetwork/HostNetwork.h" + +extern hostnetwork_hostnetwork_obj_t common_hal_hostnetwork_obj; diff --git a/ports/zephyr-cp/boards/frdm_rw612.conf b/ports/zephyr-cp/boards/frdm_rw612.conf index 7f063218153e5..7d56dfed29cc9 100644 --- a/ports/zephyr-cp/boards/frdm_rw612.conf +++ b/ports/zephyr-cp/boards/frdm_rw612.conf @@ -20,6 +20,7 @@ CONFIG_MBEDTLS_PKCS1_V15=y CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=y CONFIG_MBEDTLS_ENTROPY_C=y CONFIG_MBEDTLS_CTR_DRBG_ENABLED=y +CONFIG_MBEDTLS_SHA1=y CONFIG_MBEDTLS_USE_PSA_CRYPTO=n CONFIG_BT=y diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 73897f7162082..3738e6494ec8b 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -49,7 +49,8 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true # Zephyr networking enabled +hostnetwork = true # Zephyr board has hostnetwork i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false @@ -87,7 +88,7 @@ rtc = false sdcardio = true # Zephyr board has busio sdioio = false sharpdisplay = true # Zephyr board has busio -socketpool = false +socketpool = true # Zephyr networking enabled spitarget = false ssl = false storage = true # Zephyr board has flash diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 9ddbb2153fb63..aa2deb2b41ce2 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/native_sim.conf b/ports/zephyr-cp/boards/native_sim.conf index cc295949d0351..ddbfef11266d8 100644 --- a/ports/zephyr-cp/boards/native_sim.conf +++ b/ports/zephyr-cp/boards/native_sim.conf @@ -18,3 +18,19 @@ CONFIG_I2C_EMUL=y CONFIG_EEPROM=y CONFIG_EEPROM_AT24=y CONFIG_EEPROM_AT2X_EMUL=y + +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_ETH_NATIVE_TAP=n +CONFIG_NET_DRIVERS=y +CONFIG_NET_SOCKETS_OFFLOAD=y +CONFIG_NET_NATIVE_OFFLOADED_SOCKETS=y +CONFIG_HEAP_MEM_POOL_SIZE=1024 + +CONFIG_NET_LOG=y + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_SHA256=y diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 52beeda076fc3..8490d71d8f6b5 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index 2759dfb89c1c7..333733d6b307e 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 6df832f607b2c..10d03bf857840 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index d713552d87be7..7d2bceb969112 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -49,7 +49,8 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true # Zephyr networking enabled +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 233796fc6f443..37091a344cc0e 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index f7ad0289cc75e..3030dff3a6ecf 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -49,7 +49,8 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true # Zephyr networking enabled +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index 4926b5c9a6cce..9a5c4c541447d 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index 77d93aa10f095..883bf6be4665e 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 2ea2cea90b775..b2097d011fc5e 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 3be48a8b72c3f..1686169fffbce 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index 4b9c1053f2289..2e20a0db1b876 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index ec78a62f066e2..a542e756c62e3 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index 24e44662e4016..0088d60f018e9 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index e26084cc43ee3..f030ee7319ec4 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false diff --git a/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.c b/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.c new file mode 100644 index 0000000000000..494b21cb02db9 --- /dev/null +++ b/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.c @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/hostnetwork/HostNetwork.h" + +hostnetwork_hostnetwork_obj_t common_hal_hostnetwork_obj = { + .base = { &hostnetwork_hostnetwork_type }, +}; + +void common_hal_hostnetwork_hostnetwork_construct(hostnetwork_hostnetwork_obj_t *self) { + (void)self; +} diff --git a/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.h b/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.h new file mode 100644 index 0000000000000..a6731546bdef1 --- /dev/null +++ b/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "bindings/hostnetwork/HostNetwork.h" + +extern hostnetwork_hostnetwork_obj_t common_hal_hostnetwork_obj; diff --git a/ports/zephyr-cp/common-hal/socketpool/Socket.c b/ports/zephyr-cp/common-hal/socketpool/Socket.c index 857526a12debd..ca8ba4198394a 100644 --- a/ports/zephyr-cp/common-hal/socketpool/Socket.c +++ b/ports/zephyr-cp/common-hal/socketpool/Socket.c @@ -20,165 +20,104 @@ #include "supervisor/shared/tick.h" #include "supervisor/workflow.h" -#include - -// void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port) { -// // struct addrinfo *result_i; -// // const struct addrinfo hints = { -// // .ai_family = family, -// // .ai_socktype = type, -// // }; -// // int error = socketpool_getaddrinfo_common(hostname, port, &hints, &result_i); -// if (true) { -// common_hal_socketpool_socketpool_raise_gaierror_noname(); -// } -// // memcpy(addr, result_i->ai_addr, sizeof(struct sockaddr_storage)); -// // lwip_freeaddrinfo(result_i); -// } - -// static void resolve_host_or_throw(socketpool_socket_obj_t *self, const char *hostname, struct sockaddr_storage *addr, int port) { -// socketpool_resolve_host_or_throw(self->family, self->type, hostname, addr, port); -// } - -// StackType_t socket_select_stack[2 * configMINIMAL_STACK_SIZE]; - -// /* Socket state table: -// * 0 := Closed (unused) -// * 1 := Open -// * 2 := Closing (remove from rfds) -// * Index into socket_fd_state is calculated from actual lwip fd. idx := fd - LWIP_SOCKET_OFFSET -// */ -// #define FDSTATE_CLOSED 0 -// #define FDSTATE_OPEN 1 -// #define FDSTATE_CLOSING 2 -// static uint8_t socket_fd_state[CONFIG_LWIP_MAX_SOCKETS]; +#include +#include +#include +#include +#include -// How long to wait between checks for a socket to connect. -#define SOCKET_CONNECT_POLL_INTERVAL_MS 100 +#include +#include -// static socketpool_socket_obj_t *user_socket[CONFIG_LWIP_MAX_SOCKETS]; -// StaticTask_t socket_select_task_buffer; -// TaskHandle_t socket_select_task_handle; -// static int socket_change_fd = -1; - -// static void socket_select_task(void *arg) { -// uint64_t signal; -// fd_set readfds; -// fd_set excptfds; - -// while (true) { -// FD_ZERO(&readfds); -// FD_ZERO(&excptfds); -// FD_SET(socket_change_fd, &readfds); -// int max_fd = socket_change_fd; -// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { -// if ((socket_fd_state[i] == FDSTATE_OPEN) && (user_socket[i] == NULL)) { -// int sockfd = i + LWIP_SOCKET_OFFSET; -// max_fd = MAX(max_fd, sockfd); -// FD_SET(sockfd, &readfds); -// FD_SET(sockfd, &excptfds); -// } -// } - -// int num_triggered = select(max_fd + 1, &readfds, NULL, &excptfds, NULL); -// // Hard error (or someone closed a socket on another thread) -// if (num_triggered == -1) { -// assert(errno == EBADF); -// continue; -// } - -// assert(num_triggered > 0); - -// // Notice event trigger -// if (FD_ISSET(socket_change_fd, &readfds)) { -// read(socket_change_fd, &signal, sizeof(signal)); -// num_triggered--; -// } - -// // Handle active FDs, close the dead ones -// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { -// int sockfd = i + LWIP_SOCKET_OFFSET; -// if (socket_fd_state[i] != FDSTATE_CLOSED) { -// if (FD_ISSET(sockfd, &readfds) || FD_ISSET(sockfd, &excptfds)) { -// if (socket_fd_state[i] == FDSTATE_CLOSING) { -// socket_fd_state[i] = FDSTATE_CLOSED; -// num_triggered--; -// } -// } -// } -// } - -// if (num_triggered > 0) { -// // Wake up CircuitPython by queuing request -// supervisor_workflow_request_background(); -// ulTaskNotifyTake(pdTRUE, portMAX_DELAY); -// } -// } - -// close(socket_change_fd); -// socket_change_fd = -1; -// vTaskDelete(NULL); -// } +#define SOCKETPOOL_IP_STR_LEN 48 -void socket_user_reset(void) { - // if (socket_change_fd < 0) { - // esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT(); - // ESP_ERROR_CHECK(esp_vfs_eventfd_register(&config)); - - // // Clear initial socket states - // for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { - // socket_fd_state[i] = FDSTATE_CLOSED; - // user_socket[i] = NULL; - // } - // socket_change_fd = eventfd(0, 0); - // // Run this at the same priority as CP so that the web workflow background task can be - // // queued while CP is running. Both tasks can still sleep and, therefore, sleep overall. - // socket_select_task_handle = xTaskCreateStaticPinnedToCore(socket_select_task, - // "socket_select", - // 2 * configMINIMAL_STACK_SIZE, - // NULL, - // uxTaskPriorityGet(NULL), - // socket_select_stack, - // &socket_select_task_buffer, - // xPortGetCoreID()); - // } else { - // // Not init - close open user sockets - // for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { - // if ((socket_fd_state[i] == FDSTATE_OPEN) && user_socket[i]) { - // common_hal_socketpool_socket_close(user_socket[i]); - // } - // } - // } +static mp_obj_t _format_address(const struct sockaddr *addr, int family) { + char ip_str[SOCKETPOOL_IP_STR_LEN]; + const struct sockaddr_in *a = (void *)addr; + + switch (family) { + #if CIRCUITPY_SOCKETPOOL_IPV6 + case AF_INET6: + zsock_inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str)); + break; + #endif + default: + case AF_INET: + zsock_inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str)); + break; + } + return mp_obj_new_str(ip_str, strlen(ip_str)); } -// Unblock select task (ok if not blocked yet) -void socketpool_socket_poll_resume(void) { - // if (socket_select_task_handle) { - // xTaskNotifyGive(socket_select_task_handle); - // } +static mp_obj_t _sockaddr_to_tuple(const struct sockaddr_storage *sockaddr) { + mp_obj_t args[4] = { + _format_address((const struct sockaddr *)sockaddr, sockaddr->ss_family), + }; + int n = 2; + #if CIRCUITPY_SOCKETPOOL_IPV6 + if (sockaddr->ss_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const void *)sockaddr; + args[1] = MP_OBJ_NEW_SMALL_INT(ntohs(addr6->sin6_port)); + args[2] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_flowinfo); + args[3] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_scope_id); + n = 4; + } else + #endif + { + const struct sockaddr_in *addr = (const void *)sockaddr; + args[1] = MP_OBJ_NEW_SMALL_INT(ntohs(addr->sin_port)); + } + return mp_obj_new_tuple(n, args); +} + +static void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port) { + const struct zsock_addrinfo hints = { + .ai_family = family, + .ai_socktype = type, + }; + struct zsock_addrinfo *result_i = NULL; + char service_buf[6]; + + snprintf(service_buf, sizeof(service_buf), "%d", port); + + int error = zsock_getaddrinfo(hostname, service_buf, &hints, &result_i); + if (error != 0 || result_i == NULL) { + common_hal_socketpool_socketpool_raise_gaierror_noname(); + } + + memcpy(addr, result_i->ai_addr, sizeof(struct sockaddr_storage)); + zsock_freeaddrinfo(result_i); } -// The writes below send an event to the socket select task so that it redoes the -// select with the new open socket set. +static void resolve_host_or_throw(socketpool_socket_obj_t *self, const char *hostname, struct sockaddr_storage *addr, int port) { + socketpool_resolve_host_or_throw(self->family, self->type, hostname, addr, port); +} -// static bool register_open_socket(int fd) { -// if (fd < FD_SETSIZE) { -// socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN; -// user_socket[fd - LWIP_SOCKET_OFFSET] = NULL; +// How long to wait between checks for a socket to connect. +#define SOCKET_CONNECT_POLL_INTERVAL_MS 100 -// uint64_t signal = 1; -// write(socket_change_fd, &signal, sizeof(signal)); -// socketpool_socket_poll_resume(); -// return true; -// } -// return false; -// } +void socket_user_reset(void) { + // User sockets are heap objects with __del__ bound to close(). + // During VM shutdown/reset, gc_sweep_all() runs finalizers, so sockets + // are closed there rather than being tracked and closed explicitly here. +} + +static struct k_work_delayable socketpool_poll_work; +static bool socketpool_poll_work_initialized; -// static void mark_user_socket(int fd, socketpool_socket_obj_t *obj) { -// socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN; -// user_socket[fd - LWIP_SOCKET_OFFSET] = obj; -// // No need to wakeup select task -// } +static void socketpool_poll_work_handler(struct k_work *work) { + (void)work; + supervisor_workflow_request_background(); +} + +// Unblock select task (ok if not blocked yet) +void socketpool_socket_poll_resume(void) { + if (!socketpool_poll_work_initialized) { + k_work_init_delayable(&socketpool_poll_work, socketpool_poll_work_handler); + socketpool_poll_work_initialized = true; + } + k_work_schedule(&socketpool_poll_work, K_MSEC(10)); +} static bool _socketpool_socket(socketpool_socketpool_obj_t *self, socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, @@ -212,16 +151,23 @@ static bool _socketpool_socket(socketpool_socketpool_obj_t *self, sock->pool = self; sock->timeout_ms = (uint)-1; - // Create LWIP socket - // int socknum = -1; - // socknum = lwip_socket(sock->family, sock->type, sock->ipproto); - // if (socknum < 0) { - // return false; - // } + int socknum = zsock_socket(sock->family, sock->type, sock->ipproto); + if (socknum < 0) { + return false; + } - // sock->num = socknum; - // // Sockets should be nonblocking in most cases - // lwip_fcntl(socknum, F_SETFL, O_NONBLOCK); + sock->num = socknum; + + // Enable address reuse by default to avoid bind failures on recently-used ports. + int reuseaddr = 1; + if (zsock_setsockopt(socknum, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) < 0) { + // Ignore if SO_REUSEADDR is unsupported. + } + + // Sockets should be nonblocking in most cases. + if (zsock_fcntl(socknum, F_SETFL, O_NONBLOCK) < 0) { + // Ignore if non-blocking is unsupported. + } return true; } @@ -235,11 +181,6 @@ bool socketpool_socket(socketpool_socketpool_obj_t *self, return false; } - // This shouldn't happen since we have room for the same number of sockets as LWIP. - // if (!register_open_socket(sock->num)) { - // lwip_close(sock->num); - // return false; - // } return true; } @@ -260,13 +201,12 @@ socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_ if (!_socketpool_socket(self, family, type, proto, sock)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Out of sockets")); } - // mark_user_socket(sock->num, sock); return sock; } int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, socketpool_socket_obj_t *accepted) { struct sockaddr_storage peer_addr; - // socklen_t socklen = sizeof(peer_addr); + socklen_t socklen = sizeof(peer_addr); int newsoc = -1; bool timed_out = false; uint64_t start_ticks = supervisor_ticks_ms64(); @@ -277,11 +217,23 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; } RUN_BACKGROUND_TASKS; - // newsoc = lwip_accept(self->num, (struct sockaddr *)&peer_addr, &socklen); + #if CIRCUITPY_HOSTNETWORK + if (self->timeout_ms == 0) { + struct zsock_timeval tv = { + .tv_sec = 0, + .tv_usec = 1000, + }; + zsock_setsockopt(self->num, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + #endif + newsoc = zsock_accept(self->num, (struct sockaddr *)&peer_addr, &socklen); // In non-blocking mode, fail instead of timing out if (newsoc == -1 && (self->timeout_ms == 0 || mp_hal_is_interrupted())) { return -MP_EAGAIN; } + if (newsoc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { + return -errno; + } } if (timed_out) { @@ -293,18 +245,14 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, } // We got a socket. New client socket will not be non-blocking by default, so make it non-blocking. - // lwip_fcntl(newsoc, F_SETFL, O_NONBLOCK); + if (zsock_fcntl(newsoc, F_SETFL, O_NONBLOCK) < 0) { + // Ignore if non-blocking is unsupported. + } if (accepted != NULL) { // Error if called with open socket object. assert(common_hal_socketpool_socket_get_closed(accepted)); - // Register if system socket - // if (!register_open_socket(newsoc)) { - // lwip_close(newsoc); - // return -MP_EBADF; - // } - // Replace the old accepted socket with the new one. accepted->num = newsoc; accepted->pool = self->pool; @@ -313,7 +261,7 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, } if (peer_out) { - *peer_out = sockaddr_to_tuple(&peer_addr); + *peer_out = _sockaddr_to_tuple(&peer_addr); } return newsoc; @@ -327,7 +275,6 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o if (newsoc > 0) { // Create the socket - // mark_user_socket(newsoc, sock); sock->base.type = &socketpool_socket_type; sock->num = newsoc; sock->pool = self->pool; @@ -343,40 +290,42 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o size_t common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port) { - // struct sockaddr_storage bind_addr; + struct sockaddr_storage bind_addr; const char *broadcast = ""; + uint32_t local_port = port; - // bind_addr.ss_family = self->family; + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.ss_family = self->family; #if CIRCUITPY_SOCKETPOOL_IPV6 if (self->family == AF_INET6) { struct sockaddr_in6 *addr6 = (void *)&bind_addr; - addr6->sin6_port = htons(port); + addr6->sin6_port = htons(local_port); // no ipv6 broadcast if (hostlen == 0) { memset(&addr6->sin6_addr, 0, sizeof(addr6->sin6_addr)); } else { - socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port); + socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, local_port); } } else #endif { - // struct sockaddr_in *addr4 = (void *)&bind_addr; - // addr4->sin_port = htons(port); + struct sockaddr_in *addr4 = (void *)&bind_addr; + addr4->sin_port = htons(local_port); if (hostlen == 0) { - // addr4->sin_addr.s_addr = IPADDR_ANY; + addr4->sin_addr.s_addr = htonl(INADDR_ANY); } else if (hostlen == strlen(broadcast) && memcmp(host, broadcast, strlen(broadcast)) == 0) { - // addr4->sin_addr.s_addr = IPADDR_BROADCAST; + addr4->sin_addr.s_addr = htonl(INADDR_BROADCAST); } else { - // socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port); + socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, local_port); } } - // int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); - // if (result == 0) { - // return 0; - // } + int result = zsock_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); + if (result == 0) { + return 0; + } return errno; } @@ -390,20 +339,11 @@ void socketpool_socket_close(socketpool_socket_obj_t *self) { } #endif self->connected = false; - // int fd = self->num; - // Ignore bogus/closed sockets - // if (fd >= LWIP_SOCKET_OFFSET) { - // if (user_socket[fd - LWIP_SOCKET_OFFSET] == NULL) { - // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSING; - // lwip_shutdown(fd, SHUT_RDWR); - // lwip_close(fd); - // } else { - // lwip_shutdown(fd, SHUT_RDWR); - // lwip_close(fd); - // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSED; - // user_socket[fd - LWIP_SOCKET_OFFSET] = NULL; - // } - // } + int fd = self->num; + if (fd >= 0) { + zsock_shutdown(fd, ZSOCK_SHUT_RDWR); + zsock_close(fd); + } self->num = -1; } @@ -413,16 +353,11 @@ void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) { void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port) { - // struct sockaddr_storage addr; - // resolve_host_or_throw(self, host, &addr, port); - - // Replace above with function call ----- - - // Emulate SO_CONTIMEO, which is not implemented by lwip. - // All our sockets are non-blocking, so we check the timeout ourselves. + (void)hostlen; + struct sockaddr_storage addr; + resolve_host_or_throw(self, host, &addr, port); - int result = -1; - // result = lwip_connect(self->num, (struct sockaddr *)&addr, addr.s2_len); + int result = zsock_connect(self->num, (struct sockaddr *)&addr, sizeof(addr)); if (result == 0) { // Connected immediately. @@ -436,12 +371,7 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, return; } - // struct timeval timeout = { - // .tv_sec = 0, - // .tv_usec = SOCKET_CONNECT_POLL_INTERVAL_MS * 1000, - // }; - - // Keep checking, using select(), until timeout expires, at short intervals. + // Keep checking, using poll(), until timeout expires, at short intervals. // This allows ctrl-C interrupts to be detected and background tasks to run. mp_uint_t timeout_left = self->timeout_ms; @@ -452,14 +382,22 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, return; } - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); + struct zsock_pollfd fd = { + .fd = self->num, + .events = ZSOCK_POLLOUT, + }; + int poll_timeout = SOCKET_CONNECT_POLL_INTERVAL_MS; + if (self->timeout_ms == (uint)-1) { + poll_timeout = -1; + } else if (timeout_left < SOCKET_CONNECT_POLL_INTERVAL_MS) { + poll_timeout = timeout_left; + } - // result = select(self->num + 1, NULL, &fds, NULL, &timeout); + result = zsock_poll(&fd, 1, poll_timeout); if (result == 0) { - // No change to fd's after waiting for timeout, so try again if some time is still left. - // Don't wrap below 0, because we're using a uint. + if (self->timeout_ms == (uint)-1) { + continue; + } if (timeout_left < SOCKET_CONNECT_POLL_INTERVAL_MS) { timeout_left = 0; } else { @@ -469,16 +407,14 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, } if (result < 0) { - // Some error happened when doing select(); error is in errno. mp_raise_OSError(errno); } - // select() indicated the socket is writable. Check if any connection error occurred. int error_code = 0; - // socklen_t socklen = sizeof(error_code); - // result = getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); + socklen_t socklen = sizeof(error_code); + result = zsock_getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); if (result < 0 || error_code != 0) { - mp_raise_OSError(errno); + mp_raise_OSError(error_code != 0 ? error_code : errno); } self->connected = true; return; @@ -498,17 +434,15 @@ bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *self) { } bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog) { - // return lwip_listen(self->num, backlog) == 0; - return false; + return zsock_listen(self->num, backlog) == 0; } mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len, mp_obj_t *source_out) { - // struct sockaddr_storage source_addr; - // socklen_t socklen = sizeof(source_addr); + struct sockaddr_storage source_addr; + socklen_t socklen = sizeof(source_addr); - // LWIP Socket uint64_t start_ticks = supervisor_ticks_ms64(); int received = -1; bool timed_out = false; @@ -519,11 +453,18 @@ mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *se timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; } RUN_BACKGROUND_TASKS; - // received = lwip_recvfrom(self->num, buf, len, 0, (struct sockaddr *)&source_addr, &socklen); + received = zsock_recvfrom(self->num, buf, len, ZSOCK_MSG_DONTWAIT, (struct sockaddr *)&source_addr, &socklen); - // In non-blocking mode, fail instead of looping - if (received == -1 && self->timeout_ms == 0) { - mp_raise_OSError(MP_EAGAIN); + if (received < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + mp_raise_OSError(errno); + } + + if (received == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // In non-blocking mode, fail instead of looping + if (self->timeout_ms == 0) { + mp_raise_OSError(MP_EAGAIN); + } + continue; } } @@ -537,7 +478,7 @@ mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *se } if (source_out) { - // *source_out = sockaddr_to_tuple(&source_addr); + *source_out = _sockaddr_to_tuple(&source_addr); } return received; @@ -549,7 +490,6 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *self, bool timed_out = false; if (self->num != -1) { - // LWIP Socket uint64_t start_ticks = supervisor_ticks_ms64(); received = -1; while (received == -1 && @@ -558,7 +498,16 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *self, timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; } RUN_BACKGROUND_TASKS; - // received = lwip_recv(self->num, (void *)buf, len, 0); + received = zsock_recv(self->num, (void *)buf, len, ZSOCK_MSG_DONTWAIT); + if (received < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + return -errno; + } + if (received < 1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + if (self->timeout_ms == 0) { + return -MP_EAGAIN; + } + continue; + } // In non-blocking mode, fail instead of looping if (received < 1 && self->timeout_ms == 0) { if ((received == 0) || (errno == ENOTCONN)) { @@ -597,9 +546,7 @@ mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, int socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { int sent = -1; if (self->num != -1) { - // LWIP Socket - // TODO: deal with potential failure/add timeout? - // sent = lwip_send(self->num, buf, len, 0); + sent = zsock_send(self->num, buf, len, 0); } else { sent = -MP_EBADF; } @@ -626,15 +573,15 @@ mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const mp_uint_t common_hal_socketpool_socket_sendto(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port, const uint8_t *buf, uint32_t len) { - // struct sockaddr_storage addr; - // resolve_host_or_throw(self, host, &addr, port); + (void)hostlen; + struct sockaddr_storage addr; + resolve_host_or_throw(self, host, &addr, port); - // int bytes_sent = lwip_sendto(self->num, buf, len, 0, (struct sockaddr *)&addr, addr.s2_len); - // if (bytes_sent < 0) { - // mp_raise_BrokenPipeError(); - // return 0; - // } - int bytes_sent = 0; + int bytes_sent = zsock_sendto(self->num, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)); + if (bytes_sent < 0) { + mp_raise_BrokenPipeError(); + return 0; + } return bytes_sent; } @@ -648,7 +595,44 @@ mp_int_t common_hal_socketpool_socket_get_type(socketpool_socket_obj_t *self) { int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen) { - int err = 0; // lwip_setsockopt(self->num, level, optname, value, optlen); + int zephyr_level = level; + int zephyr_optname = optname; + + switch (level) { + case SOCKETPOOL_SOL_SOCKET: + zephyr_level = SOL_SOCKET; + break; + case SOCKETPOOL_IPPROTO_IP: + zephyr_level = IPPROTO_IP; + break; + case SOCKETPOOL_IPPROTO_TCP: + zephyr_level = IPPROTO_TCP; + break; + case SOCKETPOOL_IPPROTO_UDP: + zephyr_level = IPPROTO_UDP; + break; + #if CIRCUITPY_SOCKETPOOL_IPV6 + case SOCKETPOOL_IPPROTO_IPV6: + zephyr_level = IPPROTO_IPV6; + break; + #endif + } + + if (zephyr_level == SOL_SOCKET) { + switch (optname) { + case SOCKETPOOL_SO_REUSEADDR: + zephyr_optname = SO_REUSEADDR; + break; + } + } else if (zephyr_level == IPPROTO_TCP) { + switch (optname) { + case SOCKETPOOL_TCP_NODELAY: + zephyr_optname = TCP_NODELAY; + break; + } + } + + int err = zsock_setsockopt(self->num, zephyr_level, zephyr_optname, value, optlen); if (err != 0) { return -errno; } @@ -656,29 +640,23 @@ int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int l } bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) { - // struct timeval immediate = {0, 0}; + struct zsock_pollfd fd = { + .fd = self->num, + .events = ZSOCK_POLLIN, + }; - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); - // int num_triggered = select(self->num + 1, &fds, NULL, &fds, &immediate); - - // including returning true in the error case - // return num_triggered != 0; - return false; + int num_triggered = zsock_poll(&fd, 1, 0); + return num_triggered > 0; } bool common_hal_socketpool_writable(socketpool_socket_obj_t *self) { - // struct timeval immediate = {0, 0}; - - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); - // int num_triggered = select(self->num + 1, NULL, &fds, &fds, &immediate); + struct zsock_pollfd fd = { + .fd = self->num, + .events = ZSOCK_POLLOUT, + }; - // including returning true in the error case - // return num_triggered != 0; - return false; + int num_triggered = zsock_poll(&fd, 1, 0); + return num_triggered > 0; } void socketpool_socket_move(socketpool_socket_obj_t *self, socketpool_socket_obj_t *sock) { diff --git a/ports/zephyr-cp/common-hal/socketpool/SocketPool.c b/ports/zephyr-cp/common-hal/socketpool/SocketPool.c index 4531c5bf1b7fa..c9a3f008321a1 100644 --- a/ports/zephyr-cp/common-hal/socketpool/SocketPool.c +++ b/ports/zephyr-cp/common-hal/socketpool/SocketPool.c @@ -8,114 +8,116 @@ #include "common-hal/socketpool/Socket.h" #include "py/runtime.h" +#if CIRCUITPY_HOSTNETWORK +#include "bindings/hostnetwork/__init__.h" +#endif +#if CIRCUITPY_WIFI #include "shared-bindings/wifi/__init__.h" +#endif #include "common-hal/socketpool/__init__.h" +#include +#include + +#include + void common_hal_socketpool_socketpool_construct(socketpool_socketpool_obj_t *self, mp_obj_t radio) { - if (radio != MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj)) { - mp_raise_ValueError(MP_ERROR_TEXT("SocketPool can only be used with wifi.radio")); + bool is_wifi = false; + #if CIRCUITPY_WIFI + is_wifi = radio == MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj); + #endif + bool is_hostnetwork = false; + #if CIRCUITPY_HOSTNETWORK + is_hostnetwork = mp_obj_is_type(radio, &hostnetwork_hostnetwork_type); + #endif + if (!(is_wifi || is_hostnetwork)) { + mp_raise_ValueError(MP_ERROR_TEXT("SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork")); } } // common_hal_socketpool_socket is in socketpool/Socket.c to centralize open socket tracking. -// int socketpool_getaddrinfo_common(const char *host, int service, const struct addrinfo *hints, struct addrinfo **res) { -// // As of 2022, the version of lwip in esp-idf does not handle the -// // trailing-dot syntax of domain names, so emulate it. -// // Remove this once https://github.com/espressif/esp-idf/issues/10013 has -// // been implemented -// if (host) { -// size_t strlen_host = strlen(host); -// if (strlen_host && host[strlen_host - 1] == '.') { -// mp_obj_t nodot = mp_obj_new_str(host, strlen_host - 1); -// host = mp_obj_str_get_str(nodot); -// } -// } - -// // char service_buf[6]; -// // snprintf(service_buf, sizeof(service_buf), "%d", service); - -// // return lwip_getaddrinfo(host, service_buf, hints, res); -// return 0; -// } - -// static mp_obj_t format_address(const struct sockaddr *addr, int family) { -// char ip_str[IPADDR_STRLEN_MAX]; // big enough for any supported address type -// const struct sockaddr_in *a = (void *)addr; - -// switch (family) { -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// case AF_INET6: -// inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str)); -// break; -// #endif -// default: -// case AF_INET: -// inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str)); -// break; -// } -// return mp_obj_new_str(ip_str, strlen(ip_str)); -// } - -// static mp_obj_t convert_sockaddr(const struct addrinfo *ai, int port) { -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// mp_int_t n_tuple = ai->ai_family == AF_INET6 ? 4 : 2; -// #else -// mp_int_t n_tuple = 2; -// #endif -// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_tuple, NULL)); -// result->items[0] = format_address(ai->ai_addr, ai->ai_family); -// result->items[1] = MP_OBJ_NEW_SMALL_INT(port); -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// if (ai->ai_family == AF_INET6) { -// const struct sockaddr_in6 *ai6 = (void *)ai->ai_addr; -// result->items[2] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_flowinfo); -// result->items[3] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_scope_id); -// } -// #endif -// return result; -// } - -// static mp_obj_t convert_addrinfo(const struct addrinfo *ai, int port) { -// MP_STATIC_ASSERT(AF_INET == SOCKETPOOL_AF_INET); -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// MP_STATIC_ASSERT(AF_INET6 == SOCKETPOOL_AF_INET6); -// #endif -// // MP_STATIC_ASSERT(AF_UNSPEC == SOCKETPOOL_AF_UNSPEC); -// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); -// result->items[0] = MP_OBJ_NEW_SMALL_INT(ai->ai_family); -// result->items[1] = MP_OBJ_NEW_SMALL_INT(ai->ai_socktype); -// result->items[2] = MP_OBJ_NEW_SMALL_INT(ai->ai_protocol); -// result->items[3] = ai->ai_canonname ? mp_obj_new_str(ai->ai_canonname, strlen(ai->ai_canonname)) : MP_OBJ_NEW_QSTR(MP_QSTR_); -// result->items[4] = convert_sockaddr(ai, port); -// return result; -// } +static int socketpool_getaddrinfo_common(const char *host, int service, const struct zsock_addrinfo *hints, struct zsock_addrinfo **res) { + char service_buf[6]; + snprintf(service_buf, sizeof(service_buf), "%d", service); + + return zsock_getaddrinfo(host, service_buf, hints, res); +} + +#define SOCKETPOOL_IP_STR_LEN 48 + +static mp_obj_t format_address(const struct sockaddr *addr, int family) { + char ip_str[SOCKETPOOL_IP_STR_LEN]; + const struct sockaddr_in *a = (void *)addr; + + switch (family) { + #if CIRCUITPY_SOCKETPOOL_IPV6 + case AF_INET6: + zsock_inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str)); + break; + #endif + default: + case AF_INET: + zsock_inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str)); + break; + } + return mp_obj_new_str(ip_str, strlen(ip_str)); +} + +static mp_obj_t convert_sockaddr(const struct zsock_addrinfo *ai, int port) { + #if CIRCUITPY_SOCKETPOOL_IPV6 + mp_int_t n_tuple = ai->ai_family == AF_INET6 ? 4 : 2; + #else + mp_int_t n_tuple = 2; + #endif + mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_tuple, NULL)); + result->items[0] = format_address(ai->ai_addr, ai->ai_family); + result->items[1] = MP_OBJ_NEW_SMALL_INT(port); + #if CIRCUITPY_SOCKETPOOL_IPV6 + if (ai->ai_family == AF_INET6) { + const struct sockaddr_in6 *ai6 = (void *)ai->ai_addr; + result->items[2] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_flowinfo); + result->items[3] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_scope_id); + } + #endif + return result; +} + +static mp_obj_t convert_addrinfo(const struct zsock_addrinfo *ai, int port) { + mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); + result->items[0] = MP_OBJ_NEW_SMALL_INT(ai->ai_family); + result->items[1] = MP_OBJ_NEW_SMALL_INT(ai->ai_socktype); + result->items[2] = MP_OBJ_NEW_SMALL_INT(ai->ai_protocol); + result->items[3] = ai->ai_canonname ? mp_obj_new_str(ai->ai_canonname, strlen(ai->ai_canonname)) : MP_OBJ_NEW_QSTR(MP_QSTR_); + result->items[4] = convert_sockaddr(ai, port); + return result; +} mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *self, const char *host, int port, int family, int type, int proto, int flags) { - // const struct addrinfo hints = { - // .ai_flags = flags, - // .ai_family = family, - // .ai_protocol = proto, - // .ai_socktype = type, - // }; - - // struct addrinfo *res = NULL; - // int err = socketpool_getaddrinfo_common(host, port, &hints, &res); - if (true) { + const struct zsock_addrinfo hints = { + .ai_flags = flags, + .ai_family = family, + .ai_protocol = proto, + .ai_socktype = type, + }; + + struct zsock_addrinfo *res = NULL; + int err = socketpool_getaddrinfo_common(host, port, &hints, &res); + if (err != 0 || res == NULL) { common_hal_socketpool_socketpool_raise_gaierror_noname(); } nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t result = mp_obj_new_list(0, NULL); - // for (struct addrinfo *ai = res; ai; ai = ai->ai_next) { - // mp_obj_list_append(result, convert_addrinfo(ai, port)); - // } + for (struct zsock_addrinfo *ai = res; ai; ai = ai->ai_next) { + mp_obj_list_append(result, convert_addrinfo(ai, port)); + } nlr_pop(); - // lwip_freeaddrinfo(res); + zsock_freeaddrinfo(res); return result; } else { - // lwip_freeaddrinfo(res); + zsock_freeaddrinfo(res); nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val)); } } diff --git a/ports/zephyr-cp/common-hal/socketpool/SocketPool.h b/ports/zephyr-cp/common-hal/socketpool/SocketPool.h index 64f91e01e1cf1..7891a8b8e4820 100644 --- a/ports/zephyr-cp/common-hal/socketpool/SocketPool.h +++ b/ports/zephyr-cp/common-hal/socketpool/SocketPool.h @@ -6,6 +6,9 @@ #pragma once +#include +#include + #include "py/obj.h" typedef struct { diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 3a007661c77d9..51521e913ab5d 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -224,6 +224,20 @@ async def generate_display_resources(output_path, translation, font, extra_chara ) +async def generate_web_workflow_static(output_path, static_files): + await cpbuild.run_command( + [ + "python", + srcdir / "tools" / "gen_web_workflow_static.py", + "--output_c_file", + output_path, + *static_files, + ], + srcdir, + check_hash=[output_path], + ) + + def determine_enabled_modules(board_info, portdir, srcdir): """Determine which CircuitPython modules should be enabled based on board capabilities. @@ -246,10 +260,16 @@ def determine_enabled_modules(board_info, portdir, srcdir): enabled_modules.add("storage") module_reasons["storage"] = "Zephyr board has flash" - if "wifi" in enabled_modules: + network_enabled = board_info.get("wifi", False) or board_info.get("hostnetwork", False) + + if network_enabled: enabled_modules.add("socketpool") - enabled_modules.add("ssl") module_reasons["socketpool"] = "Zephyr networking enabled" + enabled_modules.add("hashlib") + module_reasons["hashlib"] = "Zephyr networking enabled" + + if board_info.get("wifi", False) or board_info.get("ethernet", False): + enabled_modules.add("ssl") module_reasons["ssl"] = "Zephyr networking enabled" for port_module in (portdir / "bindings").iterdir(): @@ -296,6 +316,9 @@ async def build_circuitpython(): lto = cmake_args.get("LTO", "n") == "y" circuitpython_flags.append(f"-DCIRCUITPY_ENABLE_MPY_NATIVE={1 if enable_mpy_native else 0}") circuitpython_flags.append(f"-DCIRCUITPY_FULL_BUILD={1 if full_build else 0}") + circuitpython_flags.append(f"-DCIRCUITPY_SETTINGS_TOML={1 if full_build else 0}") + circuitpython_flags.append(f"-DCIRCUITPY_OS_GETENV={1 if full_build else 0}") + circuitpython_flags.append("-DCIRCUITPY_STATUS_BAR=1") circuitpython_flags.append(f"-DCIRCUITPY_USB_HOST={1 if usb_host else 0}") circuitpython_flags.append(f"-DCIRCUITPY_BOARD_ID='\"{board}\"'") circuitpython_flags.append(f"-DCIRCUITPY_TRANSLATE_OBJECT={1 if lto else 0}") @@ -352,7 +375,10 @@ async def build_circuitpython(): enabled_modules, module_reasons = determine_enabled_modules(board_info, portdir, srcdir) + web_workflow_enabled = board_info.get("wifi", False) or board_info.get("hostnetwork", False) + circuitpython_flags.extend(board_info["cflags"]) + circuitpython_flags.append(f"-DCIRCUITPY_WEB_WORKFLOW={1 if web_workflow_enabled else 0}") supervisor_source = [ "main.c", "extmod/modjson.c", @@ -387,8 +413,8 @@ async def build_circuitpython(): supervisor_source.extend(top.glob("supervisor/shared/*.c")) supervisor_source.append(top / "supervisor/shared/bluetooth/bluetooth.c") supervisor_source.append(top / "supervisor/shared/translate/translate.c") - # if web_workflow: - # supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c")) + if web_workflow_enabled: + supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c")) usb_ok = board_info.get("usb_device", False) circuitpython_flags.append(f"-DCIRCUITPY_USB_DEVICE={1 if usb_ok else 0}") @@ -416,10 +442,22 @@ async def build_circuitpython(): (portdir / "supervisor/usb.c", srcdir / "supervisor/shared/usb.c") ) + creator_id = mpconfigboard.get("CIRCUITPY_CREATOR_ID", mpconfigboard.get("USB_VID")) + creation_id = mpconfigboard.get("CIRCUITPY_CREATION_ID", mpconfigboard.get("USB_PID")) + if creator_id is not None: + circuitpython_flags.append(f"-DCIRCUITPY_CREATOR_ID=0x{creator_id:08x}") + if creation_id is not None: + circuitpython_flags.append(f"-DCIRCUITPY_CREATION_ID=0x{creation_id:08x}") + # Always use port serial. It'll switch between USB and UART automatically. circuitpython_flags.append("-DCIRCUITPY_PORT_SERIAL=1") - if "ssl" in enabled_modules: + if "hashlib" in enabled_modules: + circuitpython_flags.append("-DCIRCUITPY_HASHLIB_MBEDTLS=1") + if "ssl" not in enabled_modules: + circuitpython_flags.append("-DCIRCUITPY_HASHLIB_MBEDTLS_ONLY=1") + + if "ssl" in enabled_modules or "hashlib" in enabled_modules: # TODO: Figure out how to get these paths from zephyr circuitpython_flags.append("-DMBEDTLS_CONFIG_FILE='\"config-mbedtls.h\"'") circuitpython_flags.extend( @@ -432,7 +470,8 @@ async def build_circuitpython(): ("-isystem", portdir / "modules" / "crypto" / "mbedtls" / "include") ) circuitpython_flags.extend(("-isystem", zephyrdir / "modules" / "mbedtls" / "configs")) - supervisor_source.append(top / "lib" / "mbedtls_config" / "crt_bundle.c") + if "ssl" in enabled_modules: + supervisor_source.append(top / "lib" / "mbedtls_config" / "crt_bundle.c") # Make sure all modules have a setting by filling in defaults. hal_source = [] @@ -549,6 +588,12 @@ async def build_circuitpython(): ) source_files.append(output_path) + if web_workflow_enabled: + output_path = board_build / "autogen_web_workflow_static.c" + static_files = sorted((srcdir / "supervisor/shared/web_workflow/static").glob("*")) + tg.create_task(generate_web_workflow_static(output_path, static_files)) + source_files.append(output_path) + # This file is generated by the QSTR/translation process. source_files.append(builddir / f"translations-{translation}.c") # These files don't include unique QSTRs. They just need to be compiled. diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 86b0b0fe40386..a891069b3156d 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -395,6 +395,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa "wifi": False, "usb_device": False, "_bleio": False, + "hostnetwork": board_id in ["native_sim"], } config_bt_enabled = False @@ -746,11 +747,25 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa pins.write_text(pin_declarations) board_c = board_dir / "board.c" + hostnetwork_include = "" + hostnetwork_entry = "" + if board_info.get("hostnetwork", False): + hostnetwork_include = ( + '#if CIRCUITPY_HOSTNETWORK\n#include "bindings/hostnetwork/__init__.h"\n#endif\n' + ) + hostnetwork_entry = ( + "#if CIRCUITPY_HOSTNETWORK\n" + " { MP_ROM_QSTR(MP_QSTR_NETWORK), MP_ROM_PTR(&common_hal_hostnetwork_obj) },\n" + "#endif\n" + ) + new_board_c_content = f""" // This file is autogenerated by build_circuitpython.py #include "shared-bindings/board/__init__.h" +{hostnetwork_include} + #include #include "py/obj.h" @@ -779,6 +794,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa static const mp_rom_map_elem_t board_module_globals_table[] = {{ CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS +{hostnetwork_entry} {board_pin_mapping} {zephyr_binding_labels} diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 121de2c4ae52d..d0f4d911c123b 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -1,4 +1,4 @@ -CONFIG_SYS_HEAP_RUNTIME_STATS=y +CONFIG_SYS_HEAP_RUNTIME_STATS=n CONFIG_FLASH=y CONFIG_FLASH_MAP=y CONFIG_STD_C23=y @@ -12,10 +12,10 @@ CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_THREAD_STACK_INFO=y -CONFIG_STACK_SENTINEL=y -CONFIG_DEBUG_THREAD_INFO=y -CONFIG_DEBUG_INFO=y -CONFIG_EXCEPTION_STACK_TRACE=y +CONFIG_STACK_SENTINEL=n +CONFIG_DEBUG_THREAD_INFO=n +CONFIG_DEBUG_INFO=n +CONFIG_EXCEPTION_STACK_TRACE=n CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USBD_CDC_ACM_CLASS=y @@ -30,8 +30,8 @@ CONFIG_HWINFO=y CONFIG_REBOOT=y CONFIG_ENTROPY_GENERATOR=y -CONFIG_ASSERT=y -CONFIG_LOG_BLOCK_IN_THREAD=y +CONFIG_ASSERT=n +CONFIG_LOG_BLOCK_IN_THREAD=n CONFIG_EVENTS=y @@ -45,7 +45,7 @@ CONFIG_SPI_ASYNC=y CONFIG_LOG=y CONFIG_LOG_MAX_LEVEL=2 CONFIG_HW_STACK_PROTECTION=y -CONFIG_FRAME_POINTER=y +CONFIG_FRAME_POINTER=n CONFIG_BT_BUF_ACL_TX_COUNT=7 CONFIG_BT_HCI_ERR_TO_STR=y diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py index 0bc296cf43feb..7c7eb6ae34994 100644 --- a/ports/zephyr-cp/tests/conftest.py +++ b/ports/zephyr-cp/tests/conftest.py @@ -42,6 +42,10 @@ def pytest_configure(config): "markers", "code_py_runs(count): stop native_sim after count code.py runs (default: 1)", ) + config.addinivalue_line( + "markers", + "native_sim_rt: run native_sim in realtime mode (-rt instead of -no-rt)", + ) ZEPHYR_CP = Path(__file__).parent.parent @@ -190,6 +194,8 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp else: code_py_runs = int(runs_marker.args[0]) + use_realtime = request.node.get_closest_marker("native_sim_rt") is not None + if "bsim" in board: cmd = [str(native_sim_binary), f"--flash_app={flash}"] if instance_count > 1: @@ -207,7 +213,8 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp else: cmd = [str(native_sim_binary), f"--flash={flash}"] # native_sim vm-runs includes the boot VM setup run. - cmd.extend(("-no-rt", "-wait_uart", f"--vm-runs={code_py_runs + 1}")) + realtime_flag = "-rt" if use_realtime else "-no-rt" + cmd.extend((realtime_flag, "-wait_uart", f"--vm-runs={code_py_runs + 1}")) marker = request.node.get_closest_marker("disable_i2c_devices") if marker and len(marker.args) > 0: diff --git a/ports/zephyr-cp/tests/docs/web_workflow.md b/ports/zephyr-cp/tests/docs/web_workflow.md new file mode 100644 index 0000000000000..eb6742b8a3ec6 --- /dev/null +++ b/ports/zephyr-cp/tests/docs/web_workflow.md @@ -0,0 +1,37 @@ +# Web Workflow native_sim Tests + +These tests validate CircuitPython's web workflow support in the Zephyr native_sim port, including filesystem write behavior with and without USB-style write protection. + +## Coverage + +- `test_web_workflow_hostnetwork`: Verifies the web workflow HTTP server responds and enforces authentication (`/edit/` returns `401 Unauthorized`). +- `test_web_workflow_write_code_py_conflict`: Exercises a write attempt while the filesystem is protected (no `boot.py` remount). The DELETE request should return `409 Conflict`. +- `test_web_workflow_write_code_py_remount`: Uses a `boot.py` remount to allow CircuitPython to write. A PUT request updates `code.py`, and a subsequent GET verifies the contents. + +## Filesystem Setup + +The tests create a flash image with: + +- `settings.toml` containing `CIRCUITPY_WEB_API_PASSWORD="testpass"` so the web workflow starts using the on-device settings file. +- `boot.py` (for the remount test only) with: + ```python + import storage + storage.remount("/", readonly=False) + ``` + This disables concurrent write protection so the web workflow can write to CIRCUITPY. + +## Running the Tests + +Build native_sim (if needed): + +```bash +make BOARD=native_native_sim +``` + +Run the tests: + +```bash +pytest -q ports/zephyr-cp/tests/test_web_workflow.py::test_web_workflow_hostnetwork -vv +pytest -q ports/zephyr-cp/tests/test_web_workflow.py::test_web_workflow_write_code_py_conflict -vv +pytest -q ports/zephyr-cp/tests/test_web_workflow.py::test_web_workflow_write_code_py_remount -vv +``` diff --git a/ports/zephyr-cp/tests/test_web_workflow.py b/ports/zephyr-cp/tests/test_web_workflow.py new file mode 100644 index 0000000000000..d3a00782a333e --- /dev/null +++ b/ports/zephyr-cp/tests/test_web_workflow.py @@ -0,0 +1,142 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Tests for web workflow on native_sim.""" + +from __future__ import annotations + +import json +import re + +import pytest +import requests + + +pytestmark = pytest.mark.native_sim_rt + +WEB_WORKFLOW_PORT = 8090 +WEB_WORKFLOW_PASSWORD = "testpass" + +WEB_WORKFLOW_CODE = """\ +import time + +# Keep the VM alive while the web workflow starts. +time.sleep(3) +""" + +WEB_WORKFLOW_UPDATED_CODE = """\ +print("updated") +""" + +WEB_WORKFLOW_SETTINGS = f"""\ +CIRCUITPY_WEB_API_PASSWORD="{WEB_WORKFLOW_PASSWORD}" +CIRCUITPY_WEB_API_PORT={WEB_WORKFLOW_PORT} +""" + +WEB_WORKFLOW_SETTINGS_PORT_80 = f"""\ +CIRCUITPY_WEB_API_PASSWORD="{WEB_WORKFLOW_PASSWORD}" +CIRCUITPY_WEB_API_PORT=80 +""" + +WEB_WORKFLOW_BOOT = """\ +import storage + +storage.remount("/", readonly=False) +""" + + +@pytest.mark.circuitpy_drive( + { + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS, + } +) +def test_web_workflow_hostnetwork(circuitpython): + """Ensure web workflow responds over hostnetwork.""" + circuitpython.serial.wait_for(f"127.0.0.1:{WEB_WORKFLOW_PORT}") + response = requests.get(f"http://127.0.0.1:{WEB_WORKFLOW_PORT}/edit/", timeout=1.0) + + assert response.status_code == 401 + + +@pytest.mark.circuitpy_drive( + { + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS, + } +) +def test_web_workflow_version_json_hostnetwork_ip_and_port(circuitpython): + """Ensure /cp/version.json reports hostnetwork endpoint with configured port.""" + circuitpython.serial.wait_for(f"127.0.0.1:{WEB_WORKFLOW_PORT}") + response = requests.get( + f"http://127.0.0.1:{WEB_WORKFLOW_PORT}/cp/version.json", + auth=("", WEB_WORKFLOW_PASSWORD), + timeout=1.0, + ) + + assert response.status_code == 200 + + payload = json.loads(response.text) + assert payload["ip"] == "127.0.0.1" + assert payload["port"] == WEB_WORKFLOW_PORT + + +@pytest.mark.circuitpy_drive( + { + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS, + } +) +def test_web_workflow_status_line_hostnetwork_non_default_port(circuitpython): + """Status line should include hostnetwork IP and non-default port.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + + # Remove ANSI control sequences before matching. + output = re.sub(r"\x1b\[[0-9;]*[A-Za-z]", "", output) + assert "127.0.0.1:8090" in output + + +@pytest.mark.circuitpy_drive( + { + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS_PORT_80, + } +) +def test_web_workflow_status_line_hostnetwork_default_port(circuitpython): + """Status line should show IP without :80 for default HTTP port.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + + output = re.sub(r"\x1b\[[0-9;]*[A-Za-z]", "", output) + assert "127.0.0.1" in output + assert "127.0.0.1:80" not in output + + +@pytest.mark.circuitpy_drive( + { + "boot.py": WEB_WORKFLOW_BOOT, + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS, + } +) +def test_web_workflow_write_code_py_remount(circuitpython): + """Ensure web workflow can update code.py after remounting.""" + circuitpython.serial.wait_for(f"127.0.0.1:{WEB_WORKFLOW_PORT}") + body = WEB_WORKFLOW_UPDATED_CODE.encode("utf-8") + + response = requests.put( + f"http://127.0.0.1:{WEB_WORKFLOW_PORT}/fs/code.py", + auth=("", WEB_WORKFLOW_PASSWORD), + data=body, + timeout=1.0, + ) + assert response.status_code in (201, 204) + + response = requests.get( + f"http://127.0.0.1:{WEB_WORKFLOW_PORT}/fs/code.py", + auth=("", WEB_WORKFLOW_PASSWORD), + timeout=1.0, + ) + assert response.status_code == 200 + assert WEB_WORKFLOW_UPDATED_CODE in response.text diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 56fc695742133..a1297a4754435 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -346,6 +346,9 @@ CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS=$(CIRCUITPY_HASHLIB_MBEDTLS) CIRCUITPY_HASHLIB_MBEDTLS_ONLY ?= $(call enable-if-all,$(CIRCUITPY_HASHLIB_MBEDTLS) $(call enable-if-not,$(CIRCUITPY_SSL))) CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS_ONLY=$(CIRCUITPY_HASHLIB_MBEDTLS_ONLY) +# Always zero because it is for Zephyr only +CFLAGS += -DCIRCUITPY_HOSTNETWORK=0 + CIRCUITPY_I2CTARGET ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_I2CTARGET=$(CIRCUITPY_I2CTARGET) diff --git a/shared-bindings/socketpool/SocketPool.c b/shared-bindings/socketpool/SocketPool.c index e139e3a077ab2..06b63b80ea74d 100644 --- a/shared-bindings/socketpool/SocketPool.c +++ b/shared-bindings/socketpool/SocketPool.c @@ -24,12 +24,11 @@ //| a pool of sockets provided by the underlying OS. //| """ //| -//| def __init__(self, radio: wifi.Radio) -> None: +//| def __init__(self, radio: Union[wifi.Radio, hostnetwork.HostNetwork]) -> None: //| """Create a new SocketPool object for the provided radio //| -//| :param wifi.Radio radio: The (connected) network hardware to associate -//| with this SocketPool; currently, this will always be the object -//| returned by :py:attr:`wifi.radio` +//| :param radio: The (connected) network interface to associate with this +//| SocketPool, such as :py:attr:`wifi.radio` or :py:attr:`board.NETWORK`. //| """ //| ... //| diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 74e4fed57592c..55b7de8df6340 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -8,6 +8,8 @@ #define _GNU_SOURCE #include +#include +#include #include #include "extmod/vfs.h" @@ -42,6 +44,10 @@ #include "shared-bindings/socketpool/Socket.h" #include "shared-bindings/socketpool/SocketPool.h" +#if CIRCUITPY_HOSTNETWORK +#include "bindings/hostnetwork/__init__.h" +#endif + #if CIRCUITPY_WIFI #include "shared-bindings/wifi/__init__.h" #endif @@ -84,12 +90,18 @@ typedef struct { char websocket_key[24 + 1]; } _request; +#if CIRCUITPY_WIFI static wifi_radio_error_t _wifi_status = WIFI_RADIO_ERROR_NONE; +#endif -#if CIRCUITPY_STATUS_BAR +#if CIRCUITPY_STATUS_BAR && (CIRCUITPY_WIFI || CIRCUITPY_HOSTNETWORK) // Store various last states to compute if status bar needs an update. static bool _last_enabled = false; static uint32_t _last_ip = 0; +static mp_int_t _last_web_api_port = 80; +#endif + +#if CIRCUITPY_STATUS_BAR && CIRCUITPY_WIFI static wifi_radio_error_t _last_wifi_status = WIFI_RADIO_ERROR_NONE; #endif @@ -171,11 +183,7 @@ static bool _base64_in_place(char *buf, size_t in_len, size_t out_len) { return true; } -static void _update_encoded_ip(void) { - uint32_t ipv4_address = 0; - if (common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) { - ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj); - } +static void _update_encoded_ip(uint32_t ipv4_address) { if (_encoded_ip != ipv4_address) { uint8_t *octets = (uint8_t *)&ipv4_address; snprintf(_our_ip_encoded, sizeof(_our_ip_encoded), "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]); @@ -183,66 +191,118 @@ static void _update_encoded_ip(void) { } } +static bool _get_web_workflow_ip(uint32_t *ipv4_address) { + *ipv4_address = 0; + #if CIRCUITPY_WIFI + if (!common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) { + return false; + } + *ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj); + return true; + #elif CIRCUITPY_HOSTNETWORK + // hostnetwork uses the host network namespace and is reachable via localhost. + *ipv4_address = 0x0100007f; // 127.0.0.1 + return true; + #else + return false; + #endif +} + +#if CIRCUITPY_STATUS_BAR +static void _print_web_workflow_endpoint(void) { + mp_printf(&mp_plat_print, "%s", _our_ip_encoded); + if (web_api_port != 80) { + mp_printf(&mp_plat_print, ":%d", web_api_port); + } +} +#endif + mdns_server_obj_t *supervisor_web_workflow_mdns(mp_obj_t network_interface) { - #if CIRCUITPY_MDNS + #if CIRCUITPY_MDNS && CIRCUITPY_WIFI if (network_interface == &common_hal_wifi_radio_obj && mdns.base.type == &mdns_server_type) { return &mdns; } #endif + (void)network_interface; return NULL; } #if CIRCUITPY_STATUS_BAR bool supervisor_web_workflow_status_dirty(void) { - return common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj) != _last_enabled || - _encoded_ip != _last_ip || - _last_wifi_status != _wifi_status; + #if CIRCUITPY_WIFI || CIRCUITPY_HOSTNETWORK + uint32_t ipv4_address = 0; + bool enabled = _get_web_workflow_ip(&ipv4_address); + if (enabled != _last_enabled || ipv4_address != _last_ip || web_api_port != _last_web_api_port) { + return true; + } + #if CIRCUITPY_WIFI + if (_last_wifi_status != _wifi_status) { + return true; + } + #endif + return false; + #else + return false; + #endif } #endif #if CIRCUITPY_STATUS_BAR void supervisor_web_workflow_status(void) { - _last_enabled = common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj); + #if CIRCUITPY_WIFI || CIRCUITPY_HOSTNETWORK + uint32_t ipv4_address = 0; + _last_enabled = _get_web_workflow_ip(&ipv4_address); + _last_web_api_port = web_api_port; + if (_last_enabled) { - uint32_t ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj); + _update_encoded_ip(ipv4_address); + _last_ip = _encoded_ip; + if (ipv4_address != 0) { - _update_encoded_ip(); - _last_ip = _encoded_ip; - mp_printf(&mp_plat_print, "%s", _our_ip_encoded); - if (web_api_port != 80) { - mp_printf(&mp_plat_print, ":%d", web_api_port); - } + _print_web_workflow_endpoint(); // TODO: Use these unicode to show signal strength: ▂▄▆█ return; } - serial_write_compressed(MP_ERROR_TEXT("Wi-Fi: ")); - _last_wifi_status = _wifi_status; - if (_wifi_status == WIFI_RADIO_ERROR_AUTH_EXPIRE || - _wifi_status == WIFI_RADIO_ERROR_AUTH_FAIL) { - serial_write_compressed(MP_ERROR_TEXT("Authentication failure")); - } else if (_wifi_status != WIFI_RADIO_ERROR_NONE) { - mp_printf(&mp_plat_print, "%d", _wifi_status); - } else if (ipv4_address == 0) { - _last_ip = 0; - serial_write_compressed(MP_ERROR_TEXT("No IP")); - } else { - } - } else { - // Keep Wi-Fi print separate so its data can be matched with the one above. - serial_write_compressed(MP_ERROR_TEXT("Wi-Fi: ")); + } + + #if CIRCUITPY_WIFI + serial_write_compressed(MP_ERROR_TEXT("Wi-Fi: ")); + _last_wifi_status = _wifi_status; + if (!_last_enabled) { serial_write_compressed(MP_ERROR_TEXT("off")); + } else if (_wifi_status == WIFI_RADIO_ERROR_AUTH_EXPIRE || + _wifi_status == WIFI_RADIO_ERROR_AUTH_FAIL) { + serial_write_compressed(MP_ERROR_TEXT("Authentication failure")); + } else if (_wifi_status != WIFI_RADIO_ERROR_NONE) { + mp_printf(&mp_plat_print, "%d", _wifi_status); + } else { + _last_ip = 0; + serial_write_compressed(MP_ERROR_TEXT("No IP")); } + #endif + #else + return; + #endif } #endif bool supervisor_start_web_workflow(void) { - #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_SETTINGS_TOML + #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_SETTINGS_TOML && (CIRCUITPY_WIFI || CIRCUITPY_HOSTNETWORK) + + #if CIRCUITPY_WIFI + mp_obj_t socketpool_radio = MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj); + #else + mp_obj_t socketpool_radio = MP_OBJ_FROM_PTR(&common_hal_hostnetwork_obj); + #endif + settings_err_t result; + + #if CIRCUITPY_WIFI char ssid[33]; char password[64]; - settings_err_t result = settings_get_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid)); + result = settings_get_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid)); if (result != SETTINGS_OK || strlen(ssid) < 1) { return false; } @@ -275,6 +335,7 @@ bool supervisor_start_web_workflow(void) { common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, false); return false; } + #endif // Skip starting the workflow if we're not starting from power on or reset. const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason(); @@ -307,7 +368,7 @@ bool supervisor_start_web_workflow(void) { } pool.base.type = &socketpool_socketpool_type; - common_hal_socketpool_socketpool_construct(&pool, &common_hal_wifi_radio_obj); + common_hal_socketpool_socketpool_construct(&pool, socketpool_radio); socketpool_socket_reset(&listening); socketpool_socket_reset(&active); @@ -847,7 +908,9 @@ static void _reply_with_version_json(socketpool_socket_obj_t *socket, _request * instance_name = common_hal_mdns_server_get_instance_name(&mdns); } #endif - _update_encoded_ip(); + uint32_t ipv4_address = 0; + (void)_get_web_workflow_ip(&ipv4_address); + _update_encoded_ip(ipv4_address); // Note: this leverages the fact that C concats consecutive string literals together. mp_printf(&_socket_print, "{\"web_api_version\": 4, " diff --git a/supervisor/shared/web_workflow/web_workflow.h b/supervisor/shared/web_workflow/web_workflow.h index 439964dd333ac..9ec15a14ddc48 100644 --- a/supervisor/shared/web_workflow/web_workflow.h +++ b/supervisor/shared/web_workflow/web_workflow.h @@ -8,7 +8,12 @@ #include +#if CIRCUITPY_MDNS #include "shared-bindings/mdns/Server.h" +#else +typedef struct mdns_server_obj mdns_server_obj_t; +#endif + #include "shared-bindings/socketpool/Socket.h" // This background function should be called repeatedly. It cannot be done based diff --git a/supervisor/shared/workflow.c b/supervisor/shared/workflow.c index c1999b4a7b68a..7370e34e86007 100644 --- a/supervisor/shared/workflow.c +++ b/supervisor/shared/workflow.c @@ -43,10 +43,12 @@ void supervisor_workflow_reset(void) { #if CIRCUITPY_WEB_WORKFLOW bool result = supervisor_start_web_workflow(); - if (workflow_background_cb.fun) { - if (result) { - supervisor_workflow_request_background(); + if (result) { + if (!workflow_background_cb.fun) { + memset(&workflow_background_cb, 0, sizeof(workflow_background_cb)); + workflow_background_cb.fun = supervisor_web_workflow_background; } + supervisor_workflow_request_background(); } #endif } @@ -105,9 +107,11 @@ void supervisor_workflow_start(void) { #if CIRCUITPY_WEB_WORKFLOW if (supervisor_start_web_workflow()) { - // Enable background callbacks if web_workflow startup successful + // Enable background callbacks if web_workflow startup successful. memset(&workflow_background_cb, 0, sizeof(workflow_background_cb)); workflow_background_cb.fun = supervisor_web_workflow_background; + // Kick the first background run now that the callback is installed. + supervisor_workflow_request_background(); } #endif From d6afaf7bc2284fa67abee1bfb9160fa4048653d4 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 25 Feb 2026 11:57:39 -0800 Subject: [PATCH 362/517] Fix tinycircuits boards after base branch didn't catch the issues --- ports/raspberrypi/boards/tinycircuits_thumby/board.c | 6 +++--- .../boards/tinycircuits_thumby/mpconfigboard.h | 3 --- .../boards/tinycircuits_thumby_color/board.c | 12 ++++++------ ports/raspberrypi/mpconfigport.h | 4 +--- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/board.c b/ports/raspberrypi/boards/tinycircuits_thumby/board.c index 15a7eeb93a4c6..3a306b35f0d76 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby/board.c +++ b/ports/raspberrypi/boards/tinycircuits_thumby/board.c @@ -37,9 +37,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - CIRCUITPY_BOARD_OLED_DC, // Command or data - CIRCUITPY_BOARD_OLED_CS, // Chip select - CIRCUITPY_BOARD_OLED_RESET, // Reset + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_OLED_DC), // Command or data + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_OLED_CS), // Chip select + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_OLED_RESET), // Reset 10000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h index 91430966d232e..cec0fc10f55b1 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h +++ b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h @@ -18,6 +18,3 @@ #define CIRCUITPY_BOARD_SPI (1) #define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL}} - -// For entering safe mode -#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO6) diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c b/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c index abd80dafc62c8..17c512cb2a637 100644 --- a/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c @@ -62,12 +62,12 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - CIRCUITPY_BOARD_LCD_DC, // DC - CIRCUITPY_BOARD_LCD_CS, // CS - CIRCUITPY_BOARD_LCD_RESET, // RST - 80000000, // baudrate - 0, // polarity - 0 // phase + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_LCD_DC), // DC + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_LCD_CS), // CS + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_LCD_RESET), // RST + 80000000, // baudrate + 0, // polarity + 0 // phase ); busdisplay_busdisplay_obj_t *display = &allocate_display()->display; diff --git a/ports/raspberrypi/mpconfigport.h b/ports/raspberrypi/mpconfigport.h index 3eb576de39e67..c4253937c986c 100644 --- a/ports/raspberrypi/mpconfigport.h +++ b/ports/raspberrypi/mpconfigport.h @@ -36,10 +36,8 @@ #define CIRCUITPY_PROCESSOR_COUNT (2) -// For many RP2 boards BOOTSEL is not connected to a GPIO pin. -#ifndef CIRCUITPY_BOOT_BUTTON +// For RP2 boards we use a custom way to read BOOTSEL #define CIRCUITPY_BOOT_BUTTON_NO_GPIO (1) -#endif #if CIRCUITPY_USB_HOST #define CIRCUITPY_USB_HOST_INSTANCE 1 From 20d9e1812ce050a8ae1e3331f3e6cd833a14cd89 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 25 Feb 2026 13:06:22 -0800 Subject: [PATCH 363/517] Remove outdated OS_GETENV flag Co-authored-by: Dan Halbert --- ports/zephyr-cp/cptools/build_circuitpython.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 51521e913ab5d..4bae0cc73bf86 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -317,7 +317,6 @@ async def build_circuitpython(): circuitpython_flags.append(f"-DCIRCUITPY_ENABLE_MPY_NATIVE={1 if enable_mpy_native else 0}") circuitpython_flags.append(f"-DCIRCUITPY_FULL_BUILD={1 if full_build else 0}") circuitpython_flags.append(f"-DCIRCUITPY_SETTINGS_TOML={1 if full_build else 0}") - circuitpython_flags.append(f"-DCIRCUITPY_OS_GETENV={1 if full_build else 0}") circuitpython_flags.append("-DCIRCUITPY_STATUS_BAR=1") circuitpython_flags.append(f"-DCIRCUITPY_USB_HOST={1 if usb_host else 0}") circuitpython_flags.append(f"-DCIRCUITPY_BOARD_ID='\"{board}\"'") From a9ad7a1c90358de5ff6d02d143969c59f128d950 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Wed, 25 Feb 2026 23:09:35 +0100 Subject: [PATCH 364/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 5 ++++- locale/el.po | 5 ++++- locale/hi.po | 5 ++++- locale/ko.po | 5 ++++- locale/ru.po | 5 ++++- locale/tr.po | 5 ++++- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index 319fe64bc7522..92979bdd31b9b 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -2090,10 +2090,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "SocketPool je možné použít pouze s wifi.radio" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "Zdrojové a cílové buffery musí být stejné délky" diff --git a/locale/el.po b/locale/el.po index da44530ba7e17..9ad4e3ccfc31d 100644 --- a/locale/el.po +++ b/locale/el.po @@ -2095,10 +2095,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index f0a35d43e799f..046cea6b3d449 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -2069,10 +2069,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 31ec5a50294ba..311803d763007 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -2142,10 +2142,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" diff --git a/locale/ru.po b/locale/ru.po index 3efafa9e46438..1eac7b309a6ab 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -2120,10 +2120,13 @@ msgstr "Фрагменты не поддерживаются" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "SocketPool можно использовать только с wifi.radio" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "Исходный и конечный буферы должны иметь одинаковую длину" diff --git a/locale/tr.po b/locale/tr.po index 737eaff9b558c..4e75d975f55a4 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -2091,10 +2091,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" From d92dc147c3474589f7613bc312ee304f6b77174a Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 25 Feb 2026 17:52:09 -0600 Subject: [PATCH 365/517] update frozen libs --- frozen/Adafruit_CircuitPython_Bitmap_Font | 2 +- frozen/Adafruit_CircuitPython_Display_Text | 2 +- frozen/Adafruit_CircuitPython_HTTPServer | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frozen/Adafruit_CircuitPython_Bitmap_Font b/frozen/Adafruit_CircuitPython_Bitmap_Font index cb6a08e2ca57d..47999fac242f6 160000 --- a/frozen/Adafruit_CircuitPython_Bitmap_Font +++ b/frozen/Adafruit_CircuitPython_Bitmap_Font @@ -1 +1 @@ -Subproject commit cb6a08e2ca57df75b512b844cce78558088979fe +Subproject commit 47999fac242f673812315f42d1886ea54a2c5177 diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index 372cbe27459c0..3b606a735a01f 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit 372cbe27459c02db838fb65489cf8b4b5dba12f3 +Subproject commit 3b606a735a01f0cb577447fba59e0190ba79b10e diff --git a/frozen/Adafruit_CircuitPython_HTTPServer b/frozen/Adafruit_CircuitPython_HTTPServer index e234a4940504b..1419fc5c071b7 160000 --- a/frozen/Adafruit_CircuitPython_HTTPServer +++ b/frozen/Adafruit_CircuitPython_HTTPServer @@ -1 +1 @@ -Subproject commit e234a4940504b4fb21c7338d774ec273260ef672 +Subproject commit 1419fc5c071b7c631d9285c4efef4c4df23a079c From 5a49c524f8e10d9520bbfef0b527c19968d33b08 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 26 Feb 2026 10:50:03 -0800 Subject: [PATCH 366/517] Update autogen --- .../adafruit/feather_nrf52840_zephyr/autogen_board_info.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index db83e1b74481d..43b018c327adb 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -50,6 +50,7 @@ getpass = false gifio = false gnss = false hashlib = false +hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false From 1f8408ec90cd88593fc0e017a48594fdf2c4bb87 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 26 Feb 2026 12:19:33 -0800 Subject: [PATCH 367/517] Fix S3 startup due to ble_gap_adv_active crash ble_gap_adv_active() crashes now when BLE hasn't been init. This is likely due to internal memory allocations in esp-nimble. So, check enabled before calling into it. Fixes #10849 --- ports/espressif/common-hal/_bleio/Adapter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c index 327515da4de56..aca3f8c2042ca 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.c +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -733,7 +733,7 @@ void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { } bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) { - return ble_gap_adv_active(); + return common_hal_bleio_adapter_get_enabled(self) && ble_gap_adv_active(); } bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) { From a358cd58bce7afcfb3ff4ea2f5dd264aeb75518d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 26 Feb 2026 12:23:08 -0800 Subject: [PATCH 368/517] Tweak flaky test Now the bsim PHY will stay active until the test harness terminates it. This requires the CircuitPython device process to exit correctly first but is backed up by wall clock timeouts in the harness. (Sim timeouts were in simulated time.) --- ports/zephyr-cp/tests/bsim/conftest.py | 5 +++-- ports/zephyr-cp/zephyr-config/west.yml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ports/zephyr-cp/tests/bsim/conftest.py b/ports/zephyr-cp/tests/bsim/conftest.py index b7a66346ce269..67b76469fa030 100644 --- a/ports/zephyr-cp/tests/bsim/conftest.py +++ b/ports/zephyr-cp/tests/bsim/conftest.py @@ -97,7 +97,9 @@ def bsim_phy(request, bsim_phy_binary, native_sim_env, sim_id): sample_device_id = int(sample_marker.kwargs.get("device_id", 1)) devices = max(devices, sample_device_id + 1) - sim_length_us = int(duration * 1e6) + # Do not pass -sim_length: if the PHY exits on simulated time, device 0 can + # still be flushing UART output and test output can get truncated. Instead, + # let pytest own process lifetime and terminate the PHY at fixture teardown. cmd = [ "stdbuf", "-oL", @@ -105,7 +107,6 @@ def bsim_phy(request, bsim_phy_binary, native_sim_env, sim_id): "-v=9", # Cleaning up level is on 9. Connecting is 7. f"-s={sim_id}", f"-D={devices}", - f"-sim_length={sim_length_us}", ] print("Running:", " ".join(cmd)) proc = subprocess.Popen( diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index a8b05fabd05cc..04983fb602b07 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -4,7 +4,7 @@ manifest: projects: - name: nrf_hw_models url: https://github.com/tannewt/ext_nRF_hw_models - revision: 24de78c485dce1a6048f8ae1c69a8d70c93b8cdd + revision: bb351cef9e5ab4d175fe3cb7c4d6761d837bac20 path: modules/bsim_hw_models/nrf_hw_models - name: zephyr url: https://github.com/adafruit/zephyr From 2c95679ccf1ea9ce4791a61ad4db632c1f4a2309 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 27 Feb 2026 13:38:47 -0800 Subject: [PATCH 369/517] Start tracking zephyr/main main is still quite stable but has the latest hardware support and bug fixes. --- ports/zephyr-cp/boards/board_aliases.cmake | 64 +++++++++++++------ .../boards/nrf5340bsim_nrf5340_cpuapp.conf | 4 -- ports/zephyr-cp/prj.conf | 10 --- ports/zephyr-cp/socs/nrf5340_cpuapp.conf | 3 +- ports/zephyr-cp/zephyr-config/west.yml | 4 +- 5 files changed, 49 insertions(+), 36 deletions(-) diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index ee46145a1127f..dce5b100aa6df 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -1,18 +1,46 @@ -set(pca10056_BOARD_ALIAS nrf52840dk/nrf52840) -set(adafruit_feather_nrf52840_zephyr_BOARD_ALIAS adafruit_feather_nrf52840/nrf52840/uf2) -set(renesas_ek_ra6m5_BOARD_ALIAS ek_ra6m5) -set(renesas_ek_ra8d1_BOARD_ALIAS ek_ra8d1) -set(renesas_da14695_dk_usb_BOARD_ALIAS da14695_dk_usb) -set(native_native_sim_BOARD_ALIAS native_sim) -set(native_nrf5340bsim_BOARD_ALIAS nrf5340bsim/nrf5340/cpuapp) -set(nordic_nrf54l15dk_BOARD_ALIAS nrf54l15dk/nrf54l15/cpuapp) -set(nordic_nrf54h20dk_BOARD_ALIAS nrf54h20dk/nrf54h20/cpuapp) -set(nordic_nrf5340dk_BOARD_ALIAS nrf5340dk/nrf5340/cpuapp) -set(nordic_nrf7002dk_BOARD_ALIAS nrf7002dk/nrf5340/cpuapp) -set(nxp_frdm_mcxn947_BOARD_ALIAS frdm_mcxn947/mcxn947/cpu0) -set(nxp_frdm_rw612_BOARD_ALIAS frdm_rw612) -set(nxp_mimxrt1170_evk_BOARD_ALIAS mimxrt1170_evk@A/mimxrt1176/cm7) -set(st_stm32h7b3i_dk_BOARD_ALIAS stm32h7b3i_dk) -set(st_stm32wba65i_dk1_BOARD_ALIAS stm32wba65i_dk1) -set(st_nucleo_u575zi_q_BOARD_ALIAS nucleo_u575zi_q/stm32u575xx) -set(st_nucleo_n657x0_q_BOARD_ALIAS nucleo_n657x0_q/stm32n657xx) +# Workaround for Zephyr alias handling with BOARD_QUALIFIERS in newer Zephyr. +# +# Instead of using _BOARD_ALIAS variables, translate BOARD directly so +# we don't end up with a spurious trailing '/' in BOARD_QUALIFIERS. +macro(cp_board_alias alias target) + if(BOARD STREQUAL "${alias}") + if(NOT "${target}" MATCHES "^([^@/]+)(@[^@/]+)?(/([^@]+))?$") + message(FATAL_ERROR "Invalid alias target '${target}'") + endif() + + set(BOARD_ALIAS "${alias}" CACHE STRING "Board alias, provided by user") + set(BOARD "${CMAKE_MATCH_1}") + + if(CMAKE_MATCH_2) + string(REPLACE "@" "" _rev "${CMAKE_MATCH_2}") + set(BOARD_REVISION "${_rev}") + else() + unset(BOARD_REVISION) + endif() + + if(CMAKE_MATCH_4) + set(BOARD_QUALIFIERS "${CMAKE_MATCH_4}") + else() + unset(BOARD_QUALIFIERS) + endif() + endif() +endmacro() + +cp_board_alias(pca10056 nrf52840dk/nrf52840) +cp_board_alias(adafruit_feather_nrf52840_zephyr adafruit_feather_nrf52840/nrf52840/uf2) +cp_board_alias(renesas_ek_ra6m5 ek_ra6m5) +cp_board_alias(renesas_ek_ra8d1 ek_ra8d1) +cp_board_alias(renesas_da14695_dk_usb da14695_dk_usb) +cp_board_alias(native_native_sim native_sim/native) +cp_board_alias(native_nrf5340bsim nrf5340bsim/nrf5340/cpuapp) +cp_board_alias(nordic_nrf54l15dk nrf54l15dk/nrf54l15/cpuapp) +cp_board_alias(nordic_nrf54h20dk nrf54h20dk/nrf54h20/cpuapp) +cp_board_alias(nordic_nrf5340dk nrf5340dk/nrf5340/cpuapp) +cp_board_alias(nordic_nrf7002dk nrf7002dk/nrf5340/cpuapp) +cp_board_alias(nxp_frdm_mcxn947 frdm_mcxn947/mcxn947/cpu0) +cp_board_alias(nxp_frdm_rw612 frdm_rw612) +cp_board_alias(nxp_mimxrt1170_evk mimxrt1170_evk@A/mimxrt1176/cm7) +cp_board_alias(st_stm32h7b3i_dk stm32h7b3i_dk) +cp_board_alias(st_stm32wba65i_dk1 stm32wba65i_dk1) +cp_board_alias(st_nucleo_u575zi_q nucleo_u575zi_q/stm32u575xx) +cp_board_alias(st_nucleo_n657x0_q nucleo_n657x0_q/stm32n657xx) diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf index f9507bd031926..02e4e0d0d5647 100644 --- a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf @@ -6,16 +6,12 @@ CONFIG_GPIO=y # Enable Bluetooth stack - bsim is for BT simulation CONFIG_BT=y CONFIG_BT_HCI=y -CONFIG_BT_HCI_IPC=y CONFIG_BT_OBSERVER=y CONFIG_BT_BROADCASTER=y CONFIG_BT_DEVICE_NAME_DYNAMIC=y CONFIG_BT_DEVICE_NAME_MAX=28 -# So we can test safe mode -CONFIG_NATIVE_SIM_REBOOT=y - # Ensure the network core image starts when using native simulator CONFIG_NATIVE_SIMULATOR_AUTOSTART_MCU=y diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index d0f4d911c123b..9b4dcccb53e4d 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -1,25 +1,20 @@ CONFIG_SYS_HEAP_RUNTIME_STATS=n CONFIG_FLASH=y CONFIG_FLASH_MAP=y -CONFIG_STD_C23=y CONFIG_DYNAMIC_INTERRUPTS=y CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_FLASH_MAP_LABELS=y CONFIG_MAIN_STACK_SIZE=24288 -CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_THREAD_STACK_INFO=y CONFIG_STACK_SENTINEL=n CONFIG_DEBUG_THREAD_INFO=n -CONFIG_DEBUG_INFO=n CONFIG_EXCEPTION_STACK_TRACE=n CONFIG_USB_DEVICE_STACK_NEXT=y CONFIG_USBD_CDC_ACM_CLASS=y -CONFIG_USBD_MAX_SPEED=1 CONFIG_USBD_MSC_STACK_SIZE=1536 CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n @@ -44,13 +39,8 @@ CONFIG_SPI_ASYNC=y CONFIG_LOG=y CONFIG_LOG_MAX_LEVEL=2 -CONFIG_HW_STACK_PROTECTION=y CONFIG_FRAME_POINTER=n -CONFIG_BT_BUF_ACL_TX_COUNT=7 -CONFIG_BT_HCI_ERR_TO_STR=y - CONFIG_NET_HOSTNAME_ENABLE=y CONFIG_NET_HOSTNAME_DYNAMIC=y CONFIG_NET_HOSTNAME="circuitpython" -CONFIG_NET_MGMT_EVENT_INFO=y diff --git a/ports/zephyr-cp/socs/nrf5340_cpuapp.conf b/ports/zephyr-cp/socs/nrf5340_cpuapp.conf index bf70997d83f29..734d26db8e939 100644 --- a/ports/zephyr-cp/socs/nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/socs/nrf5340_cpuapp.conf @@ -1,4 +1,3 @@ -CONFIG_NRFX_UARTE0=y -CONFIG_NRFX_UARTE1=y +CONFIG_NRFX_UARTE=y CONFIG_NRFX_POWER=y diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index 3cee04f982b66..bb5c9b4942b35 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -4,10 +4,10 @@ manifest: projects: - name: nrf_hw_models url: https://github.com/tannewt/ext_nRF_hw_models - revision: bb351cef9e5ab4d175fe3cb7c4d6761d837bac20 + revision: c2927847bdf2ee0af9c5459f4155c67f39f6837a path: modules/bsim_hw_models/nrf_hw_models - name: zephyr url: https://github.com/adafruit/zephyr - revision: 8801b409ec554cfd217c159c00f91280ea1331db + revision: 5351284ac926b1352ab98f5ae692a21f38068beb clone-depth: 100 import: true From 89c818679fd521a1130e989ac5cf160403e33e5a Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Tue, 24 Feb 2026 00:04:27 +0100 Subject: [PATCH 370/517] QSPI bus driver and RM690B0 display driver for Waveshare ESP32-S3 AMOLED 2.41 --- locale/circuitpython.pot | 56 ++ ports/espressif/Makefile | 3 + .../waveshare_esp32_s3_amoled_241/board.c | 8 + .../mpconfigboard.h | 41 ++ .../mpconfigboard.mk | 39 ++ .../waveshare_esp32_s3_amoled_241/pins.c | 100 +++ .../waveshare_esp32_s3_amoled_241/sdkconfig | 33 + ports/espressif/common-hal/qspibus/QSPIBus.c | 580 ++++++++++++++++++ ports/espressif/common-hal/qspibus/QSPIBus.h | 51 ++ ports/espressif/common-hal/qspibus/__init__.c | 3 + ports/espressif/common-hal/qspibus/__init__.h | 3 + py/circuitpy_defns.mk | 5 + py/circuitpy_mpconfig.h | 7 + py/circuitpy_mpconfig.mk | 4 + shared-bindings/qspibus/QSPIBus.c | 222 +++++++ shared-bindings/qspibus/QSPIBus.h | 57 ++ shared-bindings/qspibus/__init__.c | 50 ++ shared-bindings/qspibus/__init__.h | 8 + shared-module/busdisplay/BusDisplay.c | 47 ++ shared-module/displayio/__init__.c | 4 + shared-module/displayio/__init__.h | 6 + shared-module/displayio/bus_core.c | 13 + 22 files changed, 1340 insertions(+) create mode 100644 ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c create mode 100644 ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h create mode 100644 ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk create mode 100644 ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c create mode 100644 ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig create mode 100644 ports/espressif/common-hal/qspibus/QSPIBus.c create mode 100644 ports/espressif/common-hal/qspibus/QSPIBus.h create mode 100644 ports/espressif/common-hal/qspibus/__init__.c create mode 100644 ports/espressif/common-hal/qspibus/__init__.h create mode 100644 shared-bindings/qspibus/QSPIBus.c create mode 100644 shared-bindings/qspibus/QSPIBus.h create mode 100644 shared-bindings/qspibus/__init__.c create mode 100644 shared-bindings/qspibus/__init__.h diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index e2eebdea0fe04..1f3d4c0974a0f 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -646,6 +646,10 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "Begin transaction first" +msgstr "" + #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -712,6 +716,10 @@ msgstr "" msgid "Buffer too small" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "Bus in display transaction" +msgstr "" + #: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c @@ -906,6 +914,10 @@ msgstr "" msgid "Data 0 pin must be byte aligned" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "Data buffer is null" +msgstr "" + #: shared-module/jpegio/JpegDecoder.c msgid "Data format error (may be broken data)" msgstr "" @@ -1017,6 +1029,10 @@ msgstr "" msgid "Failed to allocate %q buffer" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "Failed to allocate DMA buffers" +msgstr "" + #: ports/espressif/common-hal/wifi/__init__.c msgid "Failed to allocate Wifi memory" msgstr "" @@ -1054,6 +1070,10 @@ msgstr "" msgid "Failed to create continuous channels: not found" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "Failed to create semaphore" +msgstr "" + #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to enable continuous" msgstr "" @@ -1599,6 +1619,10 @@ msgstr "" msgid "No out in program" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "No pending command" +msgstr "" + #: ports/atmel-samd/common-hal/busio/I2C.c #: ports/espressif/common-hal/busio/I2C.c #: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nordic/common-hal/busio/I2C.c @@ -1783,6 +1807,11 @@ msgstr "" msgid "Packet buffers for an SPI transfer must have the same length." msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +#, c-format +msgid "Panel IO init failed: %d" +msgstr "" + #: shared-module/jpegio/JpegDecoder.c msgid "Parameter error" msgstr "" @@ -1889,6 +1918,28 @@ msgstr "" msgid "Pull not used when direction is output." msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "QSPI DMA buffers unavailable" +msgstr "" + +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "QSPI color timeout" +msgstr "" + +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "QSPI command timeout" +msgstr "" + +#: ports/espressif/common-hal/qspibus/QSPIBus.c +#, c-format +msgid "QSPI send color failed: %d" +msgstr "" + +#: ports/espressif/common-hal/qspibus/QSPIBus.c +#, c-format +msgid "QSPI send failed: %d" +msgstr "" + #: ports/raspberrypi/common-hal/countio/Counter.c msgid "RISE_AND_FALL not available on this chip" msgstr "" @@ -2010,6 +2061,11 @@ msgstr "" msgid "SDIO Init Error %x" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c +#, c-format +msgid "SPI bus init failed: %d" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c msgid "SPI configuration failed" msgstr "" diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 9e331740dd6a7..2545b33d22a39 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -780,6 +780,9 @@ endif ifneq ($(CIRCUITPY_PARALLELDISPLAYBUS),0) ESP_IDF_COMPONENTS_LINK += esp_lcd endif +ifneq ($(CIRCUITPY_QSPIBUS),0) + ESP_IDF_COMPONENTS_LINK += esp_lcd +endif ifneq ($(CIRCUITPY_USB_DEVICE),0) ESP_IDF_COMPONENTS_LINK += usb endif diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c new file mode 100644 index 0000000000000..10ef3d7274c48 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +void board_init(void) { +} diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h new file mode 100644 index 0000000000000..b4d4df961bf41 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Waveshare ESP32-S3-Touch-AMOLED-2.41" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +// USB identifiers +#define USB_VID 0x303A +#define USB_PID 0x82CE +#define USB_MANUFACTURER "Waveshare" +#define USB_PRODUCT "ESP32-S3-Touch-AMOLED-2.41" + +// I2C bus - Disabled on boot to avoid conflicts. User must manually initialize I2C. +#define CIRCUITPY_BOARD_I2C (0) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO48, .sda = &pin_GPIO47}} + +// QSPI display refresh buffer: 2048 uint32_t words = 8KB on stack. +// ESP32-S3 main task stack is 24KB; verified safe with this board. +#define CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE (2048) + +// AMOLED Display (displayio + qspibus path) +#define CIRCUITPY_BOARD_DISPLAY (0) +#define CIRCUITPY_LCD_CS (&pin_GPIO9) +#define CIRCUITPY_LCD_CLK (&pin_GPIO10) +#define CIRCUITPY_LCD_D0 (&pin_GPIO11) +#define CIRCUITPY_LCD_D1 (&pin_GPIO12) +#define CIRCUITPY_LCD_D2 (&pin_GPIO13) +#define CIRCUITPY_LCD_D3 (&pin_GPIO14) +#define CIRCUITPY_LCD_RESET (&pin_GPIO21) +#define CIRCUITPY_LCD_POWER (&pin_GPIO16) +#define CIRCUITPY_LCD_POWER_ON_LEVEL (1) // GPIO level: 1=high, 0=low + +// No default SPI bus — SD card uses SDIO, display uses QSPI. +#define CIRCUITPY_BOARD_SPI (0) + +// Default UART bus +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk new file mode 100644 index 0000000000000..0546d90693390 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +# +# SPDX-License-Identifier: MIT + +CIRCUITPY_CREATOR_ID = 0x57415645 # 'WAVE' (Waveshare) +CIRCUITPY_CREATION_ID = 0x41323431 # 'A241' (AMOLED 2.41) + +# USB identifiers - from Arduino pins_arduino.h +USB_VID = 0x303A +USB_PID = 0x82CE +USB_MANUFACTURER = "Waveshare" +USB_PRODUCT = "ESP32-S3-Touch-AMOLED-2.41" + +IDF_TARGET = esp32s3 + +# Flash configuration - 16MB QSPI Flash +CIRCUITPY_ESP_FLASH_SIZE = 16MB +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m + +# PSRAM configuration - 8MB Octal PSRAM +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +OPTIMIZATION_FLAGS = -Os + +# QSPI bus for RM690B0 AMOLED display +CIRCUITPY_QSPIBUS = 1 +CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# No camera on this board +CIRCUITPY_ESPCAMERA = 0 + +# Capacitive touch not available; board uses I2C touch controller +CIRCUITPY_TOUCHIO = 0 + +# SD card via SDMMC interface +CIRCUITPY_SDIOIO = 1 diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c new file mode 100644 index 0000000000000..437488e3f2b52 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/mphal.h" +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // ================================================================= + // ONBOARD PERIPHERALS - Functional Names + // ================================================================= + + // Boot/Control/Battery/Display Power + // NOTE: GPIO16 is shared between battery control circuitry and LCD power + // (see CIRCUITPY_QSPIBUS_PANEL_POWER_PIN in mpconfigboard.h). + { MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_KEY_BAT), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_BAT_CONTROL), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_LCD_POWER), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_BAT_ADC), MP_ROM_PTR(&pin_GPIO17) }, + + // I2C Bus (shared by Touch, RTC, IMU, IO Expander) + // NOTE: board.I2C auto-initialization is disabled (CIRCUITPY_BOARD_I2C=0) + // to avoid boot conflicts. Users must manually create I2C bus: + // i2c = busio.I2C(board.SCL, board.SDA) + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO48) }, + + // Touch Panel (FT6336U on I2C) + { MP_ROM_QSTR(MP_QSTR_TP_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_TP_SCL), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_TP_RESET), MP_ROM_PTR(&pin_GPIO3) }, + + // RTC (PCF85063 on I2C) + { MP_ROM_QSTR(MP_QSTR_RTC_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_RTC_SCL), MP_ROM_PTR(&pin_GPIO48) }, + + // IMU (QMI8658 on I2C) + { MP_ROM_QSTR(MP_QSTR_IMU_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_IMU_SCL), MP_ROM_PTR(&pin_GPIO48) }, + + // I/O Expander (TCA9554 on I2C) + { MP_ROM_QSTR(MP_QSTR_EXIO_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_EXIO_SCL), MP_ROM_PTR(&pin_GPIO48) }, + + // USB + { MP_ROM_QSTR(MP_QSTR_USB_D_N), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_USB_D_P), MP_ROM_PTR(&pin_GPIO20) }, + + // UART + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + + // QSPI Display (RM690B0) - canonical generic LCD aliases. + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CLK), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D0), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D1), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D2), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D3), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RESET), MP_ROM_PTR(&pin_GPIO21) }, + + // Display Aliases + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_SCK), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_D0), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_D1), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_D2), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_D3), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_RST), MP_ROM_PTR(&pin_GPIO21) }, + + // SD Card (SDIO / SDMMC) + { MP_ROM_QSTR(MP_QSTR_SDIO_CLK), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_SDIO_CMD), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_SDIO_D0), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_SDIO_D3), MP_ROM_PTR(&pin_GPIO2) }, + + // ================================================================= + // GENERAL PURPOSE I/O (IOxx - Espressif Convention) + // ================================================================= + // Only pins NOT dedicated to onboard peripherals are exposed here. + // Use functional names above for dedicated pins (e.g., SDA, SD_CS). + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, // BOOT button (available when not holding BOOT) + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, // TP_RESET (available if touch not used) + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, // Available +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig new file mode 100644 index 0000000000000..3ef898aed3e46 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig @@ -0,0 +1,33 @@ +# +# Configuration file for the Waveshare ESP32-S3 Touch AMOLED 2.41 +# + +# PSRAM Configuration +CONFIG_SPIRAM=y +CONFIG_SPIRAM_BOOT_INIT=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y +CONFIG_SPIRAM_RODATA=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_CLK_IO=39 +CONFIG_SPIRAM_CS_IO=38 + +# Performance and Cache +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y +CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y + +# Default flash settings for this board +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="esp-idf-config/partitions-16MB.csv" +CONFIG_PARTITION_TABLE_FILENAME="esp-idf-config/partitions-16MB.csv" + +# Networking +CONFIG_LWIP_LOCAL_HOSTNAME="waveshare-esp32-s3-amoled" + +# Disable USB-Serial/JTAG console - CircuitPython uses TinyUSB (USB OTG) for REPL +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=n + +# Enable .app_desc structure +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c new file mode 100644 index 0000000000000..7182869b6b600 --- /dev/null +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -0,0 +1,580 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/qspibus/QSPIBus.h" + +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +#include "py/gc.h" +#include "py/runtime.h" + +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "soc/soc_caps.h" +#include + +#define QSPI_OPCODE_WRITE_CMD (0x02U) +#define QSPI_OPCODE_WRITE_COLOR (0x32U) +#define LCD_CMD_RAMWR (0x2CU) +#define LCD_CMD_RAMWRC (0x3CU) +#define LCD_CMD_DISPOFF (0x28U) +#define LCD_CMD_SLPIN (0x10U) +#define QSPI_DMA_BUFFER_COUNT (2U) +#define QSPI_DMA_BUFFER_SIZE (16U * 1024U) +#define QSPI_COLOR_TIMEOUT_MS (1000U) +#if defined(CIRCUITPY_LCD_POWER) +#define CIRCUITPY_QSPIBUS_PANEL_POWER_PIN CIRCUITPY_LCD_POWER +#endif + +#ifndef CIRCUITPY_LCD_POWER_ON_LEVEL +#define CIRCUITPY_LCD_POWER_ON_LEVEL (1) +#endif + +static void qspibus_release_dma_buffers(qspibus_qspibus_obj_t *self) { + for (size_t i = 0; i < QSPI_DMA_BUFFER_COUNT; i++) { + if (self->dma_buffer[i] != NULL) { + heap_caps_free(self->dma_buffer[i]); + self->dma_buffer[i] = NULL; + } + } + self->dma_buffer_size = 0; + self->active_buffer = 0; + self->inflight_transfers = 0; + self->transfer_in_progress = false; +} + +static bool qspibus_allocate_dma_buffers(qspibus_qspibus_obj_t *self) { + const size_t candidates[] = { + QSPI_DMA_BUFFER_SIZE, + QSPI_DMA_BUFFER_SIZE / 2, + QSPI_DMA_BUFFER_SIZE / 4, + }; + + for (size_t c = 0; c < MP_ARRAY_SIZE(candidates); c++) { + size_t size = candidates[c]; + bool ok = true; + for (size_t i = 0; i < QSPI_DMA_BUFFER_COUNT; i++) { + self->dma_buffer[i] = heap_caps_malloc(size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + if (self->dma_buffer[i] == NULL) { + ok = false; + break; + } + } + if (ok) { + self->dma_buffer_size = size; + self->active_buffer = 0; + self->inflight_transfers = 0; + self->transfer_in_progress = false; + return true; + } + qspibus_release_dma_buffers(self); + } + return false; +} + +// Reset transfer bookkeeping after timeout/error. Drains any stale semaphore +// tokens that late ISR completions may have posted after the timeout expired. +static void qspibus_reset_transfer_state(qspibus_qspibus_obj_t *self) { + self->inflight_transfers = 0; + self->transfer_in_progress = false; + if (self->transfer_done_sem != NULL) { + while (xSemaphoreTake(self->transfer_done_sem, 0) == pdTRUE) { + } + } +} + +static bool qspibus_wait_one_transfer_done(qspibus_qspibus_obj_t *self, TickType_t timeout) { + if (self->inflight_transfers == 0) { + self->transfer_in_progress = false; + return true; + } + + if (xSemaphoreTake(self->transfer_done_sem, timeout) != pdTRUE) { + return false; + } + self->inflight_transfers--; + self->transfer_in_progress = (self->inflight_transfers > 0); + return true; +} + +static bool qspibus_wait_all_transfers_done(qspibus_qspibus_obj_t *self, TickType_t timeout) { + while (self->inflight_transfers > 0) { + if (!qspibus_wait_one_transfer_done(self, timeout)) { + return false; + } + } + return true; +} + +static void qspibus_send_command_bytes( + qspibus_qspibus_obj_t *self, + uint8_t command, + const uint8_t *data, + size_t len) { + + if (!self->bus_initialized) { + raise_deinited_error(); + } + if (self->inflight_transfers >= QSPI_DMA_BUFFER_COUNT) { + if (!qspibus_wait_one_transfer_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg(MP_ERROR_TEXT("QSPI command timeout")); + } + } + + uint32_t packed_cmd = ((uint32_t)QSPI_OPCODE_WRITE_CMD << 24) | ((uint32_t)command << 8); + esp_err_t err = esp_lcd_panel_io_tx_param(self->io_handle, packed_cmd, data, len); + if (err != ESP_OK) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("QSPI send failed: %d"), err); + } +} + +static void qspibus_send_color_bytes( + qspibus_qspibus_obj_t *self, + uint8_t command, + const uint8_t *data, + size_t len) { + + if (!self->bus_initialized) { + raise_deinited_error(); + } + + if (len == 0) { + qspibus_send_command_bytes(self, command, NULL, 0); + return; + } + if (data == NULL || self->dma_buffer_size == 0) { + mp_raise_OSError_msg(MP_ERROR_TEXT("QSPI DMA buffers unavailable")); + } + + // RAMWR must transition to RAMWRC for continued payload chunks. + uint8_t chunk_command = command; + const uint8_t *cursor = data; + size_t remaining = len; + + while (remaining > 0) { + if (self->inflight_transfers >= QSPI_DMA_BUFFER_COUNT) { + if (!qspibus_wait_one_transfer_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg(MP_ERROR_TEXT("QSPI color timeout")); + } + } + + size_t chunk = remaining; + if (chunk > self->dma_buffer_size) { + chunk = self->dma_buffer_size; + } + + uint8_t *buffer = self->dma_buffer[self->active_buffer]; + memcpy(buffer, cursor, chunk); + + uint32_t packed_cmd = ((uint32_t)QSPI_OPCODE_WRITE_COLOR << 24) | ((uint32_t)chunk_command << 8); + esp_err_t err = esp_lcd_panel_io_tx_color(self->io_handle, packed_cmd, buffer, chunk); + if (err != ESP_OK) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("QSPI send color failed: %d"), err); + } + + self->inflight_transfers++; + self->transfer_in_progress = true; + self->active_buffer = (self->active_buffer + 1) % QSPI_DMA_BUFFER_COUNT; + + if (chunk_command == LCD_CMD_RAMWR) { + chunk_command = LCD_CMD_RAMWRC; + } + + cursor += chunk; + remaining -= chunk; + } + + // Keep Python/API semantics predictable: color transfer call returns only + // after queued DMA chunks have completed. + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg(MP_ERROR_TEXT("QSPI color timeout")); + } +} + +static bool qspibus_is_color_payload_command(uint8_t command) { + return command == LCD_CMD_RAMWR || command == LCD_CMD_RAMWRC; +} + +static void qspibus_panel_sleep_best_effort(qspibus_qspibus_obj_t *self) { + if (!self->bus_initialized || self->io_handle == NULL) { + return; + } + + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + } + + // If a command is buffered, flush it first so the panel state machine + // doesn't get a truncated transaction before sleep. + if (self->has_pending_command) { + uint32_t pending = ((uint32_t)QSPI_OPCODE_WRITE_CMD << 24) | ((uint32_t)self->pending_command << 8); + (void)esp_lcd_panel_io_tx_param(self->io_handle, pending, NULL, 0); + self->has_pending_command = false; + } + + uint32_t disp_off = ((uint32_t)QSPI_OPCODE_WRITE_CMD << 24) | ((uint32_t)LCD_CMD_DISPOFF << 8); + (void)esp_lcd_panel_io_tx_param(self->io_handle, disp_off, NULL, 0); + vTaskDelay(pdMS_TO_TICKS(20)); + + uint32_t sleep_in = ((uint32_t)QSPI_OPCODE_WRITE_CMD << 24) | ((uint32_t)LCD_CMD_SLPIN << 8); + (void)esp_lcd_panel_io_tx_param(self->io_handle, sleep_in, NULL, 0); + vTaskDelay(pdMS_TO_TICKS(120)); +} + +static bool IRAM_ATTR qspibus_on_color_trans_done( + esp_lcd_panel_io_handle_t io_handle, + esp_lcd_panel_io_event_data_t *event_data, + void *user_ctx) { + (void)io_handle; + (void)event_data; + + qspibus_qspibus_obj_t *self = (qspibus_qspibus_obj_t *)user_ctx; + if (self->transfer_done_sem == NULL) { + return false; + } + BaseType_t x_higher_priority_task_woken = pdFALSE; + + xSemaphoreGiveFromISR(self->transfer_done_sem, &x_higher_priority_task_woken); + return x_higher_priority_task_woken == pdTRUE; +} + +void common_hal_qspibus_qspibus_construct( + qspibus_qspibus_obj_t *self, + const mcu_pin_obj_t *clock, + const mcu_pin_obj_t *data0, + const mcu_pin_obj_t *data1, + const mcu_pin_obj_t *data2, + const mcu_pin_obj_t *data3, + const mcu_pin_obj_t *cs, + const mcu_pin_obj_t *dcx, + const mcu_pin_obj_t *reset, + uint32_t frequency) { + + self->io_handle = NULL; + self->host_id = SPI2_HOST; + self->clock_pin = clock->number; + self->data0_pin = data0->number; + self->data1_pin = data1->number; + self->data2_pin = data2->number; + self->data3_pin = data3->number; + self->cs_pin = cs->number; + self->dcx_pin = (dcx != NULL) ? dcx->number : -1; + self->reset_pin = (reset != NULL) ? reset->number : -1; + self->power_pin = -1; + self->frequency = frequency; + self->bus_initialized = false; + self->in_transaction = false; + self->has_pending_command = false; + self->pending_command = 0; + self->transfer_in_progress = false; + self->active_buffer = 0; + self->inflight_transfers = 0; + self->dma_buffer_size = 0; + self->dma_buffer[0] = NULL; + self->dma_buffer[1] = NULL; + self->transfer_done_sem = NULL; + + self->transfer_done_sem = xSemaphoreCreateCounting(QSPI_DMA_BUFFER_COUNT, 0); + if (self->transfer_done_sem == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to create semaphore")); + } + + if (!qspibus_allocate_dma_buffers(self)) { + vSemaphoreDelete(self->transfer_done_sem); + self->transfer_done_sem = NULL; + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to allocate DMA buffers")); + } + + const spi_bus_config_t bus_config = { + .sclk_io_num = self->clock_pin, + .mosi_io_num = self->data0_pin, + .miso_io_num = self->data1_pin, + .quadwp_io_num = self->data2_pin, + .quadhd_io_num = self->data3_pin, + .max_transfer_sz = self->dma_buffer_size, + .flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_GPIO_PINS, + }; + + esp_err_t err = spi_bus_initialize(self->host_id, &bus_config, SPI_DMA_CH_AUTO); + if (err != ESP_OK) { + qspibus_release_dma_buffers(self); + vSemaphoreDelete(self->transfer_done_sem); + self->transfer_done_sem = NULL; + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("SPI bus init failed: %d"), err); + } + + const esp_lcd_panel_io_spi_config_t io_config = { + .cs_gpio_num = self->cs_pin, + .dc_gpio_num = -1, + .spi_mode = 0, + .pclk_hz = self->frequency, + .trans_queue_depth = QSPI_DMA_BUFFER_COUNT, + .on_color_trans_done = qspibus_on_color_trans_done, + .user_ctx = self, + .lcd_cmd_bits = 32, + .lcd_param_bits = 8, + .flags = { + .quad_mode = 1, + }, + }; + + err = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)self->host_id, &io_config, &self->io_handle); + if (err != ESP_OK) { + spi_bus_free(self->host_id); + qspibus_release_dma_buffers(self); + vSemaphoreDelete(self->transfer_done_sem); + self->transfer_done_sem = NULL; + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("Panel IO init failed: %d"), err); + } + + claim_pin(clock); + claim_pin(data0); + claim_pin(data1); + claim_pin(data2); + claim_pin(data3); + claim_pin(cs); + if (dcx != NULL) { + claim_pin(dcx); + gpio_set_direction((gpio_num_t)self->dcx_pin, GPIO_MODE_OUTPUT); + gpio_set_level((gpio_num_t)self->dcx_pin, 1); + } + + #ifdef CIRCUITPY_QSPIBUS_PANEL_POWER_PIN + const mcu_pin_obj_t *power = CIRCUITPY_QSPIBUS_PANEL_POWER_PIN; + if (power != NULL) { + self->power_pin = power->number; + claim_pin(power); + gpio_set_direction((gpio_num_t)self->power_pin, GPIO_MODE_OUTPUT); + gpio_set_level((gpio_num_t)self->power_pin, CIRCUITPY_LCD_POWER_ON_LEVEL ? 1 : 0); + // Panel power rail needs extra settle time before reset/init commands. + vTaskDelay(pdMS_TO_TICKS(200)); + } + #endif + + if (reset != NULL) { + claim_pin(reset); + + gpio_set_direction((gpio_num_t)self->reset_pin, GPIO_MODE_OUTPUT); + gpio_set_level((gpio_num_t)self->reset_pin, 0); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level((gpio_num_t)self->reset_pin, 1); + vTaskDelay(pdMS_TO_TICKS(120)); + } + + self->bus_initialized = true; +} + +void common_hal_qspibus_qspibus_deinit(qspibus_qspibus_obj_t *self) { + if (!self->bus_initialized) { + qspibus_release_dma_buffers(self); + return; + } + + qspibus_panel_sleep_best_effort(self); + self->in_transaction = false; + + if (self->io_handle != NULL) { + esp_lcd_panel_io_del(self->io_handle); + self->io_handle = NULL; + } + + spi_bus_free(self->host_id); + + if (self->transfer_done_sem != NULL) { + // Set NULL before delete so late ISR callbacks (if any) see NULL and skip. + SemaphoreHandle_t sem = self->transfer_done_sem; + self->transfer_done_sem = NULL; + vSemaphoreDelete(sem); + } + + qspibus_release_dma_buffers(self); + + reset_pin_number(self->clock_pin); + reset_pin_number(self->data0_pin); + reset_pin_number(self->data1_pin); + reset_pin_number(self->data2_pin); + reset_pin_number(self->data3_pin); + reset_pin_number(self->cs_pin); + if (self->dcx_pin >= 0) { + reset_pin_number(self->dcx_pin); + } + if (self->power_pin >= 0) { + reset_pin_number(self->power_pin); + } + if (self->reset_pin >= 0) { + reset_pin_number(self->reset_pin); + } + + self->bus_initialized = false; + self->in_transaction = false; + self->has_pending_command = false; + self->pending_command = 0; + self->transfer_in_progress = false; + self->inflight_transfers = 0; +} + +bool common_hal_qspibus_qspibus_deinited(qspibus_qspibus_obj_t *self) { + return !self->bus_initialized; +} + +void common_hal_qspibus_qspibus_send_command( + qspibus_qspibus_obj_t *self, + uint8_t command, + const uint8_t *data, + size_t len) { + qspibus_send_command_bytes(self, command, data, len); +} + + +void common_hal_qspibus_qspibus_write_command( + qspibus_qspibus_obj_t *self, + uint8_t command) { + if (!self->bus_initialized) { + raise_deinited_error(); + } + if (self->in_transaction) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Bus in display transaction")); + } + + // If caller stages command-only operations repeatedly, flush the previous + // pending command as no-data before replacing it. + if (self->has_pending_command) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + } + + self->pending_command = command; + self->has_pending_command = true; +} + +void common_hal_qspibus_qspibus_write_data( + qspibus_qspibus_obj_t *self, + const uint8_t *data, + size_t len) { + if (!self->bus_initialized) { + raise_deinited_error(); + } + if (self->in_transaction) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Bus in display transaction")); + } + if (len == 0) { + if (self->has_pending_command) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + self->has_pending_command = false; + } + return; + } + if (data == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("Data buffer is null")); + } + if (!self->has_pending_command) { + mp_raise_ValueError(MP_ERROR_TEXT("No pending command")); + } + + if (qspibus_is_color_payload_command(self->pending_command)) { + qspibus_send_color_bytes(self, self->pending_command, data, len); + } else { + qspibus_send_command_bytes(self, self->pending_command, data, len); + } + self->has_pending_command = false; +} + +bool common_hal_qspibus_qspibus_reset(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + if (!self->bus_initialized || self->reset_pin < 0) { + return false; + } + + gpio_set_level((gpio_num_t)self->reset_pin, 0); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level((gpio_num_t)self->reset_pin, 1); + vTaskDelay(pdMS_TO_TICKS(120)); + return true; +} + +bool common_hal_qspibus_qspibus_bus_free(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + return self->bus_initialized && !self->in_transaction && !self->transfer_in_progress && !self->has_pending_command; +} + +bool common_hal_qspibus_qspibus_begin_transaction(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + if (!self->bus_initialized || self->in_transaction) { + return false; + } + self->in_transaction = true; + self->has_pending_command = false; + self->pending_command = 0; + return true; +} + +void common_hal_qspibus_qspibus_send( + mp_obj_t obj, + display_byte_type_t data_type, + display_chip_select_behavior_t chip_select, + const uint8_t *data, + uint32_t data_length) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + (void)chip_select; + if (!self->bus_initialized) { + raise_deinited_error(); + } + if (!self->in_transaction) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Begin transaction first")); + } + + if (data_type == DISPLAY_COMMAND) { + for (uint32_t i = 0; i < data_length; i++) { + if (self->has_pending_command) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + } + self->pending_command = data[i]; + self->has_pending_command = true; + } + return; + } + + if (!self->has_pending_command) { + if (data_length == 0) { + // Zero-length data write after a no-data command is benign. + return; + } + mp_raise_ValueError(MP_ERROR_TEXT("No pending command")); + } + + if (data_length == 0) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + self->has_pending_command = false; + return; + } + + if (qspibus_is_color_payload_command(self->pending_command)) { + qspibus_send_color_bytes(self, self->pending_command, data, data_length); + } else { + qspibus_send_command_bytes(self, self->pending_command, data, data_length); + } + self->has_pending_command = false; +} + +void common_hal_qspibus_qspibus_end_transaction(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + if (!self->bus_initialized) { + return; + } + if (self->has_pending_command) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + self->has_pending_command = false; + } + self->in_transaction = false; +} + +void common_hal_qspibus_qspibus_collect_ptrs(mp_obj_t obj) { + (void)obj; +} diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.h b/ports/espressif/common-hal/qspibus/QSPIBus.h new file mode 100644 index 0000000000000..ba525a0e6edc9 --- /dev/null +++ b/ports/espressif/common-hal/qspibus/QSPIBus.h @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +#include "py/obj.h" + +#include "esp-idf/components/esp_lcd/include/esp_lcd_panel_io.h" +#include "driver/spi_master.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +typedef struct { + mp_obj_base_t base; + + // ESP LCD panel IO handle used for QSPI transactions. + esp_lcd_panel_io_handle_t io_handle; + + // SPI host (SPI2_HOST on ESP32-S3). + spi_host_device_t host_id; + + // Claimed GPIO numbers. + int8_t clock_pin; + int8_t data0_pin; + int8_t data1_pin; + int8_t data2_pin; + int8_t data3_pin; + int8_t cs_pin; + int8_t dcx_pin; // -1 when optional DCX line is not provided. + int8_t reset_pin; // -1 when reset line is not provided. + int8_t power_pin; // -1 when board has no explicit display power pin. + + uint32_t frequency; + bool bus_initialized; + bool in_transaction; + bool has_pending_command; + uint8_t pending_command; + bool transfer_in_progress; + uint8_t active_buffer; + uint8_t inflight_transfers; + size_t dma_buffer_size; + uint8_t *dma_buffer[2]; + + // Signaled from ISR when panel IO transfer completes. + SemaphoreHandle_t transfer_done_sem; +} qspibus_qspibus_obj_t; diff --git a/ports/espressif/common-hal/qspibus/__init__.c b/ports/espressif/common-hal/qspibus/__init__.c new file mode 100644 index 0000000000000..4ac7203fd289b --- /dev/null +++ b/ports/espressif/common-hal/qspibus/__init__.c @@ -0,0 +1,3 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT diff --git a/ports/espressif/common-hal/qspibus/__init__.h b/ports/espressif/common-hal/qspibus/__init__.h new file mode 100644 index 0000000000000..4ac7203fd289b --- /dev/null +++ b/ports/espressif/common-hal/qspibus/__init__.h @@ -0,0 +1,3 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index d8c95d18a79b9..ae7ec7da7b763 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -228,6 +228,9 @@ endif ifeq ($(CIRCUITPY_FOURWIRE),1) SRC_PATTERNS += fourwire/% endif +ifeq ($(CIRCUITPY_QSPIBUS),1) +SRC_PATTERNS += qspibus/% +endif ifeq ($(CIRCUITPY_FRAMEBUFFERIO),1) SRC_PATTERNS += framebufferio/% endif @@ -552,6 +555,8 @@ SRC_COMMON_HAL_ALL = \ pulseio/__init__.c \ pwmio/PWMOut.c \ pwmio/__init__.c \ + qspibus/QSPIBus.c \ + qspibus/__init__.c \ rclcpy/__init__.c \ rclcpy/Node.c \ rclcpy/Publisher.c \ diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index f6310dfaf6855..1db12ac8ae9fd 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -399,6 +399,13 @@ typedef long mp_off_t; #define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (128) #endif +// QSPI display buffer size in uint32_t words for _refresh_area() VLA. +// Allocated on stack; boards should verify sufficient stack headroom. +// Default 512 words = 2KB. Override per-board for larger buffers. +#ifndef CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE +#define CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE (512) +#endif + #else #define CIRCUITPY_DISPLAY_LIMIT (0) #define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (0) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index a1297a4754435..ea8412fa3e6d5 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -262,6 +262,10 @@ CFLAGS += -DCIRCUITPY_BUSDISPLAY=$(CIRCUITPY_BUSDISPLAY) CIRCUITPY_FOURWIRE ?= $(CIRCUITPY_DISPLAYIO) CFLAGS += -DCIRCUITPY_FOURWIRE=$(CIRCUITPY_FOURWIRE) +# QSPI bus protocol for quad-SPI displays (like RM690B0) +CIRCUITPY_QSPIBUS ?= 0 +CFLAGS += -DCIRCUITPY_QSPIBUS=$(CIRCUITPY_QSPIBUS) + CIRCUITPY_EPAPERDISPLAY ?= $(CIRCUITPY_DISPLAYIO) CFLAGS += -DCIRCUITPY_EPAPERDISPLAY=$(CIRCUITPY_EPAPERDISPLAY) diff --git a/shared-bindings/qspibus/QSPIBus.c b/shared-bindings/qspibus/QSPIBus.c new file mode 100644 index 0000000000000..18419bf6c559c --- /dev/null +++ b/shared-bindings/qspibus/QSPIBus.c @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared-bindings/qspibus/QSPIBus.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" + +#include "py/binary.h" +#include "py/obj.h" +#include "py/runtime.h" + +static void check_for_deinit(qspibus_qspibus_obj_t *self) { + if (common_hal_qspibus_qspibus_deinited(self)) { + raise_deinited_error(); + } +} + +//| class QSPIBus: +//| """QSPI bus for quad-SPI displays.""" +//| +//| def __init__( +//| self, +//| *, +//| clock: microcontroller.Pin, +//| data0: microcontroller.Pin, +//| data1: microcontroller.Pin, +//| data2: microcontroller.Pin, +//| data3: microcontroller.Pin, +//| cs: microcontroller.Pin, +//| dcx: Optional[microcontroller.Pin] = None, +//| reset: Optional[microcontroller.Pin] = None, +//| frequency: int = 80_000_000, +//| ) -> None: +//| """Create a QSPIBus object for quad-SPI display communication. +//| +//| :param ~microcontroller.Pin clock: QSPI clock pin +//| :param ~microcontroller.Pin data0: QSPI data line 0 +//| :param ~microcontroller.Pin data1: QSPI data line 1 +//| :param ~microcontroller.Pin data2: QSPI data line 2 +//| :param ~microcontroller.Pin data3: QSPI data line 3 +//| :param ~microcontroller.Pin cs: Chip select pin +//| :param ~microcontroller.Pin dcx: Optional data/command select pin. +//| Reserved for future hardware paths. Current ESP32-S3 implementation +//| uses encoded QSPI command words and does not require explicit DCX. +//| :param ~microcontroller.Pin reset: Optional reset pin +//| :param int frequency: Bus frequency in Hz (1-80MHz) +//| """ +//| ... +//| +static mp_obj_t qspibus_qspibus_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + + enum { ARG_clock, ARG_data0, ARG_data1, ARG_data2, ARG_data3, ARG_cs, ARG_dcx, ARG_reset, ARG_frequency }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data0, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data1, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data2, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data3, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_dcx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_reset, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 80000000} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *data0 = validate_obj_is_free_pin(args[ARG_data0].u_obj, MP_QSTR_data0); + const mcu_pin_obj_t *data1 = validate_obj_is_free_pin(args[ARG_data1].u_obj, MP_QSTR_data1); + const mcu_pin_obj_t *data2 = validate_obj_is_free_pin(args[ARG_data2].u_obj, MP_QSTR_data2); + const mcu_pin_obj_t *data3 = validate_obj_is_free_pin(args[ARG_data3].u_obj, MP_QSTR_data3); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + const mcu_pin_obj_t *dcx = validate_obj_is_free_pin_or_none(args[ARG_dcx].u_obj, MP_QSTR_dcx); + const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj, MP_QSTR_reset); + + uint32_t frequency = (uint32_t)mp_arg_validate_int_range(args[ARG_frequency].u_int, 1, 80000000, MP_QSTR_frequency); + + qspibus_qspibus_obj_t *self = &allocate_display_bus_or_raise()->qspi_bus; + self->base.type = &qspibus_qspibus_type; + common_hal_qspibus_qspibus_construct(self, clock, data0, data1, data2, data3, cs, dcx, reset, frequency); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Release QSPI bus resources and claimed pins.""" +//| ... +//| +static mp_obj_t qspibus_qspibus_deinit(mp_obj_t self_in) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_qspibus_qspibus_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(qspibus_qspibus_deinit_obj, qspibus_qspibus_deinit); + +//| def send(self, command: int, data: ReadableBuffer = b"") -> None: +//| """Send command with optional payload bytes. +//| +//| This mirrors FourWire-style convenience API: +//| - command byte is sent first +//| - optional payload bytes follow +//| """ +//| ... +//| +static mp_obj_t qspibus_qspibus_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_command, ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + uint8_t command = (uint8_t)mp_arg_validate_int_range(args[ARG_command].u_int, 0, 255, MP_QSTR_command); + + const uint8_t *data = NULL; + size_t len = 0; + mp_buffer_info_t data_bufinfo; + if (args[ARG_data].u_obj != mp_const_none) { + mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ); + data = (const uint8_t *)data_bufinfo.buf; + len = data_bufinfo.len; + } + + // Wait for display bus to be available, then acquire transaction. + // Mirrors FourWire.send() pattern: begin_transaction → send → end_transaction. + while (!common_hal_qspibus_qspibus_begin_transaction(MP_OBJ_FROM_PTR(self))) { + RUN_BACKGROUND_TASKS; + } + common_hal_qspibus_qspibus_send(MP_OBJ_FROM_PTR(self), DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, &command, 1); + common_hal_qspibus_qspibus_send(MP_OBJ_FROM_PTR(self), DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data, len); + common_hal_qspibus_qspibus_end_transaction(MP_OBJ_FROM_PTR(self)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(qspibus_qspibus_send_obj, 1, qspibus_qspibus_send); + +//| def write_command(self, command: int) -> None: +//| """Stage a command byte for subsequent :py:meth:`write_data`. +//| +//| If a previously staged command had no data, it is sent as +//| a command-only transaction before staging the new one. +//| """ +//| ... +//| +static mp_obj_t qspibus_qspibus_write_command(mp_obj_t self_in, mp_obj_t command_obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + uint8_t command = (uint8_t)mp_arg_validate_int_range(mp_obj_get_int(command_obj), 0, 255, MP_QSTR_command); + common_hal_qspibus_qspibus_write_command(self, command); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(qspibus_qspibus_write_command_obj, qspibus_qspibus_write_command); + +//| def write_data(self, data: ReadableBuffer) -> None: +//| """Send payload bytes for the most recently staged command.""" +//| ... +//| +static mp_obj_t qspibus_qspibus_write_data(mp_obj_t self_in, mp_obj_t data_obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ); + common_hal_qspibus_qspibus_write_data(self, (const uint8_t *)bufinfo.buf, bufinfo.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(qspibus_qspibus_write_data_obj, qspibus_qspibus_write_data); + +//| def __enter__(self) -> QSPIBus: +//| """No-op context manager entry.""" +//| ... +//| +static mp_obj_t qspibus_qspibus___enter__(mp_obj_t self_in) { + return self_in; +} +static MP_DEFINE_CONST_FUN_OBJ_1(qspibus_qspibus___enter___obj, qspibus_qspibus___enter__); + +//| def __exit__( +//| self, +//| exc_type: type[BaseException] | None, +//| exc_value: BaseException | None, +//| traceback: TracebackType | None, +//| ) -> None: +//| """Deinitialize on context manager exit.""" +//| ... +//| +static mp_obj_t qspibus_qspibus___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_qspibus_qspibus_deinit(MP_OBJ_TO_PTR(args[0])); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qspibus_qspibus___exit___obj, 4, 4, qspibus_qspibus___exit__); + +static const mp_rom_map_elem_t qspibus_qspibus_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&qspibus_qspibus_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&qspibus_qspibus_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_command), MP_ROM_PTR(&qspibus_qspibus_write_command_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_data), MP_ROM_PTR(&qspibus_qspibus_write_data_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&qspibus_qspibus___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&qspibus_qspibus___exit___obj) }, +}; +static MP_DEFINE_CONST_DICT(qspibus_qspibus_locals_dict, qspibus_qspibus_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + qspibus_qspibus_type, + MP_QSTR_QSPIBus, + MP_TYPE_FLAG_NONE, + make_new, qspibus_qspibus_make_new, + locals_dict, &qspibus_qspibus_locals_dict + ); diff --git a/shared-bindings/qspibus/QSPIBus.h b/shared-bindings/qspibus/QSPIBus.h new file mode 100644 index 0000000000000..1055ee7c6a5f5 --- /dev/null +++ b/shared-bindings/qspibus/QSPIBus.h @@ -0,0 +1,57 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" + +#include "shared-bindings/displayio/__init__.h" + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/qspibus/QSPIBus.h" + +extern const mp_obj_type_t qspibus_qspibus_type; + +void common_hal_qspibus_qspibus_construct( + qspibus_qspibus_obj_t *self, + const mcu_pin_obj_t *clock, + const mcu_pin_obj_t *data0, + const mcu_pin_obj_t *data1, + const mcu_pin_obj_t *data2, + const mcu_pin_obj_t *data3, + const mcu_pin_obj_t *cs, + const mcu_pin_obj_t *dcx, + const mcu_pin_obj_t *reset, + uint32_t frequency); + +void common_hal_qspibus_qspibus_deinit(qspibus_qspibus_obj_t *self); +bool common_hal_qspibus_qspibus_deinited(qspibus_qspibus_obj_t *self); + +void common_hal_qspibus_qspibus_send_command( + qspibus_qspibus_obj_t *self, + uint8_t command, + const uint8_t *data, + size_t len); +void common_hal_qspibus_qspibus_write_command( + qspibus_qspibus_obj_t *self, + uint8_t command); +void common_hal_qspibus_qspibus_write_data( + qspibus_qspibus_obj_t *self, + const uint8_t *data, + size_t len); + +bool common_hal_qspibus_qspibus_reset(mp_obj_t obj); +bool common_hal_qspibus_qspibus_bus_free(mp_obj_t obj); +bool common_hal_qspibus_qspibus_begin_transaction(mp_obj_t obj); +void common_hal_qspibus_qspibus_send( + mp_obj_t obj, + display_byte_type_t data_type, + display_chip_select_behavior_t chip_select, + const uint8_t *data, + uint32_t data_length); +void common_hal_qspibus_qspibus_end_transaction(mp_obj_t obj); +void common_hal_qspibus_qspibus_collect_ptrs(mp_obj_t obj); diff --git a/shared-bindings/qspibus/__init__.c b/shared-bindings/qspibus/__init__.c new file mode 100644 index 0000000000000..51a5267e30a73 --- /dev/null +++ b/shared-bindings/qspibus/__init__.c @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/qspibus/__init__.h" +#include "shared-bindings/qspibus/QSPIBus.h" + +//| """QSPI bus protocol for quad-SPI displays +//| +//| The `qspibus` module provides a low-level QSPI bus interface for displays +//| that use four data lines. It is analogous to `fourwire` for standard SPI. +//| +//| Use :class:`qspibus.QSPIBus` to create a bus instance. +//| +//| Example usage:: +//| +//| import board +//| import qspibus +//| import displayio +//| +//| displayio.release_displays() +//| +//| bus = qspibus.QSPIBus( +//| clock=board.LCD_CLK, +//| data0=board.LCD_D0, +//| data1=board.LCD_D1, +//| data2=board.LCD_D2, +//| data3=board.LCD_D3, +//| cs=board.LCD_CS, +//| reset=board.LCD_RESET, +//| frequency=80_000_000, +//| ) +//| """ + +static const mp_rom_map_elem_t qspibus_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_qspibus) }, + { MP_ROM_QSTR(MP_QSTR_QSPIBus), MP_ROM_PTR(&qspibus_qspibus_type) }, +}; + +static MP_DEFINE_CONST_DICT(qspibus_module_globals, qspibus_module_globals_table); + +const mp_obj_module_t qspibus_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&qspibus_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_qspibus, qspibus_module); diff --git a/shared-bindings/qspibus/__init__.h b/shared-bindings/qspibus/__init__.h new file mode 100644 index 0000000000000..3604bf04c0e22 --- /dev/null +++ b/shared-bindings/qspibus/__init__.h @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/qspibus/QSPIBus.h" diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index 6ef1c4c7f66ce..3a8d2721af764 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -16,6 +16,9 @@ #if CIRCUITPY_PARALLELDISPLAYBUS #include "shared-bindings/paralleldisplaybus/ParallelBus.h" #endif +#if CIRCUITPY_QSPIBUS +#include "shared-bindings/qspibus/QSPIBus.h" +#endif #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/__init__.h" @@ -254,6 +257,50 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } } + #if CIRCUITPY_QSPIBUS + // QSPI panels benefit from larger sub-rectangle buffers because each chunk + // has non-trivial command/window overhead. Keep this path qspibus-specific + // to avoid increasing stack usage on other display buses. + if (mp_obj_is_type(self->bus.bus, &qspibus_qspibus_type) && + self->core.colorspace.depth == 16 && + !self->bus.data_as_commands && + !self->bus.SH1107_addressing && + buffer_size < CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE) { + buffer_size = CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE; + rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped); + if (rows_per_buffer == 0) { + rows_per_buffer = 1; + } + subrectangles = displayio_area_height(&clipped) / rows_per_buffer; + if (displayio_area_height(&clipped) % rows_per_buffer != 0) { + subrectangles++; + } + pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); + buffer_size = pixels_per_buffer / pixels_per_word; + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + } + + if (mp_obj_is_type(self->bus.bus, &qspibus_qspibus_type) && + self->core.colorspace.depth == 16 && + !self->bus.data_as_commands && + displayio_area_height(&clipped) > 1 && + rows_per_buffer < 2 && + (2 * displayio_area_width(&clipped) + pixels_per_word - 1) / pixels_per_word <= CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE) { + rows_per_buffer = 2; + subrectangles = displayio_area_height(&clipped) / rows_per_buffer; + if (displayio_area_height(&clipped) % rows_per_buffer != 0) { + subrectangles++; + } + pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); + buffer_size = pixels_per_buffer / pixels_per_word; + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + } + #endif + // Allocated and shared as a uint32_t array so the compiler knows the // alignment everywhere. uint32_t buffer[buffer_size]; diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index e325a7857f464..ce90bf8f1c327 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -189,6 +189,10 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { } else if (bus_type == &mipidsi_display_type) { common_hal_mipidsi_display_deinit(&display_buses[i].mipidsi); #endif + #if CIRCUITPY_QSPIBUS + } else if (bus_type == &qspibus_qspibus_type) { + common_hal_qspibus_qspibus_deinit(&display_buses[i].qspi_bus); + #endif } display_buses[i].bus_base.type = &mp_type_NoneType; } diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index f2f4691b313a7..0c6a881ef621c 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -41,6 +41,9 @@ #if CIRCUITPY_MIPIDSI #include "shared-bindings/mipidsi/Display.h" #endif +#if CIRCUITPY_QSPIBUS +#include "shared-bindings/qspibus/QSPIBus.h" +#endif // Port unique frame buffers. #if CIRCUITPY_VIDEOCORE #include "bindings/videocore/Framebuffer.h" @@ -87,6 +90,9 @@ typedef struct { #if CIRCUITPY_MIPIDSI mipidsi_display_obj_t mipidsi; #endif + #if CIRCUITPY_QSPIBUS + qspibus_qspibus_obj_t qspi_bus; + #endif }; } primary_display_bus_t; diff --git a/shared-module/displayio/bus_core.c b/shared-module/displayio/bus_core.c index e01b9d9eef685..3a181181051da 100644 --- a/shared-module/displayio/bus_core.c +++ b/shared-module/displayio/bus_core.c @@ -17,6 +17,9 @@ #if CIRCUITPY_PARALLELDISPLAYBUS #include "shared-bindings/paralleldisplaybus/ParallelBus.h" #endif +#if CIRCUITPY_QSPIBUS +#include "shared-bindings/qspibus/QSPIBus.h" +#endif #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/__init__.h" @@ -79,6 +82,16 @@ void displayio_display_bus_construct(displayio_display_bus_t *self, self->collect_ptrs = common_hal_i2cdisplaybus_i2cdisplaybus_collect_ptrs; } else #endif + #if CIRCUITPY_QSPIBUS + if (mp_obj_is_type(bus, &qspibus_qspibus_type)) { + self->bus_reset = common_hal_qspibus_qspibus_reset; + self->bus_free = common_hal_qspibus_qspibus_bus_free; + self->begin_transaction = common_hal_qspibus_qspibus_begin_transaction; + self->send = common_hal_qspibus_qspibus_send; + self->end_transaction = common_hal_qspibus_qspibus_end_transaction; + self->collect_ptrs = common_hal_qspibus_qspibus_collect_ptrs; + } else + #endif { mp_raise_ValueError(MP_ERROR_TEXT("Unsupported display bus type")); } From f74912453db3f74f90178bcb5cf26f58faecc92e Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Tue, 24 Feb 2026 23:51:19 +0100 Subject: [PATCH 371/517] qspibus: address code review feedback - Reuse existing CircuitPython error messages to reduce translation burden: "QSPI command/color timeout" -> "Operation timed out", "QSPI send[color] failed: %d" / "SPI bus init failed: %d" / "Panel IO init failed: %d" -> "%q failure: %d", "Failed to allocate DMA buffers" / "QSPI DMA buffers unavailable" -> "Could not allocate DMA capable buffer", "Data buffer is null" -> "Buffer too small". Net removal of 8 unique translatable strings with 0 new ones. - Regenerate locale/circuitpython.pot to remove stale entries. - Add _Static_assert in BusDisplay.c to guard the QSPI stack-allocated refresh buffer size (max 2048 uint32_t words = 8KB). - Add comment clarifying that inflight_transfers bookkeeping is task-context only (ISR only signals semaphore), so no atomics needed. - Fix SPDX file header format across all new qspibus files to match the CircuitPython project convention. --- locale/circuitpython.pot | 41 ++----------------- .../waveshare_esp32_s3_amoled_241/board.c | 2 +- .../mpconfigboard.h | 2 +- .../waveshare_esp32_s3_amoled_241/pins.c | 2 +- ports/espressif/common-hal/qspibus/QSPIBus.c | 25 ++++++----- ports/espressif/common-hal/qspibus/QSPIBus.h | 2 +- ports/espressif/common-hal/qspibus/__init__.c | 2 +- ports/espressif/common-hal/qspibus/__init__.h | 2 +- shared-bindings/qspibus/QSPIBus.c | 2 +- shared-bindings/qspibus/QSPIBus.h | 2 +- shared-bindings/qspibus/__init__.c | 2 +- shared-bindings/qspibus/__init__.h | 2 +- shared-module/busdisplay/BusDisplay.c | 4 ++ 13 files changed, 32 insertions(+), 58 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 1f3d4c0974a0f..7c66d1de8ce7a 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -99,6 +99,7 @@ msgid "%q contains duplicate pins" msgstr "" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "" @@ -710,6 +711,7 @@ msgid "Buffer too short by %d bytes" msgstr "" #: ports/cxd56/common-hal/camera/Camera.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/struct/__init__.c shared-module/struct/__init__.c @@ -872,7 +874,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1029,10 +1031,6 @@ msgstr "" msgid "Failed to allocate %q buffer" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Failed to allocate DMA buffers" -msgstr "" - #: ports/espressif/common-hal/wifi/__init__.c msgid "Failed to allocate Wifi memory" msgstr "" @@ -1770,6 +1768,7 @@ msgid "Operation or feature not supported" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "" @@ -1807,11 +1806,6 @@ msgstr "" msgid "Packet buffers for an SPI transfer must have the same length." msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -#, c-format -msgid "Panel IO init failed: %d" -msgstr "" - #: shared-module/jpegio/JpegDecoder.c msgid "Parameter error" msgstr "" @@ -1918,28 +1912,6 @@ msgstr "" msgid "Pull not used when direction is output." msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "QSPI DMA buffers unavailable" -msgstr "" - -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "QSPI color timeout" -msgstr "" - -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "QSPI command timeout" -msgstr "" - -#: ports/espressif/common-hal/qspibus/QSPIBus.c -#, c-format -msgid "QSPI send color failed: %d" -msgstr "" - -#: ports/espressif/common-hal/qspibus/QSPIBus.c -#, c-format -msgid "QSPI send failed: %d" -msgstr "" - #: ports/raspberrypi/common-hal/countio/Counter.c msgid "RISE_AND_FALL not available on this chip" msgstr "" @@ -2061,11 +2033,6 @@ msgstr "" msgid "SDIO Init Error %x" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -#, c-format -msgid "SPI bus init failed: %d" -msgstr "" - #: ports/espressif/common-hal/busio/SPI.c msgid "SPI configuration failed" msgstr "" diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c index 10ef3d7274c48..3941e89a5e4d7 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #include "supervisor/board.h" diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h index b4d4df961bf41..d31dd2e0e84f4 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #pragma once diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c index 437488e3f2b52..ed03341923326 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #include "py/obj.h" diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index 7182869b6b600..5041c826affe8 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #include "shared-bindings/qspibus/QSPIBus.h" @@ -121,7 +121,7 @@ static void qspibus_send_command_bytes( if (self->inflight_transfers >= QSPI_DMA_BUFFER_COUNT) { if (!qspibus_wait_one_transfer_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { qspibus_reset_transfer_state(self); - mp_raise_OSError_msg(MP_ERROR_TEXT("QSPI command timeout")); + mp_raise_OSError_msg(MP_ERROR_TEXT("Operation timed out")); } } @@ -129,7 +129,7 @@ static void qspibus_send_command_bytes( esp_err_t err = esp_lcd_panel_io_tx_param(self->io_handle, packed_cmd, data, len); if (err != ESP_OK) { qspibus_reset_transfer_state(self); - mp_raise_OSError_msg_varg(MP_ERROR_TEXT("QSPI send failed: %d"), err); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_QSPI, (int)err); } } @@ -148,7 +148,7 @@ static void qspibus_send_color_bytes( return; } if (data == NULL || self->dma_buffer_size == 0) { - mp_raise_OSError_msg(MP_ERROR_TEXT("QSPI DMA buffers unavailable")); + mp_raise_OSError_msg(MP_ERROR_TEXT("Could not allocate DMA capable buffer")); } // RAMWR must transition to RAMWRC for continued payload chunks. @@ -157,10 +157,13 @@ static void qspibus_send_color_bytes( size_t remaining = len; while (remaining > 0) { + // inflight_transfers is only modified in task context (never from ISR), + // so no atomic/critical section is needed. The ISR only signals the + // counting semaphore; all counter bookkeeping happens task-side. if (self->inflight_transfers >= QSPI_DMA_BUFFER_COUNT) { if (!qspibus_wait_one_transfer_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { qspibus_reset_transfer_state(self); - mp_raise_OSError_msg(MP_ERROR_TEXT("QSPI color timeout")); + mp_raise_OSError_msg(MP_ERROR_TEXT("Operation timed out")); } } @@ -176,7 +179,7 @@ static void qspibus_send_color_bytes( esp_err_t err = esp_lcd_panel_io_tx_color(self->io_handle, packed_cmd, buffer, chunk); if (err != ESP_OK) { qspibus_reset_transfer_state(self); - mp_raise_OSError_msg_varg(MP_ERROR_TEXT("QSPI send color failed: %d"), err); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_QSPI, (int)err); } self->inflight_transfers++; @@ -195,7 +198,7 @@ static void qspibus_send_color_bytes( // after queued DMA chunks have completed. if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { qspibus_reset_transfer_state(self); - mp_raise_OSError_msg(MP_ERROR_TEXT("QSPI color timeout")); + mp_raise_OSError_msg(MP_ERROR_TEXT("Operation timed out")); } } @@ -290,7 +293,7 @@ void common_hal_qspibus_qspibus_construct( if (!qspibus_allocate_dma_buffers(self)) { vSemaphoreDelete(self->transfer_done_sem); self->transfer_done_sem = NULL; - mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to allocate DMA buffers")); + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); } const spi_bus_config_t bus_config = { @@ -308,7 +311,7 @@ void common_hal_qspibus_qspibus_construct( qspibus_release_dma_buffers(self); vSemaphoreDelete(self->transfer_done_sem); self->transfer_done_sem = NULL; - mp_raise_OSError_msg_varg(MP_ERROR_TEXT("SPI bus init failed: %d"), err); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_SPI, (int)err); } const esp_lcd_panel_io_spi_config_t io_config = { @@ -332,7 +335,7 @@ void common_hal_qspibus_qspibus_construct( qspibus_release_dma_buffers(self); vSemaphoreDelete(self->transfer_done_sem); self->transfer_done_sem = NULL; - mp_raise_OSError_msg_varg(MP_ERROR_TEXT("Panel IO init failed: %d"), err); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_QSPI, (int)err); } claim_pin(clock); @@ -472,7 +475,7 @@ void common_hal_qspibus_qspibus_write_data( return; } if (data == NULL) { - mp_raise_ValueError(MP_ERROR_TEXT("Data buffer is null")); + mp_raise_ValueError(MP_ERROR_TEXT("Buffer too small")); } if (!self->has_pending_command) { mp_raise_ValueError(MP_ERROR_TEXT("No pending command")); diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.h b/ports/espressif/common-hal/qspibus/QSPIBus.h index ba525a0e6edc9..caed3661cfd73 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.h +++ b/ports/espressif/common-hal/qspibus/QSPIBus.h @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #pragma once diff --git a/ports/espressif/common-hal/qspibus/__init__.c b/ports/espressif/common-hal/qspibus/__init__.c index 4ac7203fd289b..2f763b218cb43 100644 --- a/ports/espressif/common-hal/qspibus/__init__.c +++ b/ports/espressif/common-hal/qspibus/__init__.c @@ -1,3 +1,3 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT diff --git a/ports/espressif/common-hal/qspibus/__init__.h b/ports/espressif/common-hal/qspibus/__init__.h index 4ac7203fd289b..2f763b218cb43 100644 --- a/ports/espressif/common-hal/qspibus/__init__.h +++ b/ports/espressif/common-hal/qspibus/__init__.h @@ -1,3 +1,3 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT diff --git a/shared-bindings/qspibus/QSPIBus.c b/shared-bindings/qspibus/QSPIBus.c index 18419bf6c559c..60f83682ba077 100644 --- a/shared-bindings/qspibus/QSPIBus.c +++ b/shared-bindings/qspibus/QSPIBus.c @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #include diff --git a/shared-bindings/qspibus/QSPIBus.h b/shared-bindings/qspibus/QSPIBus.h index 1055ee7c6a5f5..8b8e7f76b3f38 100644 --- a/shared-bindings/qspibus/QSPIBus.h +++ b/shared-bindings/qspibus/QSPIBus.h @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #pragma once diff --git a/shared-bindings/qspibus/__init__.c b/shared-bindings/qspibus/__init__.c index 51a5267e30a73..0281c41c80420 100644 --- a/shared-bindings/qspibus/__init__.c +++ b/shared-bindings/qspibus/__init__.c @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #include "py/obj.h" diff --git a/shared-bindings/qspibus/__init__.h b/shared-bindings/qspibus/__init__.h index 3604bf04c0e22..9b4ca24332703 100644 --- a/shared-bindings/qspibus/__init__.h +++ b/shared-bindings/qspibus/__init__.h @@ -1,5 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org // SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -// // SPDX-License-Identifier: MIT #pragma once diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index 3a8d2721af764..4c1fcb2c45805 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -261,6 +261,10 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are // QSPI panels benefit from larger sub-rectangle buffers because each chunk // has non-trivial command/window overhead. Keep this path qspibus-specific // to avoid increasing stack usage on other display buses. + // Guard: buffer is a VLA on stack; 2048 uint32_t words = 8KB is the safe + // upper bound for ESP32-S3's 24KB main task stack. + _Static_assert(CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE <= 2048, + "CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE exceeds safe stack limit (8KB)"); if (mp_obj_is_type(self->bus.bus, &qspibus_qspibus_type) && self->core.colorspace.depth == 16 && !self->bus.data_as_commands && From 21bf0f7ed54bc200037fe4877a5bfa088022405b Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 25 Feb 2026 00:02:34 +0100 Subject: [PATCH 372/517] Update: Fixes in locale/circuitpython.pot --- locale/circuitpython.pot | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 7c66d1de8ce7a..5d9a525c4795a 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -874,7 +874,8 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +#: shared-module/usb/core/Device.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Could not allocate DMA capable buffer" msgstr "" From 48aaf516739171dbde8fe41be19261b2c984e58e Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 25 Feb 2026 00:31:53 +0100 Subject: [PATCH 373/517] qspibus: align Python API with FourWire/ParallelBus conventions - Add reset() method binding (common_hal implementation already existed) with "No %q pin" error matching FourWire/ParallelBus pattern. - Remove deinit(), __enter__(), __exit__() from Python API; display bus lifecycle is managed through displayio.release_displays(). - Remove two unreachable data == NULL guards: Python binding validates buffers via mp_get_buffer_raise(), and displayio always passes valid pointers. --- ports/espressif/common-hal/qspibus/QSPIBus.c | 5 +-- shared-bindings/qspibus/QSPIBus.c | 45 ++++++-------------- 2 files changed, 13 insertions(+), 37 deletions(-) diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index 5041c826affe8..f6dc10f0b39e1 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -147,7 +147,7 @@ static void qspibus_send_color_bytes( qspibus_send_command_bytes(self, command, NULL, 0); return; } - if (data == NULL || self->dma_buffer_size == 0) { + if (self->dma_buffer_size == 0) { mp_raise_OSError_msg(MP_ERROR_TEXT("Could not allocate DMA capable buffer")); } @@ -474,9 +474,6 @@ void common_hal_qspibus_qspibus_write_data( } return; } - if (data == NULL) { - mp_raise_ValueError(MP_ERROR_TEXT("Buffer too small")); - } if (!self->has_pending_command) { mp_raise_ValueError(MP_ERROR_TEXT("No pending command")); } diff --git a/shared-bindings/qspibus/QSPIBus.c b/shared-bindings/qspibus/QSPIBus.c index 60f83682ba077..398be4d38c411 100644 --- a/shared-bindings/qspibus/QSPIBus.c +++ b/shared-bindings/qspibus/QSPIBus.c @@ -89,16 +89,22 @@ static mp_obj_t qspibus_qspibus_make_new(const mp_obj_type_t *type, size_t n_arg return MP_OBJ_FROM_PTR(self); } -//| def deinit(self) -> None: -//| """Release QSPI bus resources and claimed pins.""" +//| def reset(self) -> None: +//| """Perform a hardware reset using the reset pin. +//| +//| :raises RuntimeError: if no reset pin was provided at construction. +//| """ //| ... //| -static mp_obj_t qspibus_qspibus_deinit(mp_obj_t self_in) { +static mp_obj_t qspibus_qspibus_obj_reset(mp_obj_t self_in) { qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_qspibus_qspibus_deinit(self); + check_for_deinit(self); + if (!common_hal_qspibus_qspibus_reset(MP_OBJ_FROM_PTR(self))) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("No %q pin"), MP_QSTR_reset); + } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_1(qspibus_qspibus_deinit_obj, qspibus_qspibus_deinit); +static MP_DEFINE_CONST_FUN_OBJ_1(qspibus_qspibus_reset_obj, qspibus_qspibus_obj_reset); //| def send(self, command: int, data: ReadableBuffer = b"") -> None: //| """Send command with optional payload bytes. @@ -178,38 +184,11 @@ static mp_obj_t qspibus_qspibus_write_data(mp_obj_t self_in, mp_obj_t data_obj) } MP_DEFINE_CONST_FUN_OBJ_2(qspibus_qspibus_write_data_obj, qspibus_qspibus_write_data); -//| def __enter__(self) -> QSPIBus: -//| """No-op context manager entry.""" -//| ... -//| -static mp_obj_t qspibus_qspibus___enter__(mp_obj_t self_in) { - return self_in; -} -static MP_DEFINE_CONST_FUN_OBJ_1(qspibus_qspibus___enter___obj, qspibus_qspibus___enter__); - -//| def __exit__( -//| self, -//| exc_type: type[BaseException] | None, -//| exc_value: BaseException | None, -//| traceback: TracebackType | None, -//| ) -> None: -//| """Deinitialize on context manager exit.""" -//| ... -//| -static mp_obj_t qspibus_qspibus___exit__(size_t n_args, const mp_obj_t *args) { - (void)n_args; - common_hal_qspibus_qspibus_deinit(MP_OBJ_TO_PTR(args[0])); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(qspibus_qspibus___exit___obj, 4, 4, qspibus_qspibus___exit__); - static const mp_rom_map_elem_t qspibus_qspibus_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&qspibus_qspibus_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&qspibus_qspibus_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&qspibus_qspibus_send_obj) }, { MP_ROM_QSTR(MP_QSTR_write_command), MP_ROM_PTR(&qspibus_qspibus_write_command_obj) }, { MP_ROM_QSTR(MP_QSTR_write_data), MP_ROM_PTR(&qspibus_qspibus_write_data_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&qspibus_qspibus___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&qspibus_qspibus___exit___obj) }, }; static MP_DEFINE_CONST_DICT(qspibus_qspibus_locals_dict, qspibus_qspibus_locals_dict_table); From 1518226147da04f0bf4c51cbbc6b6c68d3478b8e Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 25 Feb 2026 00:53:19 +0100 Subject: [PATCH 374/517] qspibus: fix begin_transaction() consistency and regenerate .pot - Align begin_transaction() with bus_free() by delegating to it, so transfer_in_progress and has_pending_command are checked before entering a transaction. This prevents silently discarding a staged write_command() or starting a transaction during active DMA. - Regenerate locale/circuitpython.pot to remove stale "Data buffer is null" entry left over from the previous round of error message reuse. --- locale/circuitpython.pot | 10 ++-------- ports/espressif/common-hal/qspibus/QSPIBus.c | 4 +--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 5d9a525c4795a..2779501a19b35 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -711,7 +711,6 @@ msgid "Buffer too short by %d bytes" msgstr "" #: ports/cxd56/common-hal/camera/Camera.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/struct/__init__.c shared-module/struct/__init__.c @@ -874,8 +873,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -917,10 +915,6 @@ msgstr "" msgid "Data 0 pin must be byte aligned" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Data buffer is null" -msgstr "" - #: shared-module/jpegio/JpegDecoder.c msgid "Data format error (may be broken data)" msgstr "" @@ -1536,7 +1530,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "" diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index f6dc10f0b39e1..348ac317da4d4 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -506,12 +506,10 @@ bool common_hal_qspibus_qspibus_bus_free(mp_obj_t obj) { bool common_hal_qspibus_qspibus_begin_transaction(mp_obj_t obj) { qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); - if (!self->bus_initialized || self->in_transaction) { + if (!common_hal_qspibus_qspibus_bus_free(obj)) { return false; } self->in_transaction = true; - self->has_pending_command = false; - self->pending_command = 0; return true; } From 25709c1003728ee04f52e9926404e72ef1de49df Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 00:59:29 +0100 Subject: [PATCH 375/517] qspibus: locale fixes and API cleanups for rm690b0 QSPI display driver (#1) * Initial plan * Fix stale and missing locale entries for qspibus Co-authored-by: ppsx <7107135+ppsx@users.noreply.github.com> * qspibus: three low-cost cleanups consistent with CircuitPython conventions Co-authored-by: ppsx <7107135+ppsx@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ppsx <7107135+ppsx@users.noreply.github.com> Co-authored-by: ppsx --- locale/circuitpython.pot | 8 +++----- ports/espressif/common-hal/qspibus/QSPIBus.c | 11 +---------- shared-bindings/qspibus/QSPIBus.c | 4 ++-- shared-bindings/qspibus/QSPIBus.h | 5 ----- 4 files changed, 6 insertions(+), 22 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 2779501a19b35..b01cd44500308 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -976,6 +976,7 @@ msgstr "" #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "" @@ -1063,10 +1064,6 @@ msgstr "" msgid "Failed to create continuous channels: not found" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Failed to create semaphore" -msgstr "" - #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to enable continuous" msgstr "" @@ -1530,7 +1527,8 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c +#: shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "" diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index 348ac317da4d4..bda037c886e1e 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -287,7 +287,7 @@ void common_hal_qspibus_qspibus_construct( self->transfer_done_sem = xSemaphoreCreateCounting(QSPI_DMA_BUFFER_COUNT, 0); if (self->transfer_done_sem == NULL) { - mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to create semaphore")); + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("ESP-IDF memory allocation failed")); } if (!qspibus_allocate_dma_buffers(self)) { @@ -428,15 +428,6 @@ bool common_hal_qspibus_qspibus_deinited(qspibus_qspibus_obj_t *self) { return !self->bus_initialized; } -void common_hal_qspibus_qspibus_send_command( - qspibus_qspibus_obj_t *self, - uint8_t command, - const uint8_t *data, - size_t len) { - qspibus_send_command_bytes(self, command, data, len); -} - - void common_hal_qspibus_qspibus_write_command( qspibus_qspibus_obj_t *self, uint8_t command) { diff --git a/shared-bindings/qspibus/QSPIBus.c b/shared-bindings/qspibus/QSPIBus.c index 398be4d38c411..cc4fdc91c0669 100644 --- a/shared-bindings/qspibus/QSPIBus.c +++ b/shared-bindings/qspibus/QSPIBus.c @@ -106,7 +106,7 @@ static mp_obj_t qspibus_qspibus_obj_reset(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(qspibus_qspibus_reset_obj, qspibus_qspibus_obj_reset); -//| def send(self, command: int, data: ReadableBuffer = b"") -> None: +//| def send(self, command: int, data: Optional[ReadableBuffer] = None) -> None: //| """Send command with optional payload bytes. //| //| This mirrors FourWire-style convenience API: @@ -119,7 +119,7 @@ static mp_obj_t qspibus_qspibus_send(size_t n_args, const mp_obj_t *pos_args, mp enum { ARG_command, ARG_data }; static const mp_arg_t allowed_args[] = { { MP_QSTR_command, MP_ARG_INT | MP_ARG_REQUIRED }, - { MP_QSTR_data, MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} }, + { MP_QSTR_data, MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; diff --git a/shared-bindings/qspibus/QSPIBus.h b/shared-bindings/qspibus/QSPIBus.h index 8b8e7f76b3f38..a5d8f824ad28e 100644 --- a/shared-bindings/qspibus/QSPIBus.h +++ b/shared-bindings/qspibus/QSPIBus.h @@ -31,11 +31,6 @@ void common_hal_qspibus_qspibus_construct( void common_hal_qspibus_qspibus_deinit(qspibus_qspibus_obj_t *self); bool common_hal_qspibus_qspibus_deinited(qspibus_qspibus_obj_t *self); -void common_hal_qspibus_qspibus_send_command( - qspibus_qspibus_obj_t *self, - uint8_t command, - const uint8_t *data, - size_t len); void common_hal_qspibus_qspibus_write_command( qspibus_qspibus_obj_t *self, uint8_t command); From 6c2a5ce6e596c3aab2d36243dbca838473f656dd Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 25 Feb 2026 14:29:17 +0100 Subject: [PATCH 376/517] The last license header and the temple of doom (pot file) --- locale/circuitpython.pot | 3 ++- .../boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index b01cd44500308..e19fcc266eb48 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -873,7 +873,8 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +#: shared-module/usb/core/Device.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Could not allocate DMA capable buffer" msgstr "" diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk index 0546d90693390..127eec2e8b6fe 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk @@ -1,5 +1,5 @@ +# This file is part of the CircuitPython project: https://circuitpython.org # SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha -# # SPDX-License-Identifier: MIT CIRCUITPY_CREATOR_ID = 0x57415645 # 'WAVE' (Waveshare) From cb468b8510cfea852cbad65cfa8d2736031af646 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 25 Feb 2026 20:56:54 +0100 Subject: [PATCH 377/517] ~20% improvements in data transfer when drawing --- ports/espressif/common-hal/qspibus/QSPIBus.c | 33 +++-- shared-module/busdisplay/BusDisplay.c | 120 +++++++++++++------ shared-module/displayio/TileGrid.c | 69 +++++++++++ shared-module/displayio/bus_core.h | 6 + 4 files changed, 183 insertions(+), 45 deletions(-) diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index bda037c886e1e..0e74fcf1fdf95 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -194,12 +194,11 @@ static void qspibus_send_color_bytes( remaining -= chunk; } - // Keep Python/API semantics predictable: color transfer call returns only - // after queued DMA chunks have completed. - if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { - qspibus_reset_transfer_state(self); - mp_raise_OSError_msg(MP_ERROR_TEXT("Operation timed out")); - } + // Let DMA complete asynchronously. The next begin_transaction() will + // wait for all in-flight transfers, allowing fill_area() computation + // to overlap with DMA. The explicit wait is only needed for the + // Python write_data() API path where callers expect the transfer to + // be finished on return. } static bool qspibus_is_color_payload_command(uint8_t command) { @@ -465,12 +464,19 @@ void common_hal_qspibus_qspibus_write_data( } return; } - if (!self->has_pending_command) { + if (!self->has_pending_command) { mp_raise_ValueError(MP_ERROR_TEXT("No pending command")); } if (qspibus_is_color_payload_command(self->pending_command)) { qspibus_send_color_bytes(self, self->pending_command, data, len); + // Python API: wait for DMA to finish so callers see the transfer as + // complete on return. The internal displayio path skips this wait + // to allow fill_area/DMA overlap. + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg(MP_ERROR_TEXT("Operation timed out")); + } } else { qspibus_send_command_bytes(self, self->pending_command, data, len); } @@ -497,9 +503,20 @@ bool common_hal_qspibus_qspibus_bus_free(mp_obj_t obj) { bool common_hal_qspibus_qspibus_begin_transaction(mp_obj_t obj) { qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); - if (!common_hal_qspibus_qspibus_bus_free(obj)) { + if (!self->bus_initialized || self->in_transaction || self->has_pending_command) { return false; } + // Wait for any in-flight DMA to complete before starting a new + // transaction. This replaces the old non-blocking bus_free() check + // and enables fill_area()/DMA overlap: the CPU fills the next + // subrectangle while the previous DMA runs, and this wait only + // blocks when we actually need the bus for the next send. + if (self->transfer_in_progress) { + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + return false; + } + } self->in_transaction = true; return true; } diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index 4c1fcb2c45805..1994a25214c34 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -217,7 +217,7 @@ static void _send_pixels(busdisplay_busdisplay_obj_t *self, uint8_t *pixels, uin } static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_area_t *area) { - uint16_t buffer_size = 128; // In uint32_ts + uint32_t buffer_size = 128; // In uint32_ts displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. @@ -226,7 +226,7 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } uint16_t rows_per_buffer = displayio_area_height(&clipped); uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; - uint16_t pixels_per_buffer = displayio_area_size(&clipped); + uint32_t pixels_per_buffer = displayio_area_size(&clipped); uint16_t subrectangles = 1; // for SH1107 and other boundary constrained controllers @@ -265,7 +265,8 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are // upper bound for ESP32-S3's 24KB main task stack. _Static_assert(CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE <= 2048, "CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE exceeds safe stack limit (8KB)"); - if (mp_obj_is_type(self->bus.bus, &qspibus_qspibus_type) && + bool is_qspi_bus = mp_obj_is_type(self->bus.bus, &qspibus_qspibus_type); + if (is_qspi_bus && self->core.colorspace.depth == 16 && !self->bus.data_as_commands && !self->bus.SH1107_addressing && @@ -275,6 +276,10 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are if (rows_per_buffer == 0) { rows_per_buffer = 1; } + // Clamp to actual display height. + if (rows_per_buffer > displayio_area_height(&clipped)) { + rows_per_buffer = displayio_area_height(&clipped); + } subrectangles = displayio_area_height(&clipped) / rows_per_buffer; if (displayio_area_height(&clipped) % rows_per_buffer != 0) { subrectangles++; @@ -284,32 +289,31 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are if (pixels_per_buffer % pixels_per_word) { buffer_size += 1; } - } - if (mp_obj_is_type(self->bus.bus, &qspibus_qspibus_type) && - self->core.colorspace.depth == 16 && - !self->bus.data_as_commands && - displayio_area_height(&clipped) > 1 && - rows_per_buffer < 2 && - (2 * displayio_area_width(&clipped) + pixels_per_word - 1) / pixels_per_word <= CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE) { - rows_per_buffer = 2; - subrectangles = displayio_area_height(&clipped) / rows_per_buffer; - if (displayio_area_height(&clipped) % rows_per_buffer != 0) { - subrectangles++; - } - pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); - buffer_size = pixels_per_buffer / pixels_per_word; - if (pixels_per_buffer % pixels_per_word) { - buffer_size += 1; + // Ensure at least 2 rows per buffer when possible. + if (rows_per_buffer < 2 && + displayio_area_height(&clipped) > 1 && + (uint32_t)((2 * displayio_area_width(&clipped) + pixels_per_word - 1) / pixels_per_word) <= (uint32_t)CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE) { + rows_per_buffer = 2; + subrectangles = displayio_area_height(&clipped) / rows_per_buffer; + if (displayio_area_height(&clipped) % rows_per_buffer != 0) { + subrectangles++; + } + pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); + buffer_size = pixels_per_buffer / pixels_per_word; + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } } } #endif // Allocated and shared as a uint32_t array so the compiler knows the // alignment everywhere. - uint32_t buffer[buffer_size]; uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t buffer[buffer_size]; uint32_t mask[mask_length]; + uint16_t remaining_rows = displayio_area_height(&clipped); for (uint16_t j = 0; j < subrectangles; j++) { @@ -324,28 +328,55 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } remaining_rows -= rows_per_buffer; - displayio_display_bus_set_region_to_update(&self->bus, &self->core, &subrectangle); + #if CIRCUITPY_QSPIBUS + if (is_qspi_bus) { + // QSPI path: fill_area first (overlaps with previous DMA), + // then single-transaction set_region + RAMWR + pixels. + // depth is always 16 here (guarded by is_qspi_bus check above). + uint32_t subrectangle_size_bytes = (uint32_t)displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); - uint16_t subrectangle_size_bytes; - if (self->core.colorspace.depth >= 8) { - subrectangle_size_bytes = displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); - } else { - subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); - } + memset(mask, 0, mask_length * sizeof(mask[0])); + memset(buffer, 0, buffer_size * sizeof(buffer[0])); + + displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); - memset(mask, 0, mask_length * sizeof(mask[0])); - memset(buffer, 0, buffer_size * sizeof(buffer[0])); + // begin_transaction waits for any prior async DMA to finish, + // so fill_area above overlaps with previous DMA. + if (!displayio_display_bus_begin_transaction(&self->bus)) { + // Transaction failed (bus deinitialized, timeout, etc.). + // Bail out to prevent calling send() outside a transaction. + return false; + } + displayio_display_bus_send_region_commands(&self->bus, &self->core, &subrectangle); + _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); + displayio_display_bus_end_transaction(&self->bus); + } else + #endif + { + // Non-QSPI path: original ordering preserved exactly. + displayio_display_bus_set_region_to_update(&self->bus, &self->core, &subrectangle); + + uint16_t subrectangle_size_bytes; + if (self->core.colorspace.depth >= 8) { + subrectangle_size_bytes = displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); + } else { + subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); + } - displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); + memset(mask, 0, mask_length * sizeof(mask[0])); + memset(buffer, 0, buffer_size * sizeof(buffer[0])); - // Can't acquire display bus; skip the rest of the data. - if (!displayio_display_bus_is_free(&self->bus)) { - return false; - } + displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); - displayio_display_bus_begin_transaction(&self->bus); - _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); - displayio_display_bus_end_transaction(&self->bus); + // Can't acquire display bus; skip the rest of the data. + if (!displayio_display_bus_is_free(&self->bus)) { + return false; + } + + displayio_display_bus_begin_transaction(&self->bus); + _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); + displayio_display_bus_end_transaction(&self->bus); + } // Run background tasks so they can run during an explicit refresh. // Auto-refresh won't run background tasks here because it is a background task itself. @@ -356,6 +387,21 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are usb_background(); #endif } + + #if CIRCUITPY_QSPIBUS + if (is_qspi_bus) { + // Drain the last async DMA transfer before returning. + // Within the loop, begin_transaction() waits for the PREVIOUS + // subrectangle's DMA, enabling fill_area/DMA overlap. But the + // LAST subrectangle's DMA is still in-flight when the loop ends. + // Without this drain, bus_free() returns false on the next + // refresh() call, causing it to be silently skipped. + if (displayio_display_bus_begin_transaction(&self->bus)) { + displayio_display_bus_end_transaction(&self->bus); + } + } + #endif + return true; } diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index b5c691ccbd6ce..96f8b682fdd6a 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -512,6 +512,75 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self, y_shift = temp_shift; } + // Fast path for the common case: single-tile, 16bpp bitmap with Palette, + // no transforms, full coverage. Inlines bitmap access and palette cache + // lookup to eliminate per-pixel function-call overhead (~4-5x speedup). + if (full_coverage && + colorspace->depth == 16 && + self->width_in_tiles == 1 && + self->height_in_tiles == 1 && + self->absolute_transform->scale == 1 && + self->absolute_transform->dx == 1 && + self->absolute_transform->dy == 1 && + !self->absolute_transform->transpose_xy && + !self->flip_x && !self->flip_y && + !self->transpose_xy && + x_stride == 1 && y_stride == displayio_area_width(area) && + x_shift == 0 && y_shift == 0 && + mp_obj_is_type(self->bitmap, &displayio_bitmap_type) && + mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) { + + displayio_bitmap_t *bmp = MP_OBJ_TO_PTR(self->bitmap); + displayio_palette_t *pal = MP_OBJ_TO_PTR(self->pixel_shader); + + if (bmp->bits_per_value == 16 && !pal->dither) { + uint16_t *out = (uint16_t *)buffer; + bool all_opaque = true; + + // Process all pixels with inlined bitmap access and palette cache. + uint32_t idx = 0; + for (int16_t y = start_y; y < end_y; ++y) { + // Direct row pointer into bitmap data (16bpp → uint16_t). + // stride is in uint32_t units, so multiply by 2 for uint16_t indexing. + const uint16_t *bmp_row = ((const uint16_t *)bmp->data) + y * (bmp->stride * 2); + + for (int16_t x = start_x; x < end_x; ++x) { + uint16_t px_index = bmp_row[x]; + + // Inline palette cache check (avoids function call overhead). + if (px_index < pal->color_count) { + _displayio_color_t *color = &pal->colors[px_index]; + if (color->transparent) { + all_opaque = false; + idx++; + continue; + } + if (color->cached_colorspace == colorspace && + color->cached_colorspace_grayscale_bit == colorspace->grayscale_bit && + color->cached_colorspace_grayscale == colorspace->grayscale) { + out[idx] = (uint16_t)color->cached_color; + } else { + // Cache miss — do full conversion, then cache. + displayio_input_pixel_t in_px = {.pixel = px_index}; + displayio_output_pixel_t out_px = {.opaque = true}; + displayio_palette_get_color(pal, colorspace, &in_px, &out_px); + out[idx] = (uint16_t)out_px.pixel; + } + } else { + // Out of palette range — transparent. + all_opaque = false; + idx++; + continue; + } + mask[idx >> 5] |= 1u << (idx & 31); + idx++; + } + } + + return all_opaque; + } + } + displayio_input_pixel_t input_pixel; displayio_output_pixel_t output_pixel; diff --git a/shared-module/displayio/bus_core.h b/shared-module/displayio/bus_core.h index 838454c92e6d8..bd7e51aeb4561 100644 --- a/shared-module/displayio/bus_core.h +++ b/shared-module/displayio/bus_core.h @@ -49,6 +49,12 @@ void displayio_display_bus_end_transaction(displayio_display_bus_t *self); void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area); +#if CIRCUITPY_QSPIBUS +// Send column/row window commands within an already-open transaction. +// Caller must have called displayio_display_bus_begin_transaction() first. +void displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area); +#endif + void release_display_bus(displayio_display_bus_t *self); void displayio_display_bus_collect_ptrs(displayio_display_bus_t *self); From ad93dd94f2d4d02047da9949ae95d5b7e2b27df4 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 25 Feb 2026 21:12:15 +0100 Subject: [PATCH 378/517] Proper VID/PID --- .../boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk index 127eec2e8b6fe..17d5300100174 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk @@ -5,9 +5,8 @@ CIRCUITPY_CREATOR_ID = 0x57415645 # 'WAVE' (Waveshare) CIRCUITPY_CREATION_ID = 0x41323431 # 'A241' (AMOLED 2.41) -# USB identifiers - from Arduino pins_arduino.h USB_VID = 0x303A -USB_PID = 0x82CE +USB_PID = 0x8278 USB_MANUFACTURER = "Waveshare" USB_PRODUCT = "ESP32-S3-Touch-AMOLED-2.41" From 1ff419ade6755a9bb12ea9e77645205c6a1763b1 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 25 Feb 2026 21:28:11 +0100 Subject: [PATCH 379/517] displayio: make region command sends reusable for QSPI transactions Refactor display bus region-update command emission into a shared helper with optional transaction management. Keep the existing public path unchanged, and add a QSPIBus-only entrypoint that can send region commands without nesting begin/end transaction calls. Also remove the TileGrid 16bpp+Palette fast path so fill_area uses the generic path, and fix indentation in QSPIBus write_data. --- ports/espressif/common-hal/qspibus/QSPIBus.c | 2 +- shared-module/displayio/TileGrid.c | 69 -------------------- shared-module/displayio/bus_core.c | 44 ++++++++++--- 3 files changed, 36 insertions(+), 79 deletions(-) diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index 0e74fcf1fdf95..91f16d233deac 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -464,7 +464,7 @@ void common_hal_qspibus_qspibus_write_data( } return; } - if (!self->has_pending_command) { + if (!self->has_pending_command) { mp_raise_ValueError(MP_ERROR_TEXT("No pending command")); } diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index 96f8b682fdd6a..b5c691ccbd6ce 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -512,75 +512,6 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self, y_shift = temp_shift; } - // Fast path for the common case: single-tile, 16bpp bitmap with Palette, - // no transforms, full coverage. Inlines bitmap access and palette cache - // lookup to eliminate per-pixel function-call overhead (~4-5x speedup). - if (full_coverage && - colorspace->depth == 16 && - self->width_in_tiles == 1 && - self->height_in_tiles == 1 && - self->absolute_transform->scale == 1 && - self->absolute_transform->dx == 1 && - self->absolute_transform->dy == 1 && - !self->absolute_transform->transpose_xy && - !self->flip_x && !self->flip_y && - !self->transpose_xy && - x_stride == 1 && y_stride == displayio_area_width(area) && - x_shift == 0 && y_shift == 0 && - mp_obj_is_type(self->bitmap, &displayio_bitmap_type) && - mp_obj_is_type(self->pixel_shader, &displayio_palette_type)) { - - displayio_bitmap_t *bmp = MP_OBJ_TO_PTR(self->bitmap); - displayio_palette_t *pal = MP_OBJ_TO_PTR(self->pixel_shader); - - if (bmp->bits_per_value == 16 && !pal->dither) { - uint16_t *out = (uint16_t *)buffer; - bool all_opaque = true; - - // Process all pixels with inlined bitmap access and palette cache. - uint32_t idx = 0; - for (int16_t y = start_y; y < end_y; ++y) { - // Direct row pointer into bitmap data (16bpp → uint16_t). - // stride is in uint32_t units, so multiply by 2 for uint16_t indexing. - const uint16_t *bmp_row = ((const uint16_t *)bmp->data) + y * (bmp->stride * 2); - - for (int16_t x = start_x; x < end_x; ++x) { - uint16_t px_index = bmp_row[x]; - - // Inline palette cache check (avoids function call overhead). - if (px_index < pal->color_count) { - _displayio_color_t *color = &pal->colors[px_index]; - if (color->transparent) { - all_opaque = false; - idx++; - continue; - } - if (color->cached_colorspace == colorspace && - color->cached_colorspace_grayscale_bit == colorspace->grayscale_bit && - color->cached_colorspace_grayscale == colorspace->grayscale) { - out[idx] = (uint16_t)color->cached_color; - } else { - // Cache miss — do full conversion, then cache. - displayio_input_pixel_t in_px = {.pixel = px_index}; - displayio_output_pixel_t out_px = {.opaque = true}; - displayio_palette_get_color(pal, colorspace, &in_px, &out_px); - out[idx] = (uint16_t)out_px.pixel; - } - } else { - // Out of palette range — transparent. - all_opaque = false; - idx++; - continue; - } - mask[idx >> 5] |= 1u << (idx & 31); - idx++; - } - } - - return all_opaque; - } - } - displayio_input_pixel_t input_pixel; displayio_output_pixel_t output_pixel; diff --git a/shared-module/displayio/bus_core.c b/shared-module/displayio/bus_core.c index 3a181181051da..5afa42f26fdf8 100644 --- a/shared-module/displayio/bus_core.c +++ b/shared-module/displayio/bus_core.c @@ -114,7 +114,7 @@ void displayio_display_bus_end_transaction(displayio_display_bus_t *self) { self->end_transaction(self->bus); } -void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { +static void _displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area, bool manage_transactions) { uint16_t x1 = area->x1 + self->colstart; uint16_t x2 = area->x2 + self->colstart; uint16_t y1 = area->y1 + self->rowstart; @@ -141,7 +141,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d } // Set column. - displayio_display_bus_begin_transaction(self); + if (manage_transactions) { + displayio_display_bus_begin_transaction(self); + } uint8_t data[5]; data[0] = self->column_command; uint8_t data_length = 1; @@ -176,20 +178,28 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d } self->send(self->bus, data_type, chip_select, data, data_length); - displayio_display_bus_end_transaction(self); + if (manage_transactions) { + displayio_display_bus_end_transaction(self); + } if (self->set_current_column_command != NO_COMMAND) { uint8_t command = self->set_current_column_command; - displayio_display_bus_begin_transaction(self); + if (manage_transactions) { + displayio_display_bus_begin_transaction(self); + } self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); // Only send the first half of data because it is the first coordinate. self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); - displayio_display_bus_end_transaction(self); + if (manage_transactions) { + displayio_display_bus_end_transaction(self); + } } // Set row. - displayio_display_bus_begin_transaction(self); + if (manage_transactions) { + displayio_display_bus_begin_transaction(self); + } data[0] = self->row_command; data_length = 1; if (!self->data_as_commands) { @@ -220,18 +230,34 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d } self->send(self->bus, data_type, chip_select, data, data_length); - displayio_display_bus_end_transaction(self); + if (manage_transactions) { + displayio_display_bus_end_transaction(self); + } if (self->set_current_row_command != NO_COMMAND) { uint8_t command = self->set_current_row_command; - displayio_display_bus_begin_transaction(self); + if (manage_transactions) { + displayio_display_bus_begin_transaction(self); + } self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); // Only send the first half of data because it is the first coordinate. self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); - displayio_display_bus_end_transaction(self); + if (manage_transactions) { + displayio_display_bus_end_transaction(self); + } } } +void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { + _displayio_display_bus_send_region_commands(self, display, area, true); +} + +#if CIRCUITPY_QSPIBUS +void displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { + _displayio_display_bus_send_region_commands(self, display, area, false); +} +#endif + void displayio_display_bus_collect_ptrs(displayio_display_bus_t *self) { self->collect_ptrs(self->bus); } From dee2d9fe87d2388acf8819873a0f64cf899ca11b Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 25 Feb 2026 21:34:31 +0100 Subject: [PATCH 380/517] Proper VID/PID --- .../boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h index d31dd2e0e84f4..5c1aec08d96f9 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h @@ -9,7 +9,7 @@ // USB identifiers #define USB_VID 0x303A -#define USB_PID 0x82CE +#define USB_PID 0x8278 #define USB_MANUFACTURER "Waveshare" #define USB_PRODUCT "ESP32-S3-Touch-AMOLED-2.41" From 3c8e7f85e87388018b30748b1d3ab6413d28f357 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 15:55:25 +0100 Subject: [PATCH 381/517] Workaround for an issue with esp-idf 5.5.3 --- .../boards/waveshare_esp32_s3_amoled_241/sdkconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig index 3ef898aed3e46..4b70697b575c8 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig @@ -29,5 +29,18 @@ CONFIG_LWIP_LOCAL_HOSTNAME="waveshare-esp32-s3-amoled" # Disable USB-Serial/JTAG console - CircuitPython uses TinyUSB (USB OTG) for REPL CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=n +# === TEMPORARY DEBUG: route console to UART0 (TX=GPIO43, RX=GPIO44) === +# CONFIG_ESP_CONSOLE_UART_DEFAULT=y +# CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y +# === END TEMPORARY DEBUG === + +# Workaround: NimBLE BLE_STATIC_TO_DYNAMIC=y (default in IDF 5.5.3) causes +# ble_gap_vars to be a dynamically allocated pointer (NULL before ble_gap_init). +# CircuitPython calls ble_gap_adv_active() before NimBLE init → NULL deref → bootloop. +# Reverting to static allocation avoids the crash. +CONFIG_BT_NIMBLE_STATIC_TO_DYNAMIC=n + # Enable .app_desc structure CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 From 347296c86608baf78402d556696b261631603d1e Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 17:44:41 +0100 Subject: [PATCH 382/517] Fixing Copilot findings --- py/circuitpy_mpconfig.h | 7 +++++++ shared-module/busdisplay/BusDisplay.c | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 1db12ac8ae9fd..c7509753ec65a 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -406,6 +406,13 @@ typedef long mp_off_t; #define CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE (512) #endif +// Port-level upper bound for the QSPI display buffer (uint32_t words). +// The _Static_assert in BusDisplay.c enforces this at compile time. +// Ports with larger stacks can raise this in mpconfigport.h. +#ifndef CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE_MAX +#define CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE_MAX (2048) +#endif + #else #define CIRCUITPY_DISPLAY_LIMIT (0) #define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (0) diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index 1994a25214c34..f22cd78a16e2d 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -261,10 +261,10 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are // QSPI panels benefit from larger sub-rectangle buffers because each chunk // has non-trivial command/window overhead. Keep this path qspibus-specific // to avoid increasing stack usage on other display buses. - // Guard: buffer is a VLA on stack; 2048 uint32_t words = 8KB is the safe - // upper bound for ESP32-S3's 24KB main task stack. - _Static_assert(CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE <= 2048, - "CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE exceeds safe stack limit (8KB)"); + // Guard: buffer is a VLA on stack. The port-specific max keeps shared + // code free of single-port assumptions (default 2048 words = 8KB). + _Static_assert(CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE <= CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE_MAX, + "CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE exceeds CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE_MAX"); bool is_qspi_bus = mp_obj_is_type(self->bus.bus, &qspibus_qspibus_type); if (is_qspi_bus && self->core.colorspace.depth == 16 && From b2149f2c1fd34b59c9b9af0eb5d9281c9ef24bb0 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 18:01:59 +0100 Subject: [PATCH 383/517] Fixing Copilot findings (2) --- ports/espressif/common-hal/qspibus/QSPIBus.c | 3 +++ shared-module/busdisplay/BusDisplay.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index 91f16d233deac..2a262f520d859 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -352,6 +352,9 @@ void common_hal_qspibus_qspibus_construct( #ifdef CIRCUITPY_QSPIBUS_PANEL_POWER_PIN const mcu_pin_obj_t *power = CIRCUITPY_QSPIBUS_PANEL_POWER_PIN; if (power != NULL) { + if (!common_hal_mcu_pin_is_free(power)) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_power); + } self->power_pin = power->number; claim_pin(power); gpio_set_direction((gpio_num_t)self->power_pin, GPIO_MODE_OUTPUT); diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index f22cd78a16e2d..4610721a061d7 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -329,10 +329,12 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are remaining_rows -= rows_per_buffer; #if CIRCUITPY_QSPIBUS - if (is_qspi_bus) { + if (is_qspi_bus && + self->core.colorspace.depth >= 8 && + !self->bus.data_as_commands && + !self->bus.SH1107_addressing) { // QSPI path: fill_area first (overlaps with previous DMA), // then single-transaction set_region + RAMWR + pixels. - // depth is always 16 here (guarded by is_qspi_bus check above). uint32_t subrectangle_size_bytes = (uint32_t)displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); memset(mask, 0, mask_length * sizeof(mask[0])); From 170451819696319cb01363b8a1992b4d461af3ab Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 18:40:08 +0100 Subject: [PATCH 384/517] Fixing Copilot findings (3) --- ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig | 2 +- ports/espressif/common-hal/qspibus/QSPIBus.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig index 4b70697b575c8..01a370d1dbdf5 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig @@ -10,7 +10,7 @@ CONFIG_SPIRAM_MODE_OCT=y CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y CONFIG_SPIRAM_RODATA=y CONFIG_SPIRAM_SPEED_80M=y -CONFIG_SPIRAM_MEMTEST=y +CONFIG_SPIRAM_MEMTEST=n CONFIG_SPIRAM_CLK_IO=39 CONFIG_SPIRAM_CS_IO=38 diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index 2a262f520d859..46b6f3399626d 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -353,7 +353,7 @@ void common_hal_qspibus_qspibus_construct( const mcu_pin_obj_t *power = CIRCUITPY_QSPIBUS_PANEL_POWER_PIN; if (power != NULL) { if (!common_hal_mcu_pin_is_free(power)) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_power); + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_LCD_POWER); } self->power_pin = power->number; claim_pin(power); From 2bd02fada26a32f5c888c6168883b21c7f059911 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 19:22:13 +0100 Subject: [PATCH 385/517] Made board.DISPLAY available --- .../waveshare_esp32_s3_amoled_241/board.c | 87 +++++++++++++++++++ .../mpconfigboard.h | 4 +- .../waveshare_esp32_s3_amoled_241/pins.c | 4 + 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c index 3941e89a5e4d7..36d25e6fa33d5 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c @@ -3,6 +3,93 @@ // SPDX-License-Identifier: MIT #include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "shared-bindings/qspibus/QSPIBus.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" + +// RM690B0 AMOLED initialization sequence. +// Format: command byte, length | 0x80 (if delay), data bytes..., [delay ms] +// Based on vendor recommendations, tested with Waveshare 2.41" AMOLED panel. +static const uint8_t display_init_sequence[] = { + // Page select and configuration + 0xFE, 0x01, 0x20, // Enter user command mode + 0x26, 0x01, 0x0A, // Bias setting + 0x24, 0x01, 0x80, // Source output control + 0xFE, 0x01, 0x13, // Page 13 + 0xEB, 0x01, 0x0E, // Vendor command + 0xFE, 0x01, 0x00, // Return to page 0 + // Display configuration + 0x3A, 0x01, 0x55, // COLMOD: 16-bit RGB565 + 0xC2, 0x81, 0x00, 0x0A, // Vendor command + 10ms delay + 0x35, 0x00, // Tearing effect line on (no data) + 0x51, 0x81, 0x00, 0x0A, // Brightness control + 10ms delay + // Power on + 0x11, 0x80, 0x50, // Sleep out + 80ms delay + // Display window (MV=1: CASET→rows, RASET→cols) + 0x2A, 0x04, 0x00, 0x10, 0x01, 0xD1, // CASET: 16..465 (450px + 16 offset) + 0x2B, 0x04, 0x00, 0x00, 0x02, 0x57, // RASET: 0..599 (600px) + // Enable display + 0x29, 0x80, 0x0A, // Display on + 10ms delay + // Memory access: MV=1, ML=1 for landscape + 0x36, 0x81, 0x30, 0x0A, // MADCTL + 10ms delay + // Brightness + 0x51, 0x01, 0xFF, // Set brightness to maximum +}; void board_init(void) { + // 1. Allocate and construct QSPI bus + qspibus_qspibus_obj_t *bus = &allocate_display_bus_or_raise()->qspi_bus; + bus->base.type = &qspibus_qspibus_type; + + common_hal_qspibus_qspibus_construct(bus, + CIRCUITPY_LCD_CLK, // clock + CIRCUITPY_LCD_D0, // data0 + CIRCUITPY_LCD_D1, // data1 + CIRCUITPY_LCD_D2, // data2 + CIRCUITPY_LCD_D3, // data3 + CIRCUITPY_LCD_CS, // cs + NULL, // dcx (not used, QSPI uses encoded commands) + CIRCUITPY_LCD_RESET, // reset + 40000000); // 40 MHz + + // 2. Allocate and construct BusDisplay with RM690B0 init sequence. + // Physical panel: 450 cols × 600 rows. + // MADCTL MV=1 swaps row/col → logical 600×450 landscape. + busdisplay_busdisplay_obj_t *display = &allocate_display_or_raise()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct(display, + bus, + 600, // width (logical, after MV=1 swap) + 450, // height (logical, after MV=1 swap) + 0, // colstart + 16, // rowstart (physical row offset) + 0, // rotation + 16, // color_depth (RGB565) + false, // grayscale + false, // pixels_in_byte_share_row + 1, // bytes_per_cell + false, // reverse_pixels_in_byte + false, // reverse_bytes_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set_column_command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set_row_command + MIPI_COMMAND_WRITE_MEMORY_START, // write_ram_command + (uint8_t *)display_init_sequence, + sizeof(display_init_sequence), + NULL, // backlight_pin (AMOLED — no backlight GPIO) + 0x51, // brightness_command + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000); // backlight_pwm_frequency } + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h index 5c1aec08d96f9..fcd3c0e5b0398 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h @@ -21,8 +21,8 @@ // ESP32-S3 main task stack is 24KB; verified safe with this board. #define CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE (2048) -// AMOLED Display (displayio + qspibus path) -#define CIRCUITPY_BOARD_DISPLAY (0) +// AMOLED Display (displayio + qspibus path) - initialized in board_init() +#define CIRCUITPY_BOARD_DISPLAY (1) #define CIRCUITPY_LCD_CS (&pin_GPIO9) #define CIRCUITPY_LCD_CLK (&pin_GPIO10) #define CIRCUITPY_LCD_D0 (&pin_GPIO11) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c index ed03341923326..937847b6838f1 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c @@ -6,6 +6,7 @@ #include "py/mphal.h" #include "shared-bindings/board/__init__.h" #include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS @@ -96,5 +97,8 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, // Available { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, // Available { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, // Available + + // Board display (initialized in board_init) + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From d20581be6d9fa04d1e7b472172e186a321818a49 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 19:41:40 +0100 Subject: [PATCH 386/517] Removed temp debug settings --- .../boards/waveshare_esp32_s3_amoled_241/sdkconfig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig index 01a370d1dbdf5..b6c009f31162d 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig @@ -29,13 +29,6 @@ CONFIG_LWIP_LOCAL_HOSTNAME="waveshare-esp32-s3-amoled" # Disable USB-Serial/JTAG console - CircuitPython uses TinyUSB (USB OTG) for REPL CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=n -# === TEMPORARY DEBUG: route console to UART0 (TX=GPIO43, RX=GPIO44) === -# CONFIG_ESP_CONSOLE_UART_DEFAULT=y -# CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y -# CONFIG_LOG_DEFAULT_LEVEL_INFO=y -# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y -# === END TEMPORARY DEBUG === - # Workaround: NimBLE BLE_STATIC_TO_DYNAMIC=y (default in IDF 5.5.3) causes # ble_gap_vars to be a dynamically allocated pointer (NULL before ble_gap_init). # CircuitPython calls ble_gap_adv_active() before NimBLE init → NULL deref → bootloop. From 4e4461e46a2a5f0f46d262fea1617a73a43af766 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 20:00:41 +0100 Subject: [PATCH 387/517] Fixing Copilot findings (4) --- ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c index 36d25e6fa33d5..29dbb64050a7e 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c @@ -14,7 +14,8 @@ // RM690B0 AMOLED initialization sequence. // Format: command byte, length | 0x80 (if delay), data bytes..., [delay ms] // Based on vendor recommendations, tested with Waveshare 2.41" AMOLED panel. -static const uint8_t display_init_sequence[] = { +// Non-const to match upstream common_hal_busdisplay_busdisplay_construct signature. +static uint8_t display_init_sequence[] = { // Page select and configuration 0xFE, 0x01, 0x20, // Enter user command mode 0x26, 0x01, 0x0A, // Bias setting @@ -78,7 +79,7 @@ void board_init(void) { MIPI_COMMAND_SET_COLUMN_ADDRESS, // set_column_command MIPI_COMMAND_SET_PAGE_ADDRESS, // set_row_command MIPI_COMMAND_WRITE_MEMORY_START, // write_ram_command - (uint8_t *)display_init_sequence, + display_init_sequence, sizeof(display_init_sequence), NULL, // backlight_pin (AMOLED — no backlight GPIO) 0x51, // brightness_command From bdea5218d2731c1812091324d2adc5e3fd4c3b6c Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 20:15:41 +0100 Subject: [PATCH 388/517] Fixing Copilot findings (5) --- ports/espressif/common-hal/qspibus/QSPIBus.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index 46b6f3399626d..33c5f1282062e 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -156,6 +156,15 @@ static void qspibus_send_color_bytes( const uint8_t *cursor = data; size_t remaining = len; + // Drain stale semaphore tokens that late ISR completions may have + // posted after a previous qspibus_reset_transfer_state(). Without + // this, a stale token could satisfy a future wait, causing the next + // transfer to skip its real DMA-done wait. + if (self->inflight_transfers == 0 && self->transfer_done_sem != NULL) { + while (xSemaphoreTake(self->transfer_done_sem, 0) == pdTRUE) { + } + } + while (remaining > 0) { // inflight_transfers is only modified in task context (never from ISR), // so no atomic/critical section is needed. The ISR only signals the From 79eb389f0777da893bd4b5e4a1045497dcec8995 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 20:33:04 +0100 Subject: [PATCH 389/517] Fixing Copilot findings (6) --- shared-bindings/qspibus/QSPIBus.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/shared-bindings/qspibus/QSPIBus.c b/shared-bindings/qspibus/QSPIBus.c index cc4fdc91c0669..1452d2ddd86a6 100644 --- a/shared-bindings/qspibus/QSPIBus.c +++ b/shared-bindings/qspibus/QSPIBus.c @@ -139,8 +139,14 @@ static mp_obj_t qspibus_qspibus_send(size_t n_args, const mp_obj_t *pos_args, mp len = data_bufinfo.len; } + // Flush any pending command from a prior write_command() call. + // begin_transaction() returns false while has_pending_command is set, + // so entering the wait loop without flushing would spin forever. + if (self->has_pending_command) { + common_hal_qspibus_qspibus_write_data(self, NULL, 0); + } + // Wait for display bus to be available, then acquire transaction. - // Mirrors FourWire.send() pattern: begin_transaction → send → end_transaction. while (!common_hal_qspibus_qspibus_begin_transaction(MP_OBJ_FROM_PTR(self))) { RUN_BACKGROUND_TASKS; } From 11299b3afe2b06fb7dd4e4d77a1acadeffdc71da Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Thu, 26 Feb 2026 21:04:56 +0100 Subject: [PATCH 390/517] Fixing Copilot findings (7) --- shared-bindings/qspibus/QSPIBus.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/shared-bindings/qspibus/QSPIBus.c b/shared-bindings/qspibus/QSPIBus.c index 1452d2ddd86a6..24c3089213fec 100644 --- a/shared-bindings/qspibus/QSPIBus.c +++ b/shared-bindings/qspibus/QSPIBus.c @@ -106,20 +106,18 @@ static mp_obj_t qspibus_qspibus_obj_reset(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(qspibus_qspibus_reset_obj, qspibus_qspibus_obj_reset); -//| def send(self, command: int, data: Optional[ReadableBuffer] = None) -> None: -//| """Send command with optional payload bytes. -//| -//| This mirrors FourWire-style convenience API: -//| - command byte is sent first -//| - optional payload bytes follow -//| """ +//| def send( +//| self, command: int, data: ReadableBuffer, *, toggle_every_byte: bool = False +//| ) -> None: +//| """Sends the given command value followed by the full set of data. Display state, such as +//| vertical scroll, set via ``send`` may or may not be reset once the code is done.""" //| ... //| static mp_obj_t qspibus_qspibus_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_command, ARG_data }; static const mp_arg_t allowed_args[] = { { MP_QSTR_command, MP_ARG_INT | MP_ARG_REQUIRED }, - { MP_QSTR_data, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -130,14 +128,8 @@ static mp_obj_t qspibus_qspibus_send(size_t n_args, const mp_obj_t *pos_args, mp uint8_t command = (uint8_t)mp_arg_validate_int_range(args[ARG_command].u_int, 0, 255, MP_QSTR_command); - const uint8_t *data = NULL; - size_t len = 0; - mp_buffer_info_t data_bufinfo; - if (args[ARG_data].u_obj != mp_const_none) { - mp_get_buffer_raise(args[ARG_data].u_obj, &data_bufinfo, MP_BUFFER_READ); - data = (const uint8_t *)data_bufinfo.buf; - len = data_bufinfo.len; - } + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); // Flush any pending command from a prior write_command() call. // begin_transaction() returns false while has_pending_command is set, @@ -151,7 +143,7 @@ static mp_obj_t qspibus_qspibus_send(size_t n_args, const mp_obj_t *pos_args, mp RUN_BACKGROUND_TASKS; } common_hal_qspibus_qspibus_send(MP_OBJ_FROM_PTR(self), DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, &command, 1); - common_hal_qspibus_qspibus_send(MP_OBJ_FROM_PTR(self), DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data, len); + common_hal_qspibus_qspibus_send(MP_OBJ_FROM_PTR(self), DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, ((uint8_t *)bufinfo.buf), bufinfo.len); common_hal_qspibus_qspibus_end_transaction(MP_OBJ_FROM_PTR(self)); return mp_const_none; } From 551bc83e642e24ce6280298b51b26d8bd762fe0d Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Fri, 27 Feb 2026 12:16:21 +0100 Subject: [PATCH 391/517] Added common touch pins definitions --- .../boards/waveshare_esp32_s3_amoled_241/pins.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c index 937847b6838f1..8804cace06dcf 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c @@ -32,9 +32,14 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO48) }, // Touch Panel (FT6336U on I2C) - { MP_ROM_QSTR(MP_QSTR_TP_SDA), MP_ROM_PTR(&pin_GPIO47) }, - { MP_ROM_QSTR(MP_QSTR_TP_SCL), MP_ROM_PTR(&pin_GPIO48) }, - { MP_ROM_QSTR(MP_QSTR_TP_RESET), MP_ROM_PTR(&pin_GPIO3) }, + // NOTE: TP_INT is routed through the TCA9554 IO expander (EXIO2), + // not a direct ESP32-S3 GPIO — no TOUCH_INT alias is possible here. + { MP_ROM_QSTR(MP_QSTR_TP_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_TP_SCL), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_TP_RESET), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_SCL), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_RST), MP_ROM_PTR(&pin_GPIO3) }, // RTC (PCF85063 on I2C) { MP_ROM_QSTR(MP_QSTR_RTC_SDA), MP_ROM_PTR(&pin_GPIO47) }, From a01d36036fe242e33c8aeaacfad7873e4f9759a7 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 1 Mar 2026 14:05:48 -0500 Subject: [PATCH 392/517] Espressif SPI: remove duplicate mutex create --- ports/espressif/common-hal/busio/SPI.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ports/espressif/common-hal/busio/SPI.c b/ports/espressif/common-hal/busio/SPI.c index 4c07917b20b86..fc12d40fccb59 100644 --- a/ports/espressif/common-hal/busio/SPI.c +++ b/ports/espressif/common-hal/busio/SPI.c @@ -72,11 +72,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_ValueError(MP_ERROR_TEXT("All SPI peripherals are in use")); } - self->mutex = xSemaphoreCreateMutex(); - if (self->mutex == NULL) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Unable to create lock")); - } - esp_err_t result = spi_bus_initialize(self->host_id, &bus_config, SPI_DMA_CH_AUTO); if (result == ESP_ERR_NO_MEM) { mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("ESP-IDF memory allocation failed")); @@ -166,7 +161,7 @@ bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return false; } - return xSemaphoreTake(self->mutex, 1) == pdTRUE; + return xSemaphoreTake(self->mutex, 0) == pdTRUE; } bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { From 77fcc6fa96aae33d3da2c5639054514bd83b6ea4 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 1 Mar 2026 14:06:50 -0500 Subject: [PATCH 393/517] use CONFIG_ESP_CONSOLE_UART_{TX,RX}_GPIO instead of deprecated CONFIG_CONSOLE_UART_*_GPIO --- ports/espressif/supervisor/port.c | 8 ++++---- py/mpconfig.h | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 192a91717169a..4691433079876 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -251,12 +251,12 @@ safe_mode_t port_init(void) { #define pin_GPIOn(n) pin_GPIO##n #define pin_GPIOn_EXPAND(x) pin_GPIOn(x) - #ifdef CONFIG_CONSOLE_UART_TX_GPIO - common_hal_never_reset_pin(&pin_GPIOn_EXPAND(CONFIG_CONSOLE_UART_TX_GPIO)); + #ifdef CONFIG_ESP_CONSOLE_UART_TX_GPIO + common_hal_never_reset_pin(&pin_GPIOn_EXPAND(CONFIG_ESP_CONSOLE_UART_TX_GPIO)); #endif - #ifdef CONFIG_CONSOLE_UART_RX_GPIO - common_hal_never_reset_pin(&pin_GPIOn_EXPAND(CONFIG_CONSOLE_UART_RX_GPIO)); + #ifdef CONFIG_ESP_CONSOLE_UART_RX_GPIO + common_hal_never_reset_pin(&pin_GPIOn_EXPAND(CONFIG_ESP_CONSOLE_UART_RX_GPIO)); #endif #ifndef ENABLE_JTAG diff --git a/py/mpconfig.h b/py/mpconfig.h index a48958200616b..5ff568e64b00a 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2205,6 +2205,12 @@ typedef double mp_float_t; #define MP_INLINE inline MP_NO_INSTRUMENT #endif +// CIRCUITPY-CHANGE +// Modifier for functions whose return value should not be ignored +#ifndef MP_WARN_UNUSED_RESULT +#define MP_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#endif + // Modifier for functions which should be never inlined #ifndef MP_NOINLINE #define MP_NOINLINE __attribute__((noinline)) From bcc33faf67737e461720b2eb6125a7d702820397 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 1 Mar 2026 14:11:44 -0500 Subject: [PATCH 394/517] displayio: check whether we locked the bus or not --- shared-module/busdisplay/BusDisplay.c | 9 +++++--- shared-module/displayio/bus_core.c | 23 ++++++++++++++++----- shared-module/epaperdisplay/EPaperDisplay.c | 14 ++++++++++--- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index 6ef1c4c7f66ce..d320d4f13eaa7 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -6,6 +6,7 @@ #include "shared-bindings/busdisplay/BusDisplay.h" +#include "py/mphal.h" #include "py/runtime.h" #if CIRCUITPY_FOURWIRE #include "shared-bindings/fourwire/FourWire.h" @@ -16,6 +17,7 @@ #if CIRCUITPY_PARALLELDISPLAYBUS #include "shared-bindings/paralleldisplaybus/ParallelBus.h" #endif +#include "shared/runtime/interrupt_char.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/__init__.h" @@ -73,6 +75,9 @@ void common_hal_busdisplay_busdisplay_construct(busdisplay_busdisplay_obj_t *sel uint8_t *data = cmd + 2; while (!displayio_display_bus_begin_transaction(&self->bus)) { RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_display); + } } if (self->bus.data_as_commands) { uint8_t full_command[data_size + 1]; @@ -288,11 +293,9 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); // Can't acquire display bus; skip the rest of the data. - if (!displayio_display_bus_is_free(&self->bus)) { + if (!displayio_display_bus_begin_transaction(&self->bus)) { return false; } - - displayio_display_bus_begin_transaction(&self->bus); _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); displayio_display_bus_end_transaction(&self->bus); diff --git a/shared-module/displayio/bus_core.c b/shared-module/displayio/bus_core.c index e01b9d9eef685..4f0d7c4d9d2f3 100644 --- a/shared-module/displayio/bus_core.c +++ b/shared-module/displayio/bus_core.c @@ -85,11 +85,16 @@ void displayio_display_bus_construct(displayio_display_bus_t *self, self->bus = bus; } +// This is just a hint, and is not a reliable result, since the bus could be grabbed in between this called +// and the attempt to use the bus. Use displayio_display_bus_begin_transaction(), which is atomic. bool displayio_display_bus_is_free(displayio_display_bus_t *self) { return !self->bus || self->bus_free(self->bus); } -bool displayio_display_bus_begin_transaction(displayio_display_bus_t *self) { +MP_WARN_UNUSED_RESULT bool displayio_display_bus_begin_transaction(displayio_display_bus_t *self) { + if (!self->bus) { + return false; + } mp_obj_base_t *bus_base = MP_OBJ_TO_PTR(self->bus); if (bus_base->type == &mp_type_NoneType) { return false; @@ -128,7 +133,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d } // Set column. - displayio_display_bus_begin_transaction(self); + if (!displayio_display_bus_begin_transaction(self)) { + return; + } uint8_t data[5]; data[0] = self->column_command; uint8_t data_length = 1; @@ -167,7 +174,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d if (self->set_current_column_command != NO_COMMAND) { uint8_t command = self->set_current_column_command; - displayio_display_bus_begin_transaction(self); + if (!displayio_display_bus_begin_transaction(self)) { + return; + } self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); // Only send the first half of data because it is the first coordinate. self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); @@ -176,7 +185,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d // Set row. - displayio_display_bus_begin_transaction(self); + if (!displayio_display_bus_begin_transaction(self)) { + return; + } data[0] = self->row_command; data_length = 1; if (!self->data_as_commands) { @@ -211,7 +222,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d if (self->set_current_row_command != NO_COMMAND) { uint8_t command = self->set_current_row_command; - displayio_display_bus_begin_transaction(self); + if (!displayio_display_bus_begin_transaction(self)) { + return; + } self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); // Only send the first half of data because it is the first coordinate. self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index 655ae10307155..d34be9d5c7c0a 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -153,7 +153,10 @@ static void send_command_sequence(epaperdisplay_epaperdisplay_obj_t *self, data_size = ((data_size & ~DELAY) << 8) + *(cmd + 2); data = cmd + 3; } - displayio_display_bus_begin_transaction(&self->bus); + while (!displayio_display_bus_begin_transaction(&self->bus) && + !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } self->bus.send(self->bus.bus, DISPLAY_COMMAND, self->chip_select, cmd, 1); self->bus.send(self->bus.bus, DISPLAY_DATA, self->chip_select, data, data_size); displayio_display_bus_end_transaction(&self->bus); @@ -311,7 +314,10 @@ static bool epaperdisplay_epaperdisplay_refresh_area(epaperdisplay_epaperdisplay if (pass == 1) { write_command = self->write_color_ram_command; } - displayio_display_bus_begin_transaction(&self->bus); + if (!displayio_display_bus_begin_transaction(&self->bus)) { + // Display bus not available now. + return false; + } self->bus.send(self->bus.bus, DISPLAY_COMMAND, self->chip_select, &write_command, 1); displayio_display_bus_end_transaction(&self->bus); @@ -388,7 +394,9 @@ static bool _clean_area(epaperdisplay_epaperdisplay_obj_t *self) { memset(buffer, 0x77, width / 2); uint8_t write_command = self->write_black_ram_command; - displayio_display_bus_begin_transaction(&self->bus); + if (displayio_display_bus_begin_transaction(&self->bus)) { + return false; + } self->bus.send(self->bus.bus, DISPLAY_COMMAND, self->chip_select, &write_command, 1); displayio_display_bus_end_transaction(&self->bus); From 882701bca07c97735df6ccc00f2df20da21180c5 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Mar 2026 12:07:04 -0800 Subject: [PATCH 395/517] Fix build --- .../boards/adafruit_feather_nrf52840_uf2.conf | 1 - ports/zephyr-cp/boards/frdm_rw612.conf | 5 +-- ports/zephyr-cp/boards/frdm_rw612.overlay | 2 +- .../mimxrt1170_evk_mimxrt1176_cm7.overlay | 12 +++--- .../zephyr-cp/boards/stm32wba65i_dk1.overlay | 6 +-- ports/zephyr-cp/cptools/zephyr2cp.py | 43 ++++++++++++++----- ports/zephyr-cp/socs/stm32h7b3xx.conf | 3 -- ports/zephyr-cp/socs/stm32u575xx.conf | 2 - 8 files changed, 44 insertions(+), 30 deletions(-) diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf index c30c8c19fcddb..4849c8ce2b4ad 100644 --- a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf @@ -6,4 +6,3 @@ CONFIG_BT_OBSERVER=y CONFIG_BT_EXT_ADV=y CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n -CONFIG_BOARD_REQUIRES_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/frdm_rw612.conf b/ports/zephyr-cp/boards/frdm_rw612.conf index 7d56dfed29cc9..4fc5c8c5bf0fe 100644 --- a/ports/zephyr-cp/boards/frdm_rw612.conf +++ b/ports/zephyr-cp/boards/frdm_rw612.conf @@ -2,7 +2,6 @@ CONFIG_NETWORKING=y CONFIG_NET_IPV4=y CONFIG_NET_DHCPV4=y CONFIG_NET_SOCKETS=y -CONFIG_NET_SOCKETS_POSIX_NAMES=y CONFIG_WIFI=y CONFIG_NET_L2_WIFI_MGMT=y @@ -17,9 +16,9 @@ CONFIG_MBEDTLS=y CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y CONFIG_MBEDTLS_RSA_C=y CONFIG_MBEDTLS_PKCS1_V15=y -CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_ENABLED=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=y CONFIG_MBEDTLS_ENTROPY_C=y -CONFIG_MBEDTLS_CTR_DRBG_ENABLED=y +CONFIG_MBEDTLS_CTR_DRBG_C=y CONFIG_MBEDTLS_SHA1=y CONFIG_MBEDTLS_USE_PSA_CRYPTO=n diff --git a/ports/zephyr-cp/boards/frdm_rw612.overlay b/ports/zephyr-cp/boards/frdm_rw612.overlay index 1c38f42c4abd2..c6a021d999956 100644 --- a/ports/zephyr-cp/boards/frdm_rw612.overlay +++ b/ports/zephyr-cp/boards/frdm_rw612.overlay @@ -1,6 +1,6 @@ &w25q512jvfiq { partitions { - /delete-node/ storage_partition; + /delete-node/ partition@620000; circuitpy_partition: partition@620000 { label = "circuitpy"; reg = <0x00620000 (DT_SIZE_M(58) - DT_SIZE_K(128))>; diff --git a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay index d6f6d4f0c15e7..89a78998cea74 100644 --- a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay +++ b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay @@ -1,10 +1,10 @@ &is25wp128 { - partitions{ - /delete-node/ storage_partition; - circuitpy_partition: partition@E20000 { - label = "circuitpy"; - reg = <0x00E20000 (DT_SIZE_M(2) - DT_SIZE_K(128))>; - }; + partitions { + /delete-node/ partition@e20000; + circuitpy_partition: partition@e20000 { + label = "circuitpy"; + reg = <0x00e20000 (DT_SIZE_M(2) - DT_SIZE_K(128))>; + }; }; }; diff --git a/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay b/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay index 1b6e55ba462b3..3b72ffc99845c 100644 --- a/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay +++ b/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay @@ -47,15 +47,13 @@ }; zephyr_udc0: &usbotg_hs { - clocks = <&rcc STM32_CLOCK(AHB2, 14)>, - <&rcc STM32_SRC_HSE OTGHS_SEL(0)>; - pinctrl-0 = <&usb_otg_hs_dm_pd7 &usb_otg_hs_dp_pd6>; - pinctrl-names = "default"; status = "okay"; }; &otghs_phy { /* OTG HS clock source is 32 MHz HSE */ + clocks = <&rcc STM32_CLOCK(AHB2, 15)>, + <&rcc STM32_SRC_HSE OTGHS_SEL(0)>; clock-reference = "SYSCFG_OTG_HS_PHY_CLK_32MHz"; status = "okay"; }; diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 7c78113b95b87..f7d79517195e0 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -96,6 +96,19 @@ "D12", "D13", ], + "nordic,expansion-board-header": [ + "P1_04", + "P1_05", + "P1_06", + "P1_07", + "P1_08", + "P1_09", + "P1_10", + "P1_11", + "P1_12", + "P1_13", + "P1_14", + ], "arducam,dvp-20pin-connector": [ "SCL", "SDA", @@ -544,16 +557,26 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa all_ioports.append(node.labels[0]) if status == "okay": ioports[node.labels[0]] = set(range(0, ngpios)) - if gpio_map and compatible[0] != "gpio-nexus": - i = 0 - for offset, t, label in gpio_map._markers: - if not label: - continue - num = int.from_bytes(gpio_map.value[offset + 4 : offset + 8], "big") - if (label, num) not in board_names: - board_names[(label, num)] = [] - board_names[(label, num)].append(CONNECTORS[compatible[0]][i]) - i += 1 + if gpio_map and compatible and compatible[0] != "gpio-nexus": + connector_pins = CONNECTORS.get(compatible[0], None) + if connector_pins is None: + logger.warning(f"Unsupported connector mapping compatible: {compatible[0]}") + else: + i = 0 + for offset, t, label in gpio_map._markers: + if not label: + continue + if i >= len(connector_pins): + logger.warning( + f"Connector mapping for {compatible[0]} has more pins than names; " + f"stopping at {len(connector_pins)}" + ) + break + num = int.from_bytes(gpio_map.value[offset + 4 : offset + 8], "big") + if (label, num) not in board_names: + board_names[(label, num)] = [] + board_names[(label, num)].append(connector_pins[i]) + i += 1 if "gpio-leds" in compatible: for led in node.nodes: led = node.nodes[led] diff --git a/ports/zephyr-cp/socs/stm32h7b3xx.conf b/ports/zephyr-cp/socs/stm32h7b3xx.conf index 3c7daeb753de8..14a93b52ce802 100644 --- a/ports/zephyr-cp/socs/stm32h7b3xx.conf +++ b/ports/zephyr-cp/socs/stm32h7b3xx.conf @@ -1,4 +1 @@ -CONFIG_USE_STM32_LL_USB=y -CONFIG_USE_STM32_HAL_PCD=y - CONFIG_MEMC=y diff --git a/ports/zephyr-cp/socs/stm32u575xx.conf b/ports/zephyr-cp/socs/stm32u575xx.conf index 78cefbfef402f..e69de29bb2d1d 100644 --- a/ports/zephyr-cp/socs/stm32u575xx.conf +++ b/ports/zephyr-cp/socs/stm32u575xx.conf @@ -1,2 +0,0 @@ -CONFIG_USE_STM32_LL_USB=y -CONFIG_USE_STM32_HAL_PCD=y From 00ce600ebe54e455548ad4413de70f6dfb3c4b92 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 2 Mar 2026 13:17:19 -0800 Subject: [PATCH 396/517] Update autogen. Board names changed --- ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml | 2 +- .../zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index c24d0a8527366..91a2776ae74f9 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -1,5 +1,5 @@ # This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. -name = "NXP Semiconductors FRDM_RW612" +name = "NXP Semiconductors FRDM-RW612" [modules] __future__ = true diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index b2f384e21c1b6..6f648df82fba7 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -1,5 +1,5 @@ # This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. -name = "NXP Semiconductors MIMXRT1170-EVK/EVKB" +name = "NXP Semiconductors MIMXRT1170-EVK" [modules] __future__ = true From ac11cb27c47d819c1a0d9738111ded0bb88d4aef Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 24 Feb 2026 11:49:27 -0800 Subject: [PATCH 397/517] Implement Zephyr bleio connect and disconnect Rework the conn_handle related functions because they are port-internal API. Signed-off-by: Scott Shawcroft --- .../ble_hci/common-hal/_bleio/Connection.h | 1 + locale/circuitpython.pot | 27 +- .../espressif/common-hal/_bleio/Connection.h | 1 + ports/nordic/common-hal/_bleio/Attribute.h | 9 + ports/nordic/common-hal/_bleio/Connection.h | 1 + ports/silabs/common-hal/_bleio/Connection.h | 1 + .../boards/nrf5340bsim_nrf5340_cpuapp.conf | 11 + ports/zephyr-cp/common-hal/_bleio/Adapter.c | 252 ++++++++++++++++-- ports/zephyr-cp/common-hal/_bleio/Adapter.h | 3 +- .../zephyr-cp/common-hal/_bleio/Connection.c | 68 ++++- .../zephyr-cp/common-hal/_bleio/Connection.h | 26 +- ports/zephyr-cp/common-hal/_bleio/__init__.c | 56 ++-- .../zephyr-cp/cptools/build_circuitpython.py | 3 +- ports/zephyr-cp/supervisor/port.c | 8 - .../tests/bsim/test_bsim_ble_connect.py | 119 +++++++++ shared-bindings/_bleio/Adapter.c | 17 +- shared-bindings/_bleio/Address.c | 13 +- shared-bindings/_bleio/Characteristic.c | 6 +- shared-bindings/_bleio/Descriptor.c | 6 +- shared-bindings/_bleio/PacketBuffer.c | 5 +- shared-bindings/_bleio/UUID.c | 20 +- shared-bindings/_bleio/__init__.h | 8 - 22 files changed, 496 insertions(+), 165 deletions(-) create mode 100644 ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py diff --git a/devices/ble_hci/common-hal/_bleio/Connection.h b/devices/ble_hci/common-hal/_bleio/Connection.h index 04edb104ddcb2..fa9f4c7103e7d 100644 --- a/devices/ble_hci/common-hal/_bleio/Connection.h +++ b/devices/ble_hci/common-hal/_bleio/Connection.h @@ -61,6 +61,7 @@ typedef struct { uint8_t disconnect_reason; } bleio_connection_obj_t; +void common_hal_bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index e2eebdea0fe04..2b67216cf506a 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -476,7 +476,7 @@ msgstr "" msgid "AP could not be started" msgstr "" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "" @@ -673,11 +673,6 @@ msgstr "" msgid "Brightness not adjustable" msgstr "" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "" @@ -720,10 +715,6 @@ msgstr "" msgid "Bus pin %d is already in use" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "" - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "" @@ -1029,12 +1020,18 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect" +msgstr "" + #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "" @@ -2247,10 +2244,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "" @@ -3393,10 +3386,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3808,7 +3797,6 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4292,7 +4280,6 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" diff --git a/ports/espressif/common-hal/_bleio/Connection.h b/ports/espressif/common-hal/_bleio/Connection.h index 326eca02ecd8a..bb48782e84f74 100644 --- a/ports/espressif/common-hal/_bleio/Connection.h +++ b/ports/espressif/common-hal/_bleio/Connection.h @@ -64,6 +64,7 @@ void bleio_connection_clear(bleio_connection_internal_t *self); int bleio_connection_event_cb(struct ble_gap_event *event, void *connection_in); +void common_hal_bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/ports/nordic/common-hal/_bleio/Attribute.h b/ports/nordic/common-hal/_bleio/Attribute.h index 5fa6b4d637767..c9438571a86d0 100644 --- a/ports/nordic/common-hal/_bleio/Attribute.h +++ b/ports/nordic/common-hal/_bleio/Attribute.h @@ -6,6 +6,15 @@ #pragma once +#include + +#include "py/obj.h" + #include "shared-module/_bleio/Attribute.h" extern void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode); + +size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); +size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); diff --git a/ports/nordic/common-hal/_bleio/Connection.h b/ports/nordic/common-hal/_bleio/Connection.h index 110677dc78b41..87e0e9856a4d4 100644 --- a/ports/nordic/common-hal/_bleio/Connection.h +++ b/ports/nordic/common-hal/_bleio/Connection.h @@ -65,6 +65,7 @@ typedef struct { void bleio_connection_clear(bleio_connection_internal_t *self); bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in); +void common_hal_bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/ports/silabs/common-hal/_bleio/Connection.h b/ports/silabs/common-hal/_bleio/Connection.h index 56872024c932c..23976d3c84924 100644 --- a/ports/silabs/common-hal/_bleio/Connection.h +++ b/ports/silabs/common-hal/_bleio/Connection.h @@ -85,6 +85,7 @@ typedef struct void bleio_connection_clear(bleio_connection_internal_t *self); +void common_hal_bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal( diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf index 02e4e0d0d5647..d996ec05ac9a6 100644 --- a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf @@ -6,9 +6,20 @@ CONFIG_GPIO=y # Enable Bluetooth stack - bsim is for BT simulation CONFIG_BT=y CONFIG_BT_HCI=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y CONFIG_BT_OBSERVER=y CONFIG_BT_BROADCASTER=y +CONFIG_BT_L2CAP_TX_MTU=253 +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 + CONFIG_BT_DEVICE_NAME_DYNAMIC=y CONFIG_BT_DEVICE_NAME_MAX=28 diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.c b/ports/zephyr-cp/common-hal/_bleio/Adapter.c index d72a7bed865af..7f034cfc24d54 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.c +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -11,9 +11,10 @@ #include #include +#include #include -#include +#include "py/gc.h" #include "py/runtime.h" #include "bindings/zephyr_kernel/__init__.h" #include "shared-bindings/_bleio/__init__.h" @@ -62,6 +63,104 @@ static uint8_t bleio_address_type_from_zephyr(const bt_addr_le_t *addr) { } } +static uint8_t bleio_address_type_to_zephyr(uint8_t type) { + switch (type) { + case BLEIO_ADDRESS_TYPE_PUBLIC: + return BT_ADDR_LE_PUBLIC; + case BLEIO_ADDRESS_TYPE_RANDOM_STATIC: + case BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE: + case BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE: + return BT_ADDR_LE_RANDOM; + default: + return BT_ADDR_LE_PUBLIC; + } +} + +static bleio_connection_internal_t *bleio_connection_find_by_conn(const struct bt_conn *conn) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn == conn) { + return connection; + } + } + + return NULL; +} + +static bleio_connection_internal_t *bleio_connection_track(struct bt_conn *conn) { + bleio_connection_internal_t *connection = bleio_connection_find_by_conn(conn); + if (connection == NULL) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *candidate = &bleio_connections[i]; + if (candidate->conn == NULL) { + connection = candidate; + break; + } + } + } + + if (connection == NULL) { + return NULL; + } + + if (connection->conn == NULL) { + connection->conn = bt_conn_ref(conn); + } + + return connection; +} + +static void bleio_connection_clear(bleio_connection_internal_t *self) { + if (self == NULL) { + return; + } + + if (self->conn != NULL) { + bt_conn_unref(self->conn); + self->conn = NULL; + } + + self->connection_obj = mp_const_none; +} + +static void bleio_connection_release(bleio_connection_internal_t *connection, uint8_t reason) { + if (connection == NULL) { + return; + } + + if (connection->connection_obj != mp_const_none) { + bleio_connection_obj_t *connection_obj = MP_OBJ_TO_PTR(connection->connection_obj); + connection_obj->connection = NULL; + connection_obj->disconnect_reason = reason; + } + + bleio_connection_clear(connection); + common_hal_bleio_adapter_obj.connection_objs = NULL; +} + +static void bleio_connected_cb(struct bt_conn *conn, uint8_t err) { + if (err != 0) { + return; + } + + if (bleio_connection_track(conn) == NULL) { + bt_conn_disconnect(conn, BT_HCI_ERR_CONN_LIMIT_EXCEEDED); + return; + } + + common_hal_bleio_adapter_obj.connection_objs = NULL; +} + +static void bleio_disconnected_cb(struct bt_conn *conn, uint8_t reason) { + printk("disconnected %p\n", conn); + bleio_connection_release(bleio_connection_find_by_conn(conn), reason); +} + +BT_CONN_CB_DEFINE(bleio_connection_callbacks) = { + .connected = bleio_connected_cb, + .disconnected = bleio_disconnected_cb, +}; + static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf) { if (active_scan_results == NULL || info == NULL || buf == NULL) { return; @@ -129,8 +228,27 @@ static size_t bleio_parse_adv_data(const uint8_t *raw, size_t raw_len, struct bt return count; } +static uint16_t bleio_validate_and_convert_timeout(mp_float_t timeout) { + mp_arg_validate_float_range(timeout, 0, UINT16_MAX, MP_QSTR_timeout); + + if (timeout <= 0.0f) { + return 0; + } + + const mp_int_t timeout_units = + mp_arg_validate_int_range((mp_int_t)(timeout * 100.0f + 0.5f), 1, UINT16_MAX, MP_QSTR_timeout); + + return (uint16_t)timeout_units; +} + void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) { + if (enabled == ble_adapter_enabled) { + return; + } if (enabled) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_clear(&bleio_connections[i]); + } if (!bt_is_ready()) { int err = bt_enable(NULL); if (err != 0) { @@ -320,17 +438,7 @@ mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t uint16_t interval_units = (uint16_t)((interval / 0.000625f) + 0.5f); uint16_t window_units = (uint16_t)((window / 0.000625f) + 0.5f); - uint32_t timeout_units = 0; - - if (timeout > 0.0f) { - timeout_units = (uint32_t)(timeout * 100.0f + 0.5f); - if (timeout_units > UINT16_MAX) { - mp_raise_ValueError(MP_ERROR_TEXT("timeout must be < 655.35 secs")); - } - if (timeout_units == 0) { - mp_raise_ValueError(MP_ERROR_TEXT("non-zero timeout must be > 0.01")); - } - } + uint16_t timeout_units = bleio_validate_and_convert_timeout(timeout); struct bt_le_scan_param scan_params = { .type = active ? BT_LE_SCAN_TYPE_ACTIVE : BT_LE_SCAN_TYPE_PASSIVE, @@ -363,15 +471,110 @@ void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) { } bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) { + if (!ble_adapter_enabled) { + return false; + } + + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + if (bleio_connections[i].conn != NULL) { + return true; + } + } + return false; } mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) { - mp_raise_NotImplementedError(NULL); + if (!ble_adapter_enabled) { + self->connection_objs = NULL; + return mp_const_empty_tuple; + } + + if (self->connection_objs != NULL) { + return self->connection_objs; + } + + size_t total_connected = 0; + mp_obj_t items[BLEIO_TOTAL_CONNECTION_COUNT]; + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn == NULL) { + continue; + } + + if (connection->connection_obj == mp_const_none) { + connection->connection_obj = bleio_connection_new_from_internal(connection); + } + + items[total_connected] = connection->connection_obj; + total_connected++; + } + + self->connection_objs = mp_obj_new_tuple(total_connected, items); + return self->connection_objs; } mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { - mp_raise_NotImplementedError(NULL); + common_hal_bleio_adapter_stop_scan(self); + + const uint16_t timeout_units = bleio_validate_and_convert_timeout(timeout); + + mp_buffer_info_t address_bufinfo; + mp_get_buffer_raise(address->bytes, &address_bufinfo, MP_BUFFER_READ); + + bt_addr_le_t peer = { + .type = bleio_address_type_to_zephyr(address->type), + }; + memcpy(peer.a.val, address_bufinfo.buf, NUM_BLEIO_ADDRESS_BYTES); + + struct bt_conn_le_create_param create_params = BT_CONN_LE_CREATE_PARAM_INIT( + BT_CONN_LE_OPT_NONE, + BT_GAP_SCAN_FAST_INTERVAL, + BT_GAP_SCAN_FAST_INTERVAL); + create_params.timeout = timeout_units; + + struct bt_conn *conn = NULL; + int err = bt_conn_le_create(&peer, &create_params, BT_LE_CONN_PARAM_DEFAULT, &conn); + if (err != 0) { + raise_zephyr_error(err); + } + + while (true) { + struct bt_conn_info info; + err = bt_conn_get_info(conn, &info); + if (err == 0) { + if (info.state == BT_CONN_STATE_CONNECTED) { + break; + } + + if (info.state == BT_CONN_STATE_DISCONNECTED) { + bt_conn_unref(conn); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Failed to connect")); + } + } else if (err != -ENOTCONN) { + bt_conn_unref(conn); + raise_zephyr_error(err); + } + + RUN_BACKGROUND_TASKS; + } + + bleio_connection_internal_t *connection = bleio_connection_find_by_conn(conn); + if (connection == NULL) { + connection = bleio_connection_track(conn); + } + + if (connection == NULL) { + bt_conn_unref(conn); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Failed to connect: internal error")); + } + + // bt_conn_le_create() gave us a ref in `conn`; `connection` keeps its own + // ref via bleio_connection_track(). Drop the create ref now. + bt_conn_unref(conn); + + self->connection_objs = NULL; + return bleio_connection_new_from_internal(connection); } void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self) { @@ -383,13 +586,32 @@ bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self) { } void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter) { - // Nothing to do for now. + gc_collect_root((void **)adapter, sizeof(bleio_adapter_obj_t) / sizeof(size_t)); + gc_collect_root((void **)bleio_connections, sizeof(bleio_connections) / sizeof(size_t)); } void bleio_adapter_reset(bleio_adapter_obj_t *adapter) { if (adapter == NULL) { return; } + + common_hal_bleio_adapter_stop_scan(adapter); + common_hal_bleio_adapter_stop_advertising(adapter); + + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn != NULL) { + bt_conn_disconnect(connection->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + if (connection->connection_obj != MP_OBJ_NULL && + connection->connection_obj != mp_const_none) { + bleio_connection_obj_t *connection_obj = MP_OBJ_TO_PTR(connection->connection_obj); + connection_obj->connection = NULL; + connection_obj->disconnect_reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN; + } + bleio_connection_clear(connection); + } + adapter->scan_results = NULL; adapter->connection_objs = NULL; active_scan_results = NULL; diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.h b/ports/zephyr-cp/common-hal/_bleio/Adapter.h index dda9075776e6e..c15c698e2a525 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.h +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.h @@ -13,8 +13,7 @@ #include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/ScanResults.h" -#define BLEIO_TOTAL_CONNECTION_COUNT 5 -#define BLEIO_HANDLE_INVALID 0xffff +#define BLEIO_TOTAL_CONNECTION_COUNT CONFIG_BT_MAX_CONN extern bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.c b/ports/zephyr-cp/common-hal/_bleio/Connection.c index 2e93b6ab127b6..938359c79caf7 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Connection.c +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.c @@ -5,7 +5,14 @@ // // SPDX-License-Identifier: MIT +#include + +#include +#include + #include "py/runtime.h" +#include "bindings/zephyr_kernel/__init__.h" +#include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Connection.h" void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) { @@ -13,15 +20,47 @@ void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bo } void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) { - mp_raise_NotImplementedError(NULL); + if (self == NULL || self->conn == NULL) { + return; + } + + int err = bt_conn_disconnect(self->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + if (err != 0 && err != -ENOTCONN) { + raise_zephyr_error(err); + } + + // The connection may now be disconnecting; force connections tuple rebuild. + common_hal_bleio_adapter_obj.connection_objs = NULL; } bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self) { - return false; + if (self == NULL || self->connection == NULL) { + return false; + } + + bleio_connection_internal_t *connection = self->connection; + if (connection->conn == NULL) { + return false; + } + + struct bt_conn_info info; + if (bt_conn_get_info(connection->conn, &info) != 0) { + return false; + } + + return info.state == BT_CONN_STATE_CONNECTED || info.state == BT_CONN_STATE_DISCONNECTING; } mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_internal_t *self) { - return 20; + if (self == NULL || self->conn == NULL) { + return 20; + } + + uint16_t mtu = bt_gatt_get_mtu(self->conn); + if (mtu < 3) { + return 20; + } + return mtu - 3; } bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self) { @@ -40,18 +79,19 @@ void common_hal_bleio_connection_set_connection_interval(bleio_connection_intern mp_raise_NotImplementedError(NULL); } -void bleio_connection_clear(bleio_connection_internal_t *self) { - // Nothing to do -} +mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection) { + if (connection == NULL) { + return mp_const_none; + } -uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self) { - return self->connection->conn_handle; -} + if (connection->connection_obj != mp_const_none) { + return connection->connection_obj; + } -mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection) { - mp_raise_NotImplementedError(NULL); -} + bleio_connection_obj_t *connection_obj = mp_obj_malloc(bleio_connection_obj_t, &bleio_connection_type); + connection_obj->connection = connection; + connection_obj->disconnect_reason = 0; + connection->connection_obj = MP_OBJ_FROM_PTR(connection_obj); -bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle) { - return NULL; + return connection->connection_obj; } diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.h b/ports/zephyr-cp/common-hal/_bleio/Connection.h index f8f9581ad00bb..dc14125db5f7c 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Connection.h +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.h @@ -10,31 +10,12 @@ #include #include "py/obj.h" -#include "py/objlist.h" -#include "common-hal/_bleio/__init__.h" -#include "shared-module/_bleio/Address.h" -#include "common-hal/_bleio/Service.h" - -typedef enum { - PAIR_NOT_PAIRED, - PAIR_WAITING, - PAIR_PAIRED, -} pair_status_t; +struct bt_conn; typedef struct { - uint16_t conn_handle; - bool is_central; - mp_obj_list_t *remote_service_list; - uint16_t ediv; - volatile pair_status_t pair_status; - uint8_t sec_status; + struct bt_conn *conn; mp_obj_t connection_obj; - volatile bool conn_params_updating; - uint16_t mtu; - volatile bool do_bond_cccds; - volatile bool do_bond_keys; - uint64_t do_bond_cccds_request_time; } bleio_connection_internal_t; typedef struct { @@ -43,7 +24,4 @@ typedef struct { uint8_t disconnect_reason; } bleio_connection_obj_t; -void bleio_connection_clear(bleio_connection_internal_t *self); -uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); -bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.c b/ports/zephyr-cp/common-hal/_bleio/__init__.c index fca1eae98278d..719564c1cd47e 100644 --- a/ports/zephyr-cp/common-hal/_bleio/__init__.c +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.c @@ -8,6 +8,7 @@ #include "py/runtime.h" #include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Adapter.h" +#include "common-hal/_bleio/Adapter.h" #include "supervisor/shared/bluetooth/bluetooth.h" // The singleton _bleio.Adapter object @@ -15,61 +16,36 @@ bleio_adapter_obj_t common_hal_bleio_adapter_obj; void common_hal_bleio_init(void) { common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; + bleio_adapter_reset(&common_hal_bleio_adapter_obj); } void bleio_user_reset(void) { - common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); - common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); - bleio_adapter_reset(&common_hal_bleio_adapter_obj); - - if (supervisor_bluetooth_workflow_is_enabled()) { - supervisor_bluetooth_background(); + if (common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { + // Stop any user scanning or advertising. + common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); + common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); } + + // Maybe start advertising the BLE workflow. + supervisor_bluetooth_background(); } void bleio_reset(void) { common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; + if (!common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { + return; + } - common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); - common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); - - // Keep Zephyr BLE transport up, but present a disabled adapter state. - common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); + supervisor_stop_bluetooth(); bleio_adapter_reset(&common_hal_bleio_adapter_obj); - - if (supervisor_bluetooth_workflow_is_enabled()) { - supervisor_start_bluetooth(); - } + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); + supervisor_start_bluetooth(); } void common_hal_bleio_gc_collect(void) { - // Nothing to do for stubs -} - -void common_hal_bleio_check_connected(uint16_t conn_handle) { - mp_raise_NotImplementedError(NULL); -} - -uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device) { - mp_raise_NotImplementedError(NULL); + bleio_adapter_gc_collect(&common_hal_bleio_adapter_obj); } void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) { mp_raise_NotImplementedError(NULL); } - -size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { - mp_raise_NotImplementedError(NULL); -} - -void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { - mp_raise_NotImplementedError(NULL); -} - -size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { - mp_raise_NotImplementedError(NULL); -} - -void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { - mp_raise_NotImplementedError(NULL); -} diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index e00cf6cac8dc1..3da878e0f5b7c 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -414,7 +414,8 @@ async def build_circuitpython(): supervisor_source = [pathlib.Path(p) for p in supervisor_source] supervisor_source.extend(board_info["source_files"]) supervisor_source.extend(top.glob("supervisor/shared/*.c")) - supervisor_source.append(top / "supervisor/shared/bluetooth/bluetooth.c") + if "_bleio" in enabled_modules: + supervisor_source.append(top / "supervisor/shared/bluetooth/bluetooth.c") supervisor_source.append(top / "supervisor/shared/translate/translate.c") if web_workflow_enabled: supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c")) diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 08a84043e8eeb..0d8a2f48c1356 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -9,10 +9,6 @@ #include "mpconfigboard.h" #include "supervisor/shared/tick.h" -#if CIRCUITPY_BLEIO -#include "shared-bindings/_bleio/__init__.h" -#endif - #include #include #include @@ -93,10 +89,6 @@ void reset_cpu(void) { } void reset_port(void) { - #if CIRCUITPY_BLEIO - bleio_reset(); - #endif - #if defined(CONFIG_ARCH_POSIX) native_sim_reset_port_count++; if (native_sim_vm_runs != INT32_MAX && diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py new file mode 100644 index 0000000000000..21cfeaf79da50 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py @@ -0,0 +1,119 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE central connection tests for nrf5340bsim.""" + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_CONNECT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("connect start") +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable: + target = entry.address + print("found target") + break +adapter.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No connectable target found") + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected, adapter.connected, len(adapter.connections)) +connection.disconnect() + +for _ in range(40): + if not connection.connected and not adapter.connected: + break + time.sleep(0.1) + +print("disconnected", connection.connected, adapter.connected, len(adapter.connections)) +""" + +BSIM_RECONNECT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("run start") +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable: + target = entry.address + print("run found target") + break +adapter.stop_scan() +print("run have target", target is not None) + +if target is None: + raise RuntimeError("No connectable target found") + +connection = adapter.connect(target, timeout=5.0) +print("run connected", connection.connected, adapter.connected, len(adapter.connections)) +connection.disconnect() + +for _ in range(50): + if not connection.connected and not adapter.connected and len(adapter.connections) == 0: + break + time.sleep(0.1) + +print("run disconnected", connection.connected, adapter.connected, len(adapter.connections)) +""" + + +@pytest.mark.zephyr_sample("bluetooth/peripheral") +@pytest.mark.duration(14) +@pytest.mark.circuitpy_drive({"code.py": BSIM_CONNECT_CODE}) +def test_bsim_connect_zephyr_peripheral(bsim_phy, circuitpython, zephyr_sample): + """Connect to the Zephyr peripheral sample and disconnect cleanly.""" + peripheral = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + peripheral_output = peripheral.serial.all_output + + assert "connect start" in cp_output + assert "found target" in cp_output + assert "have target True" in cp_output + assert "connected True True 1" in cp_output + assert "disconnected False False 0" in cp_output + + assert "Advertising successfully started" in peripheral_output + assert "Connected" in peripheral_output + + +@pytest.mark.zephyr_sample("bluetooth/peripheral_sc_only") +@pytest.mark.code_py_runs(2) +@pytest.mark.duration(26) +@pytest.mark.circuitpy_drive({"code.py": BSIM_RECONNECT_CODE}) +def test_bsim_reconnect_zephyr_peripheral(bsim_phy, circuitpython, zephyr_sample): + """Connect/disconnect, soft reload, then connect/disconnect again.""" + peripheral = zephyr_sample + + circuitpython.serial.wait_for("run disconnected") + circuitpython.serial.wait_for("Press any key to enter the REPL") + circuitpython.serial.write("\x04") + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + peripheral_output = peripheral.serial.all_output + + assert cp_output.count("run start") >= 2 + assert cp_output.count("run found target") >= 2 + assert cp_output.count("run have target True") >= 2 + assert cp_output.count("run connected True True 1") >= 2 + assert cp_output.count("run disconnected False False 0") >= 2 + + assert "Advertising successfully started" in peripheral_output + assert peripheral_output.count("Connected") >= 2 diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c index 839b8b19addfa..a1f81a063fdaa 100644 --- a/shared-bindings/_bleio/Adapter.c +++ b/shared-bindings/_bleio/Adapter.c @@ -215,7 +215,7 @@ static mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT); } - const mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + const mp_float_t interval = mp_arg_validate_type_float(args[ARG_interval].u_obj, MP_QSTR_interval); if (interval < ADV_INTERVAL_MIN || interval > ADV_INTERVAL_MAX) { mp_raise_ValueError_varg(MP_ERROR_TEXT("interval must be in range %s-%s"), ADV_INTERVAL_MIN_STRING, ADV_INTERVAL_MAX_STRING); @@ -223,7 +223,7 @@ static mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t bool connectable = args[ARG_connectable].u_bool; bool anonymous = args[ARG_anonymous].u_bool; - uint32_t timeout = args[ARG_timeout].u_int; + const uint32_t timeout = (uint32_t)mp_arg_validate_int_min(args[ARG_timeout].u_int, 0, MP_QSTR_timeout); if (data_bufinfo.len > 31 && connectable && scan_response_bufinfo.len > 0) { mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Cannot have scan responses for extended, connectable advertisements.")); } @@ -306,7 +306,7 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args mp_float_t timeout = 0.0f; if (args[ARG_timeout].u_obj != mp_const_none) { - timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + timeout = mp_arg_validate_obj_float_non_negative(args[ARG_timeout].u_obj, 0.0f, MP_QSTR_timeout); } if (args[ARG_interval].u_obj == MP_OBJ_NULL) { @@ -317,7 +317,7 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args args[ARG_window].u_obj = mp_obj_new_float(WINDOW_DEFAULT); } - const mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + const mp_float_t interval = mp_arg_validate_type_float(args[ARG_interval].u_obj, MP_QSTR_interval); if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { mp_raise_ValueError_varg(MP_ERROR_TEXT("interval must be in range %s-%s"), INTERVAL_MIN_STRING, INTERVAL_MAX_STRING); } @@ -329,7 +329,7 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args } #pragma GCC diagnostic pop - const mp_float_t window = mp_obj_get_float(args[ARG_window].u_obj); + const mp_float_t window = mp_arg_validate_type_float(args[ARG_window].u_obj, MP_QSTR_window); if (window > interval) { mp_raise_ValueError(MP_ERROR_TEXT("window must be <= interval")); } @@ -344,7 +344,9 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args } } - return common_hal_bleio_adapter_start_scan(self, prefix_bufinfo.buf, prefix_bufinfo.len, args[ARG_extended].u_bool, args[ARG_buffer_size].u_int, timeout, interval, window, args[ARG_minimum_rssi].u_int, args[ARG_active].u_bool); + const mp_int_t buffer_size = mp_arg_validate_int_min(args[ARG_buffer_size].u_int, 1, MP_QSTR_buffer_size); + + return common_hal_bleio_adapter_start_scan(self, prefix_bufinfo.buf, prefix_bufinfo.len, args[ARG_extended].u_bool, buffer_size, timeout, interval, window, args[ARG_minimum_rssi].u_int, args[ARG_active].u_bool); } static MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_start_scan_obj, 1, bleio_adapter_start_scan); @@ -416,7 +418,8 @@ static mp_obj_t bleio_adapter_connect(mp_uint_t n_args, const mp_obj_t *pos_args mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); bleio_address_obj_t *address = mp_arg_validate_type(args[ARG_address].u_obj, &bleio_address_type, MP_QSTR_address); - mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + const mp_float_t timeout = + mp_arg_validate_obj_float_non_negative(args[ARG_timeout].u_obj, 0.0f, MP_QSTR_timeout); return common_hal_bleio_adapter_connect(self, address, timeout); } diff --git a/shared-bindings/_bleio/Address.c b/shared-bindings/_bleio/Address.c index 58f8a8adc1e61..e39fa4fe40ba0 100644 --- a/shared-bindings/_bleio/Address.c +++ b/shared-bindings/_bleio/Address.c @@ -42,14 +42,13 @@ static mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t address = args[ARG_address].u_obj; mp_buffer_info_t buf_info; mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); - if (buf_info.len != NUM_BLEIO_ADDRESS_BYTES) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("Address must be %d bytes long"), NUM_BLEIO_ADDRESS_BYTES); - } + mp_arg_validate_length(buf_info.len, NUM_BLEIO_ADDRESS_BYTES, MP_QSTR_address); - const mp_int_t address_type = args[ARG_address_type].u_int; - if (address_type < BLEIO_ADDRESS_TYPE_MIN || address_type > BLEIO_ADDRESS_TYPE_MAX) { - mp_arg_error_invalid(MP_QSTR_address_type); - } + const mp_int_t address_type = + mp_arg_validate_int_range(args[ARG_address_type].u_int, + BLEIO_ADDRESS_TYPE_MIN, + BLEIO_ADDRESS_TYPE_MAX, + MP_QSTR_address_type); common_hal_bleio_address_construct(self, buf_info.buf, address_type); diff --git a/shared-bindings/_bleio/Characteristic.c b/shared-bindings/_bleio/Characteristic.c index 8d6ac43e487d1..b017295b45fad 100644 --- a/shared-bindings/_bleio/Characteristic.c +++ b/shared-bindings/_bleio/Characteristic.c @@ -116,9 +116,9 @@ static mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ } mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - if (initial_value_bufinfo.len > max_length || - (fixed_length && initial_value_bufinfo.len != max_length)) { - mp_raise_ValueError(MP_ERROR_TEXT("initial_value length is wrong")); + mp_arg_validate_length_max(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); + if (fixed_length) { + mp_arg_validate_length(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); } const char *user_description = NULL; diff --git a/shared-bindings/_bleio/Descriptor.c b/shared-bindings/_bleio/Descriptor.c index 57cc605029f4a..8e721ff292d45 100644 --- a/shared-bindings/_bleio/Descriptor.c +++ b/shared-bindings/_bleio/Descriptor.c @@ -99,9 +99,9 @@ static mp_obj_t bleio_descriptor_add_to_characteristic(size_t n_args, const mp_o } } mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - if (initial_value_bufinfo.len > max_length || - (fixed_length && initial_value_bufinfo.len != max_length)) { - mp_raise_ValueError(MP_ERROR_TEXT("initial_value length is wrong")); + mp_arg_validate_length_max(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); + if (fixed_length) { + mp_arg_validate_length(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); } bleio_descriptor_obj_t *descriptor = mp_obj_malloc(bleio_descriptor_obj_t, &bleio_descriptor_type); diff --git a/shared-bindings/_bleio/PacketBuffer.c b/shared-bindings/_bleio/PacketBuffer.c index 47d71ebd55af4..aa1e6e8645a61 100644 --- a/shared-bindings/_bleio/PacketBuffer.c +++ b/shared-bindings/_bleio/PacketBuffer.c @@ -57,7 +57,10 @@ static mp_obj_t bleio_packet_buffer_make_new(const mp_obj_type_t *type, size_t n size_t max_packet_size = common_hal_bleio_characteristic_get_max_length(characteristic); if (args[ARG_max_packet_size].u_obj != mp_const_none) { - max_packet_size = mp_obj_get_int(args[ARG_max_packet_size].u_obj); + const mp_int_t max_packet_size_int = + mp_arg_validate_type_int(args[ARG_max_packet_size].u_obj, MP_QSTR_max_packet_size); + max_packet_size = + (size_t)mp_arg_validate_int_min(max_packet_size_int, 1, MP_QSTR_max_packet_size); } bleio_packet_buffer_obj_t *self = mp_obj_malloc(bleio_packet_buffer_obj_t, &bleio_packet_buffer_type); diff --git a/shared-bindings/_bleio/UUID.c b/shared-bindings/_bleio/UUID.c index 2d28d5a9b61a6..165c2982f08a4 100644 --- a/shared-bindings/_bleio/UUID.c +++ b/shared-bindings/_bleio/UUID.c @@ -40,10 +40,8 @@ static mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, si uint8_t uuid128[16]; if (mp_obj_is_int(value)) { - mp_int_t uuid16 = mp_obj_get_int(value); - if (uuid16 < 0 || uuid16 > 0xffff) { - mp_raise_ValueError(MP_ERROR_TEXT("UUID integer value must be 0-0xffff")); - } + const mp_int_t uuid16 = + mp_arg_validate_int_range(mp_obj_get_int(value), 0, 0xffff, MP_QSTR_value); // NULL means no 128-bit value. common_hal_bleio_uuid_construct(self, uuid16, NULL); @@ -82,9 +80,7 @@ static mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, si mp_raise_ValueError(MP_ERROR_TEXT("UUID value is not str, int or byte buffer")); } - if (bufinfo.len != 16) { - mp_raise_ValueError(MP_ERROR_TEXT("Byte buffer must be 16 bytes.")); - } + mp_arg_validate_length(bufinfo.len, 16, MP_QSTR_value); memcpy(uuid128, bufinfo.buf, 16); } @@ -171,12 +167,12 @@ static mp_obj_t bleio_uuid_pack_into(mp_uint_t n_args, const mp_obj_t *pos_args, mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); - size_t offset = args[ARG_offset].u_int; - if (offset + common_hal_bleio_uuid_get_size(self) / 8 > bufinfo.len) { - mp_raise_ValueError(MP_ERROR_TEXT("Buffer + offset too small %d %d %d")); - } + const mp_int_t offset = + mp_arg_validate_int_range(args[ARG_offset].u_int, 0, (mp_int_t)bufinfo.len, MP_QSTR_offset); + const size_t packed_len = common_hal_bleio_uuid_get_size(self) / 8; + mp_arg_validate_length_min(bufinfo.len - (size_t)offset, packed_len, MP_QSTR_buffer); - common_hal_bleio_uuid_pack_into(self, bufinfo.buf + offset); + common_hal_bleio_uuid_pack_into(self, (uint8_t *)bufinfo.buf + (size_t)offset); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(bleio_uuid_pack_into_obj, 1, bleio_uuid_pack_into); diff --git a/shared-bindings/_bleio/__init__.h b/shared-bindings/_bleio/__init__.h index faf11ea1d0637..f7428d2fb2138 100644 --- a/shared-bindings/_bleio/__init__.h +++ b/shared-bindings/_bleio/__init__.h @@ -50,14 +50,6 @@ NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg); NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t msg, ...); bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void); -void common_hal_bleio_check_connected(uint16_t conn_handle); - -uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); -size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); -void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); -size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); -void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); - void common_hal_bleio_gc_collect(void); From c0177c82086487725186c85e0bb6b82ffc0f121c Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Tue, 3 Mar 2026 11:23:49 +0100 Subject: [PATCH 398/517] Added qspibus = false for all Zephyr boards --- .../adafruit/feather_nrf52840_zephyr/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml | 1 + .../zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml | 1 + .../zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml | 1 + .../boards/renesas/da14695_dk_usb/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml | 1 + 17 files changed, 17 insertions(+) diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 43b018c327adb..9712f467858eb 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 6261be1ec7585..80b1b4ebf7d6a 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index a512a48088988..1bc1b96e6cf5c 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 80cf2e119ed39..7869cca4fafba 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index b3f72751cbbac..c2233ddf8b544 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index f4d2dd478c5f7..a1e8de8822b49 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 2557bc74df48e..b50b1966ed074 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index b7cd8eb61104f..21d55194a1c1c 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index c24d0a8527366..d5464e6e20bbc 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index b2f384e21c1b6..de72979513ca7 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index aa947de521397..d6efa285fe2a4 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index f7495b527230d..7600b8bbd151a 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index b65c12b0aa93e..8e49b95d33416 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index 92122525a2933..b28a9481c72d9 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index e73b1c062a5fd..6b0ef8d8480f1 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index 949cb89f0828a..b6f03f3d627c6 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index 50c17ef6dba7c..8d1fd9253488b 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -79,6 +79,7 @@ ps2io = false pulseio = false pwmio = false qrio = false +qspibus = false rainbowio = true random = true rclcpy = false From 1c481e8a15fde9efd98d485fbd460e97442861ed Mon Sep 17 00:00:00 2001 From: Liz Date: Tue, 3 Mar 2026 12:22:19 -0500 Subject: [PATCH 399/517] add xteink x4 with ssd1677 eink --- ports/espressif/boards/xteink_x4/board.c | 134 ++++++++++++++++++ .../boards/xteink_x4/mpconfigboard.h | 18 +++ .../boards/xteink_x4/mpconfigboard.mk | 29 ++++ ports/espressif/boards/xteink_x4/pins.c | 52 +++++++ ports/espressif/boards/xteink_x4/sdkconfig | 14 ++ 5 files changed, 247 insertions(+) create mode 100644 ports/espressif/boards/xteink_x4/board.c create mode 100644 ports/espressif/boards/xteink_x4/mpconfigboard.h create mode 100644 ports/espressif/boards/xteink_x4/mpconfigboard.mk create mode 100644 ports/espressif/boards/xteink_x4/pins.c create mode 100644 ports/espressif/boards/xteink_x4/sdkconfig diff --git a/ports/espressif/boards/xteink_x4/board.c b/ports/espressif/boards/xteink_x4/board.c new file mode 100644 index 0000000000000..bf6fbddebc948 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/board.c @@ -0,0 +1,134 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// SPDX-FileCopyrightText: Copyright (c) 2021 skieast/Bruce Segal +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/board.h" + +#define DELAY 0x80 + +// SSD1677 controller driving a GDEQ0426T82 4.26" 800x480 grayscale E-Ink display. + +const uint8_t ssd1677_display_start_sequence[] = { + // Software Reset + 0x12, DELAY, 0x00, 0x14, // SWRESET + wait 20ms + + // Temperature Sensor Control (use internal sensor) + 0x18, 0x00, 0x01, 0x80, + + // Booster Soft Start + 0x0C, 0x00, 0x05, 0xAE, 0xC7, 0xC3, 0xC0, 0x40, + + // Driver Output Control: 479 gates (HEIGHT-1 = 0x01DF) + 0x01, 0x00, 0x03, 0xDF, 0x01, 0x02, + + // Data Entry Mode: X increment, Y increment + 0x11, 0x00, 0x01, 0x02, + + // Border Waveform Control + 0x3C, 0x00, 0x01, 0x01, + + // Set RAM X Address Start/End: 0 to 799 + + // X start = 0 (LE: 0x00, 0x00), X end = 799 (LE: 0x1F, 0x03) + 0x44, 0x00, 0x04, 0x00, 0x00, 0x1F, 0x03, + + // Set RAM Y Address Start/End: 0 to 479 + 0x45, 0x00, 0x04, 0x00, 0x00, 0xDF, 0x01, + + // Set RAM X Counter to 0 + 0x4E, 0x00, 0x02, 0x00, 0x00, + + // Set RAM Y Counter to 0 + 0x4F, 0x00, 0x02, 0x00, 0x00, + + // Auto Write BW RAM (clear to white) + 0x46, DELAY, 0x01, 0xF7, 0xFF, // + wait 255ms + + // Display Update Control 1: bypass RED buffer for mono mode + 0x21, 0x00, 0x02, 0x40, 0x00, + + // Display Update Control 2: full refresh sequence with OTP LUT + 0x22, 0x00, 0x01, 0xF7, +}; + +const uint8_t ssd1677_display_stop_sequence[] = { + // Power off sequence + 0x22, 0x00, 0x01, 0x83, // Display update control: power off + 0x20, 0x00, 0x00, // Master activation + // Deep sleep + 0x10, 0x00, 0x01, 0x01, // Enter deep sleep mode +}; + +const uint8_t ssd1677_display_refresh_sequence[] = { + 0x20, 0x00, 0x00 +}; + +void board_init(void) { + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct(spi, &pin_GPIO8, &pin_GPIO10, NULL, false); + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + common_hal_fourwire_fourwire_construct(bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO4), + MP_OBJ_FROM_PTR(&pin_GPIO21), + MP_OBJ_FROM_PTR(&pin_GPIO5), + 40000000, + 0, + 0); + + epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; + display->base.type = &epaperdisplay_epaperdisplay_type; + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = ssd1677_display_start_sequence; + args.start_sequence_len = sizeof(ssd1677_display_start_sequence); + args.stop_sequence = ssd1677_display_stop_sequence; + args.stop_sequence_len = sizeof(ssd1677_display_stop_sequence); + args.width = 800; + args.height = 480; + args.ram_width = 800; + args.ram_height = 480; + args.rotation = 0; + args.write_black_ram_command = 0x24; + args.black_bits_inverted = true; + args.refresh_sequence = ssd1677_display_refresh_sequence; + args.refresh_sequence_len = sizeof(ssd1677_display_refresh_sequence); + args.refresh_time = 1.6; // ~1600ms full refresh + args.busy_pin = &pin_GPIO6; + args.busy_state = true; // BUSY is active HIGH on SSD1677 + args.seconds_per_frame = 5.0; + args.grayscale = false; + args.two_byte_sequence_length = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); +} + +bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + return false; +} + +void board_deinit(void) { + epaperdisplay_epaperdisplay_obj_t *display = &displays[0].epaper_display; + if (display->base.type == &epaperdisplay_epaperdisplay_type) { + while (common_hal_epaperdisplay_epaperdisplay_get_busy(display)) { + RUN_BACKGROUND_TASKS; + } + } + common_hal_displayio_release_displays(); +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/xteink_x4/mpconfigboard.h b/ports/espressif/boards/xteink_x4/mpconfigboard.h new file mode 100644 index 0000000000000..5d4bb37222826 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/mpconfigboard.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// SPDX-FileCopyrightText: Copyright (c) 2021 skieast/Bruce Segal +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Board setup +#define MICROPY_HW_BOARD_NAME "Xteink X4" +#define MICROPY_HW_MCU_NAME "ESP32-C3" + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO8, .mosi = &pin_GPIO10, .miso = &pin_GPIO7}} + +// For entering safe mode +#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO3) diff --git a/ports/espressif/boards/xteink_x4/mpconfigboard.mk b/ports/espressif/boards/xteink_x4/mpconfigboard.mk new file mode 100644 index 0000000000000..bed8b2c3fa931 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/mpconfigboard.mk @@ -0,0 +1,29 @@ +CIRCUITPY_CREATOR_ID = 0x0000303A +CIRCUITPY_CREATION_ID = 0x00001001 + +IDF_TARGET = esp32c3 + +CIRCUITPY_ESP_FLASH_MODE = dio +CIRCUITPY_ESP_FLASH_FREQ = 40m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_USB_SERIAL_JTAG = 1 + +CIRCUITPY_PARALLELDISPLAYBUS = 0 +CIRCUITPY_RGBMATRIX = 0 +CIRCUITPY_AUDIOBUSIO = 0 +CIRCUITPY_AUDIOCORE = 0 +CIRCUITPY_AUDIOMIXER = 0 +CIRCUITPY_AUDIOMP3 = 0 +CIRCUITPY_CAMERA = 0 +CIRCUITPY_CANIO = 0 +CIRCUITPY_DOTCLOCKFRAMEBUFFER = 0 +CIRCUITPY_KEYPAD = 0 +CIRCUITPY_ROTARYIO = 0 +CIRCUITPY_USB_HID = 0 +CIRCUITPY_USB_MIDI = 0 +CIRCUITPY_ULAB = 0 +CIRCUITPY_PULSEIO = 0 +CIRCUITPY_PWMIO = 0 +CIRCUITPY_RAINBOWIO = 0 +CIRCUITPY_SYNTHIO = 0 diff --git a/ports/espressif/boards/xteink_x4/pins.c b/ports/espressif/boards/xteink_x4/pins.c new file mode 100644 index 0000000000000..6b8232d4cb3a6 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/pins.c @@ -0,0 +1,52 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// SPDX-FileCopyrightText: Copyright (c) 2021 skieast/Bruce Segal +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_ADC_1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_ADC_2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_EPD_MOSI), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_EPD_SCK), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_EPD_BUSY), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_EPD_RESET), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_EPD_DC), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_EPD_CS), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].epaper_display)}, + +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/xteink_x4/sdkconfig b/ports/espressif/boards/xteink_x4/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration From 17805a82babca42bca8adeaf12ba5cb219e7d52d Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Mar 2026 11:12:41 -0800 Subject: [PATCH 400/517] Rerun the flaky zephyr test --- ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py | 1 + requirements-dev.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py index 3a022944e0068..ffc4cb6eabe4f 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py @@ -91,6 +91,7 @@ def test_bsim_scan_zephyr_beacon_reload(bsim_phy, circuitpython, zephyr_sample): assert output.count("scan run done True") >= 2 +@pytest.mark.flaky(reruns=3) @pytest.mark.zephyr_sample("bluetooth/beacon") @pytest.mark.code_py_runs(2) @pytest.mark.duration(8) diff --git a/requirements-dev.txt b/requirements-dev.txt index 46d5bd516c7f7..6a33c49daecb2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -41,4 +41,5 @@ setuptools # For zephyr port tomlkit pytest +pytest-rerunfailures perfetto From 4378f51524c8b6beb5e30bf738b627522d276351 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Tue, 3 Mar 2026 22:52:42 +0100 Subject: [PATCH 401/517] Fixes after the review --- locale/circuitpython.pot | 13 +- .../waveshare_esp32_s3_amoled_241/board.c | 10 ++ .../mpconfigboard.h | 11 +- .../mpconfigboard.mk | 3 - .../waveshare_esp32_s3_amoled_241/sdkconfig | 36 ------ ports/espressif/common-hal/qspibus/QSPIBus.c | 49 +++----- ports/espressif/common-hal/qspibus/QSPIBus.h | 1 - py/circuitpy_mpconfig.h | 18 +-- shared-bindings/displayio/__init__.h | 1 + shared-bindings/qspibus/QSPIBus.h | 1 + shared-module/busdisplay/BusDisplay.c | 115 +++--------------- shared-module/displayio/bus_core.c | 11 +- shared-module/displayio/bus_core.h | 7 +- .../framebufferio/FramebufferDisplay.c | 2 +- 14 files changed, 67 insertions(+), 211 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index e19fcc266eb48..ead836333eea3 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -647,10 +647,6 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Begin transaction first" -msgstr "" - #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -717,10 +713,6 @@ msgstr "" msgid "Buffer too small" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Bus in display transaction" -msgstr "" - #: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c @@ -1253,6 +1245,7 @@ msgstr "" msgid "Internal define error" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1611,10 +1604,6 @@ msgstr "" msgid "No out in program" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "No pending command" -msgstr "" - #: ports/atmel-samd/common-hal/busio/I2C.c #: ports/espressif/common-hal/busio/I2C.c #: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nordic/common-hal/busio/I2C.c diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c index 29dbb64050a7e..7b57f48bcdbdd 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c @@ -5,6 +5,7 @@ #include "supervisor/board.h" #include "mpconfigboard.h" #include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/qspibus/QSPIBus.h" #include "shared-bindings/busdisplay/BusDisplay.h" @@ -42,6 +43,15 @@ static uint8_t display_init_sequence[] = { }; void board_init(void) { + // 0. Enable display power before any bus/display init. + digitalio_digitalinout_obj_t power_pin; + power_pin.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(&power_pin, CIRCUITPY_LCD_POWER); + common_hal_digitalio_digitalinout_set_value(&power_pin, true); + common_hal_digitalio_digitalinout_never_reset(&power_pin); + // Allow power rail to settle before reset/init. + mp_hal_delay_ms(200); + // 1. Allocate and construct QSPI bus qspibus_qspibus_obj_t *bus = &allocate_display_bus_or_raise()->qspi_bus; bus->base.type = &qspibus_qspibus_type; diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h index fcd3c0e5b0398..242cca6b30dd4 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h @@ -7,19 +7,13 @@ #define MICROPY_HW_BOARD_NAME "Waveshare ESP32-S3-Touch-AMOLED-2.41" #define MICROPY_HW_MCU_NAME "ESP32S3" -// USB identifiers -#define USB_VID 0x303A -#define USB_PID 0x8278 -#define USB_MANUFACTURER "Waveshare" -#define USB_PRODUCT "ESP32-S3-Touch-AMOLED-2.41" - // I2C bus - Disabled on boot to avoid conflicts. User must manually initialize I2C. #define CIRCUITPY_BOARD_I2C (0) #define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO48, .sda = &pin_GPIO47}} -// QSPI display refresh buffer: 2048 uint32_t words = 8KB on stack. +// Display refresh buffer: 2048 uint32_t words = 8KB on stack. // ESP32-S3 main task stack is 24KB; verified safe with this board. -#define CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE (2048) +#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (2048) // AMOLED Display (displayio + qspibus path) - initialized in board_init() #define CIRCUITPY_BOARD_DISPLAY (1) @@ -31,7 +25,6 @@ #define CIRCUITPY_LCD_D3 (&pin_GPIO14) #define CIRCUITPY_LCD_RESET (&pin_GPIO21) #define CIRCUITPY_LCD_POWER (&pin_GPIO16) -#define CIRCUITPY_LCD_POWER_ON_LEVEL (1) // GPIO level: 1=high, 0=low // No default SPI bus — SD card uses SDIO, display uses QSPI. #define CIRCUITPY_BOARD_SPI (0) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk index 17d5300100174..f901246804cdf 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk @@ -2,9 +2,6 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha # SPDX-License-Identifier: MIT -CIRCUITPY_CREATOR_ID = 0x57415645 # 'WAVE' (Waveshare) -CIRCUITPY_CREATION_ID = 0x41323431 # 'A241' (AMOLED 2.41) - USB_VID = 0x303A USB_PID = 0x8278 USB_MANUFACTURER = "Waveshare" diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig index b6c009f31162d..fa60cc4c2378e 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig @@ -1,39 +1,3 @@ # # Configuration file for the Waveshare ESP32-S3 Touch AMOLED 2.41 # - -# PSRAM Configuration -CONFIG_SPIRAM=y -CONFIG_SPIRAM_BOOT_INIT=y -CONFIG_SPIRAM_USE_MALLOC=y -CONFIG_SPIRAM_MODE_OCT=y -CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y -CONFIG_SPIRAM_RODATA=y -CONFIG_SPIRAM_SPEED_80M=y -CONFIG_SPIRAM_MEMTEST=n -CONFIG_SPIRAM_CLK_IO=39 -CONFIG_SPIRAM_CS_IO=38 - -# Performance and Cache -CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y -CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y - -# Default flash settings for this board -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="esp-idf-config/partitions-16MB.csv" -CONFIG_PARTITION_TABLE_FILENAME="esp-idf-config/partitions-16MB.csv" - -# Networking -CONFIG_LWIP_LOCAL_HOSTNAME="waveshare-esp32-s3-amoled" - -# Disable USB-Serial/JTAG console - CircuitPython uses TinyUSB (USB OTG) for REPL -CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=n - -# Workaround: NimBLE BLE_STATIC_TO_DYNAMIC=y (default in IDF 5.5.3) causes -# ble_gap_vars to be a dynamically allocated pointer (NULL before ble_gap_init). -# CircuitPython calls ble_gap_adv_active() before NimBLE init → NULL deref → bootloop. -# Reverting to static allocation avoids the crash. -CONFIG_BT_NIMBLE_STATIC_TO_DYNAMIC=n - -# Enable .app_desc structure -CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index 33c5f1282062e..b4fabe914786e 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -25,14 +25,6 @@ #define QSPI_DMA_BUFFER_COUNT (2U) #define QSPI_DMA_BUFFER_SIZE (16U * 1024U) #define QSPI_COLOR_TIMEOUT_MS (1000U) -#if defined(CIRCUITPY_LCD_POWER) -#define CIRCUITPY_QSPIBUS_PANEL_POWER_PIN CIRCUITPY_LCD_POWER -#endif - -#ifndef CIRCUITPY_LCD_POWER_ON_LEVEL -#define CIRCUITPY_LCD_POWER_ON_LEVEL (1) -#endif - static void qspibus_release_dma_buffers(qspibus_qspibus_obj_t *self) { for (size_t i = 0; i < QSPI_DMA_BUFFER_COUNT; i++) { if (self->dma_buffer[i] != NULL) { @@ -279,7 +271,6 @@ void common_hal_qspibus_qspibus_construct( self->cs_pin = cs->number; self->dcx_pin = (dcx != NULL) ? dcx->number : -1; self->reset_pin = (reset != NULL) ? reset->number : -1; - self->power_pin = -1; self->frequency = frequency; self->bus_initialized = false; self->in_transaction = false; @@ -319,7 +310,7 @@ void common_hal_qspibus_qspibus_construct( qspibus_release_dma_buffers(self); vSemaphoreDelete(self->transfer_done_sem); self->transfer_done_sem = NULL; - mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_SPI, (int)err); + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_SPI); } const esp_lcd_panel_io_spi_config_t io_config = { @@ -358,21 +349,6 @@ void common_hal_qspibus_qspibus_construct( gpio_set_level((gpio_num_t)self->dcx_pin, 1); } - #ifdef CIRCUITPY_QSPIBUS_PANEL_POWER_PIN - const mcu_pin_obj_t *power = CIRCUITPY_QSPIBUS_PANEL_POWER_PIN; - if (power != NULL) { - if (!common_hal_mcu_pin_is_free(power)) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_LCD_POWER); - } - self->power_pin = power->number; - claim_pin(power); - gpio_set_direction((gpio_num_t)self->power_pin, GPIO_MODE_OUTPUT); - gpio_set_level((gpio_num_t)self->power_pin, CIRCUITPY_LCD_POWER_ON_LEVEL ? 1 : 0); - // Panel power rail needs extra settle time before reset/init commands. - vTaskDelay(pdMS_TO_TICKS(200)); - } - #endif - if (reset != NULL) { claim_pin(reset); @@ -420,9 +396,6 @@ void common_hal_qspibus_qspibus_deinit(qspibus_qspibus_obj_t *self) { if (self->dcx_pin >= 0) { reset_pin_number(self->dcx_pin); } - if (self->power_pin >= 0) { - reset_pin_number(self->power_pin); - } if (self->reset_pin >= 0) { reset_pin_number(self->reset_pin); } @@ -446,7 +419,7 @@ void common_hal_qspibus_qspibus_write_command( raise_deinited_error(); } if (self->in_transaction) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Bus in display transaction")); + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); } // If caller stages command-only operations repeatedly, flush the previous @@ -467,7 +440,7 @@ void common_hal_qspibus_qspibus_write_data( raise_deinited_error(); } if (self->in_transaction) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Bus in display transaction")); + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); } if (len == 0) { if (self->has_pending_command) { @@ -477,7 +450,7 @@ void common_hal_qspibus_qspibus_write_data( return; } if (!self->has_pending_command) { - mp_raise_ValueError(MP_ERROR_TEXT("No pending command")); + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); } if (qspibus_is_color_payload_command(self->pending_command)) { @@ -545,7 +518,7 @@ void common_hal_qspibus_qspibus_send( raise_deinited_error(); } if (!self->in_transaction) { - mp_raise_RuntimeError(MP_ERROR_TEXT("Begin transaction first")); + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); } if (data_type == DISPLAY_COMMAND) { @@ -564,7 +537,7 @@ void common_hal_qspibus_qspibus_send( // Zero-length data write after a no-data command is benign. return; } - mp_raise_ValueError(MP_ERROR_TEXT("No pending command")); + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); } if (data_length == 0) { @@ -593,6 +566,16 @@ void common_hal_qspibus_qspibus_end_transaction(mp_obj_t obj) { self->in_transaction = false; } +void common_hal_qspibus_qspibus_flush(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + if (!self->bus_initialized) { + return; + } + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + } +} + void common_hal_qspibus_qspibus_collect_ptrs(mp_obj_t obj) { (void)obj; } diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.h b/ports/espressif/common-hal/qspibus/QSPIBus.h index caed3661cfd73..eaaa971a79c49 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.h +++ b/ports/espressif/common-hal/qspibus/QSPIBus.h @@ -33,7 +33,6 @@ typedef struct { int8_t cs_pin; int8_t dcx_pin; // -1 when optional DCX line is not provided. int8_t reset_pin; // -1 when reset line is not provided. - int8_t power_pin; // -1 when board has no explicit display power pin. uint32_t frequency; bool bus_initialized; diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index c7509753ec65a..7bad99769df67 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -394,25 +394,13 @@ typedef long mp_off_t; #define CIRCUITPY_DISPLAY_LIMIT (1) #endif -// Framebuffer area size in bytes. Rounded down to power of four for alignment. +// Display area buffer size in uint32_t words for _refresh_area() VLA. +// Allocated on stack; boards with larger displays can override per-board. +// Default 128 words = 512 bytes. #ifndef CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE #define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (128) #endif -// QSPI display buffer size in uint32_t words for _refresh_area() VLA. -// Allocated on stack; boards should verify sufficient stack headroom. -// Default 512 words = 2KB. Override per-board for larger buffers. -#ifndef CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE -#define CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE (512) -#endif - -// Port-level upper bound for the QSPI display buffer (uint32_t words). -// The _Static_assert in BusDisplay.c enforces this at compile time. -// Ports with larger stacks can raise this in mpconfigport.h. -#ifndef CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE_MAX -#define CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE_MAX (2048) -#endif - #else #define CIRCUITPY_DISPLAY_LIMIT (0) #define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (0) diff --git a/shared-bindings/displayio/__init__.h b/shared-bindings/displayio/__init__.h index 88e9650cf44a9..583f6b217fa9f 100644 --- a/shared-bindings/displayio/__init__.h +++ b/shared-bindings/displayio/__init__.h @@ -49,4 +49,5 @@ typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); typedef void (*display_bus_send)(mp_obj_t bus, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); typedef void (*display_bus_end_transaction)(mp_obj_t bus); +typedef void (*display_bus_flush)(mp_obj_t bus); typedef void (*display_bus_collect_ptrs)(mp_obj_t bus); diff --git a/shared-bindings/qspibus/QSPIBus.h b/shared-bindings/qspibus/QSPIBus.h index a5d8f824ad28e..140b639279dc0 100644 --- a/shared-bindings/qspibus/QSPIBus.h +++ b/shared-bindings/qspibus/QSPIBus.h @@ -49,4 +49,5 @@ void common_hal_qspibus_qspibus_send( const uint8_t *data, uint32_t data_length); void common_hal_qspibus_qspibus_end_transaction(mp_obj_t obj); +void common_hal_qspibus_qspibus_flush(mp_obj_t obj); void common_hal_qspibus_qspibus_collect_ptrs(mp_obj_t obj); diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index 4610721a061d7..e83250740ba28 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -16,9 +16,6 @@ #if CIRCUITPY_PARALLELDISPLAYBUS #include "shared-bindings/paralleldisplaybus/ParallelBus.h" #endif -#if CIRCUITPY_QSPIBUS -#include "shared-bindings/qspibus/QSPIBus.h" -#endif #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/__init__.h" @@ -217,7 +214,7 @@ static void _send_pixels(busdisplay_busdisplay_obj_t *self, uint8_t *pixels, uin } static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_area_t *area) { - uint32_t buffer_size = 128; // In uint32_ts + uint32_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE; // In uint32_ts displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. @@ -257,57 +254,6 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } } - #if CIRCUITPY_QSPIBUS - // QSPI panels benefit from larger sub-rectangle buffers because each chunk - // has non-trivial command/window overhead. Keep this path qspibus-specific - // to avoid increasing stack usage on other display buses. - // Guard: buffer is a VLA on stack. The port-specific max keeps shared - // code free of single-port assumptions (default 2048 words = 8KB). - _Static_assert(CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE <= CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE_MAX, - "CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE exceeds CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE_MAX"); - bool is_qspi_bus = mp_obj_is_type(self->bus.bus, &qspibus_qspibus_type); - if (is_qspi_bus && - self->core.colorspace.depth == 16 && - !self->bus.data_as_commands && - !self->bus.SH1107_addressing && - buffer_size < CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE) { - buffer_size = CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE; - rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped); - if (rows_per_buffer == 0) { - rows_per_buffer = 1; - } - // Clamp to actual display height. - if (rows_per_buffer > displayio_area_height(&clipped)) { - rows_per_buffer = displayio_area_height(&clipped); - } - subrectangles = displayio_area_height(&clipped) / rows_per_buffer; - if (displayio_area_height(&clipped) % rows_per_buffer != 0) { - subrectangles++; - } - pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); - buffer_size = pixels_per_buffer / pixels_per_word; - if (pixels_per_buffer % pixels_per_word) { - buffer_size += 1; - } - - // Ensure at least 2 rows per buffer when possible. - if (rows_per_buffer < 2 && - displayio_area_height(&clipped) > 1 && - (uint32_t)((2 * displayio_area_width(&clipped) + pixels_per_word - 1) / pixels_per_word) <= (uint32_t)CIRCUITPY_QSPI_DISPLAY_AREA_BUFFER_SIZE) { - rows_per_buffer = 2; - subrectangles = displayio_area_height(&clipped) / rows_per_buffer; - if (displayio_area_height(&clipped) % rows_per_buffer != 0) { - subrectangles++; - } - pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); - buffer_size = pixels_per_buffer / pixels_per_word; - if (pixels_per_buffer % pixels_per_word) { - buffer_size += 1; - } - } - } - #endif - // Allocated and shared as a uint32_t array so the compiler knows the // alignment everywhere. uint32_t mask_length = (pixels_per_buffer / 32) + 1; @@ -316,6 +262,8 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are uint16_t remaining_rows = displayio_area_height(&clipped); + bool async_bus = (self->bus.flush != NULL); + for (uint16_t j = 0; j < subrectangles; j++) { displayio_area_t subrectangle = { .x1 = clipped.x1, @@ -328,49 +276,33 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } remaining_rows -= rows_per_buffer; - #if CIRCUITPY_QSPIBUS - if (is_qspi_bus && - self->core.colorspace.depth >= 8 && - !self->bus.data_as_commands && - !self->bus.SH1107_addressing) { - // QSPI path: fill_area first (overlaps with previous DMA), - // then single-transaction set_region + RAMWR + pixels. - uint32_t subrectangle_size_bytes = (uint32_t)displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); + uint16_t subrectangle_size_bytes; + if (self->core.colorspace.depth >= 8) { + subrectangle_size_bytes = displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); + } else { + subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); + } - memset(mask, 0, mask_length * sizeof(mask[0])); - memset(buffer, 0, buffer_size * sizeof(buffer[0])); + memset(mask, 0, mask_length * sizeof(mask[0])); + memset(buffer, 0, buffer_size * sizeof(buffer[0])); + if (async_bus) { + // Async path: fill_area overlaps with previous DMA transfer. + // begin_transaction() waits for prior DMA to finish. displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); - // begin_transaction waits for any prior async DMA to finish, - // so fill_area above overlaps with previous DMA. if (!displayio_display_bus_begin_transaction(&self->bus)) { - // Transaction failed (bus deinitialized, timeout, etc.). - // Bail out to prevent calling send() outside a transaction. return false; } displayio_display_bus_send_region_commands(&self->bus, &self->core, &subrectangle); _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); displayio_display_bus_end_transaction(&self->bus); - } else - #endif - { - // Non-QSPI path: original ordering preserved exactly. + } else { + // Sync path: set region first, then fill and send. displayio_display_bus_set_region_to_update(&self->bus, &self->core, &subrectangle); - uint16_t subrectangle_size_bytes; - if (self->core.colorspace.depth >= 8) { - subrectangle_size_bytes = displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); - } else { - subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth); - } - - memset(mask, 0, mask_length * sizeof(mask[0])); - memset(buffer, 0, buffer_size * sizeof(buffer[0])); - displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); - // Can't acquire display bus; skip the rest of the data. if (!displayio_display_bus_is_free(&self->bus)) { return false; } @@ -390,19 +322,8 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are #endif } - #if CIRCUITPY_QSPIBUS - if (is_qspi_bus) { - // Drain the last async DMA transfer before returning. - // Within the loop, begin_transaction() waits for the PREVIOUS - // subrectangle's DMA, enabling fill_area/DMA overlap. But the - // LAST subrectangle's DMA is still in-flight when the loop ends. - // Without this drain, bus_free() returns false on the next - // refresh() call, causing it to be silently skipped. - if (displayio_display_bus_begin_transaction(&self->bus)) { - displayio_display_bus_end_transaction(&self->bus); - } - } - #endif + // Drain any remaining asynchronous transfers. + displayio_display_bus_flush(&self->bus); return true; } diff --git a/shared-module/displayio/bus_core.c b/shared-module/displayio/bus_core.c index 5afa42f26fdf8..7362120e65941 100644 --- a/shared-module/displayio/bus_core.c +++ b/shared-module/displayio/bus_core.c @@ -52,6 +52,8 @@ void displayio_display_bus_construct(displayio_display_bus_t *self, self->SH1107_addressing = SH1107_addressing; self->address_little_endian = address_little_endian; + self->flush = NULL; + #if CIRCUITPY_PARALLELDISPLAYBUS if (mp_obj_is_type(bus, ¶lleldisplaybus_parallelbus_type)) { self->bus_reset = common_hal_paralleldisplaybus_parallelbus_reset; @@ -89,6 +91,7 @@ void displayio_display_bus_construct(displayio_display_bus_t *self, self->begin_transaction = common_hal_qspibus_qspibus_begin_transaction; self->send = common_hal_qspibus_qspibus_send; self->end_transaction = common_hal_qspibus_qspibus_end_transaction; + self->flush = common_hal_qspibus_qspibus_flush; self->collect_ptrs = common_hal_qspibus_qspibus_collect_ptrs; } else #endif @@ -252,11 +255,15 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d _displayio_display_bus_send_region_commands(self, display, area, true); } -#if CIRCUITPY_QSPIBUS void displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { _displayio_display_bus_send_region_commands(self, display, area, false); } -#endif + +void displayio_display_bus_flush(displayio_display_bus_t *self) { + if (self->flush != NULL) { + self->flush(self->bus); + } +} void displayio_display_bus_collect_ptrs(displayio_display_bus_t *self) { self->collect_ptrs(self->bus); diff --git a/shared-module/displayio/bus_core.h b/shared-module/displayio/bus_core.h index bd7e51aeb4561..163ccd2f1489e 100644 --- a/shared-module/displayio/bus_core.h +++ b/shared-module/displayio/bus_core.h @@ -21,6 +21,7 @@ typedef struct { display_bus_begin_transaction begin_transaction; display_bus_send send; display_bus_end_transaction end_transaction; + display_bus_flush flush; display_bus_collect_ptrs collect_ptrs; uint16_t ram_width; uint16_t ram_height; @@ -49,11 +50,13 @@ void displayio_display_bus_end_transaction(displayio_display_bus_t *self); void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area); -#if CIRCUITPY_QSPIBUS // Send column/row window commands within an already-open transaction. // Caller must have called displayio_display_bus_begin_transaction() first. void displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area); -#endif + +// Drain any pending asynchronous transfers on the bus. +// No-op for synchronous buses (FourWire, I2C, ParallelBus). +void displayio_display_bus_flush(displayio_display_bus_t *self); void release_display_bus(displayio_display_bus_t *self); diff --git a/shared-module/framebufferio/FramebufferDisplay.c b/shared-module/framebufferio/FramebufferDisplay.c index 8116f4b0347cb..b120afad4c5e1 100644 --- a/shared-module/framebufferio/FramebufferDisplay.c +++ b/shared-module/framebufferio/FramebufferDisplay.c @@ -117,7 +117,7 @@ static const displayio_area_t *_get_refresh_areas(framebufferio_framebufferdispl #define MARK_ROW_DIRTY(r) (dirty_row_bitmask[r / 8] |= (1 << (r & 7))) static bool _refresh_area(framebufferio_framebufferdisplay_obj_t *self, const displayio_area_t *area, uint8_t *dirty_row_bitmask) { - uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); // In uint32_ts + uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE; // In uint32_ts displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. From d2e9a584fc818af44b5004dc373f6b9533502a4e Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Tue, 3 Mar 2026 23:07:13 +0100 Subject: [PATCH 402/517] Fixes after the review --- ports/espressif/common-hal/qspibus/QSPIBus.c | 40 ++++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c index b4fabe914786e..6fb872248e2a6 100644 --- a/ports/espressif/common-hal/qspibus/QSPIBus.c +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -139,9 +139,6 @@ static void qspibus_send_color_bytes( qspibus_send_command_bytes(self, command, NULL, 0); return; } - if (self->dma_buffer_size == 0) { - mp_raise_OSError_msg(MP_ERROR_TEXT("Could not allocate DMA capable buffer")); - } // RAMWR must transition to RAMWRC for continued payload chunks. uint8_t chunk_command = command; @@ -290,8 +287,7 @@ void common_hal_qspibus_qspibus_construct( } if (!qspibus_allocate_dma_buffers(self)) { - vSemaphoreDelete(self->transfer_done_sem); - self->transfer_done_sem = NULL; + common_hal_qspibus_qspibus_deinit(self); mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); } @@ -307,12 +303,13 @@ void common_hal_qspibus_qspibus_construct( esp_err_t err = spi_bus_initialize(self->host_id, &bus_config, SPI_DMA_CH_AUTO); if (err != ESP_OK) { - qspibus_release_dma_buffers(self); - vSemaphoreDelete(self->transfer_done_sem); - self->transfer_done_sem = NULL; + common_hal_qspibus_qspibus_deinit(self); mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_SPI); } + // Mark bus as initialized so deinit knows to call spi_bus_free(). + self->bus_initialized = true; + const esp_lcd_panel_io_spi_config_t io_config = { .cs_gpio_num = self->cs_pin, .dc_gpio_num = -1, @@ -330,10 +327,7 @@ void common_hal_qspibus_qspibus_construct( err = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)self->host_id, &io_config, &self->io_handle); if (err != ESP_OK) { - spi_bus_free(self->host_id); - qspibus_release_dma_buffers(self); - vSemaphoreDelete(self->transfer_done_sem); - self->transfer_done_sem = NULL; + common_hal_qspibus_qspibus_deinit(self); mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_QSPI, (int)err); } @@ -358,26 +352,22 @@ void common_hal_qspibus_qspibus_construct( gpio_set_level((gpio_num_t)self->reset_pin, 1); vTaskDelay(pdMS_TO_TICKS(120)); } - - self->bus_initialized = true; } void common_hal_qspibus_qspibus_deinit(qspibus_qspibus_obj_t *self) { - if (!self->bus_initialized) { - qspibus_release_dma_buffers(self); - return; - } + if (self->bus_initialized) { + qspibus_panel_sleep_best_effort(self); + self->in_transaction = false; - qspibus_panel_sleep_best_effort(self); - self->in_transaction = false; + if (self->io_handle != NULL) { + esp_lcd_panel_io_del(self->io_handle); + self->io_handle = NULL; + } - if (self->io_handle != NULL) { - esp_lcd_panel_io_del(self->io_handle); - self->io_handle = NULL; + spi_bus_free(self->host_id); + self->bus_initialized = false; } - spi_bus_free(self->host_id); - if (self->transfer_done_sem != NULL) { // Set NULL before delete so late ISR callbacks (if any) see NULL and skip. SemaphoreHandle_t sem = self->transfer_done_sem; From 44072a57c6f235f5f6e615988558fa66e86883af Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Mar 2026 14:26:34 -0800 Subject: [PATCH 403/517] Add peripheral support and tests --- ports/zephyr-cp/common-hal/_bleio/Adapter.c | 6 + .../tests/bsim/test_bsim_ble_peripheral.py | 143 ++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.c b/ports/zephyr-cp/common-hal/_bleio/Adapter.c index 7f034cfc24d54..5ba562146c8e4 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.c +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -148,6 +148,12 @@ static void bleio_connected_cb(struct bt_conn *conn, uint8_t err) { return; } + // When connectable advertising results in a connection, the controller + // auto-stops advertising. Clear our flag to match (we cannot call + // stop_advertising() here because this callback runs in Zephyr's BT + // thread context). + ble_advertising = false; + common_hal_bleio_adapter_obj.connection_objs = NULL; } diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py new file mode 100644 index 0000000000000..f1db308bbfc40 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py @@ -0,0 +1,143 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE peripheral connection tests for nrf5340bsim.""" + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_PERIPHERAL_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"CPPERIPH" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("peripheral start") +adapter.start_advertising(advertisement, connectable=True) +print("advertising", adapter.advertising) + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) + +print("connected", adapter.connected, "advertising", adapter.advertising) + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) + +print("disconnected", adapter.connected, len(adapter.connections)) +""" + +BSIM_PERIPHERAL_CP_CENTRAL_PERIPHERAL_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"CPPERIPH" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("peripheral start") +adapter.start_advertising(advertisement, connectable=True) +print("advertising", adapter.advertising) + +for _ in range(80): + if adapter.connected: + break + time.sleep(0.1) + +print("connected", adapter.connected, "advertising", adapter.advertising) + +for _ in range(80): + if not adapter.connected: + break + time.sleep(0.1) + +print("disconnected", adapter.connected, len(adapter.connections)) +""" + +BSIM_PERIPHERAL_CP_CENTRAL_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("central start") +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPERIPH" in entry.advertisement_bytes: + target = entry.address + print("found peripheral") + break +adapter.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No connectable peripheral found") + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected, adapter.connected, len(adapter.connections)) +connection.disconnect() + +for _ in range(40): + if not connection.connected and not adapter.connected: + break + time.sleep(0.1) + +print("disconnected", connection.connected, adapter.connected, len(adapter.connections)) +""" + + +@pytest.mark.zephyr_sample("bluetooth/central") +@pytest.mark.duration(14) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CODE}) +def test_bsim_peripheral_zephyr_central(bsim_phy, circuitpython, zephyr_sample): + """Advertise as connectable from CP; Zephyr central connects and disconnects.""" + central = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + central_output = central.serial.all_output + + assert "peripheral start" in cp_output + assert "advertising True" in cp_output + assert "connected True advertising False" in cp_output + assert "disconnected False 0" in cp_output + + assert "Scanning successfully started" in central_output + assert "Connected:" in central_output + assert "Disconnected:" in central_output + + +@pytest.mark.duration(14) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CP_CENTRAL_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CP_CENTRAL_PERIPHERAL_CODE}) +def test_bsim_peripheral_cp_central(bsim_phy, circuitpython1, circuitpython2): + """Two CP instances: device 0 peripheral, device 1 central.""" + peripheral = circuitpython1 + central = circuitpython2 + + central.wait_until_done() + peripheral.wait_until_done() + + periph_output = peripheral.serial.all_output + central_output = central.serial.all_output + + assert "peripheral start" in periph_output + assert "advertising True" in periph_output + assert "connected True advertising False" in periph_output + assert "disconnected False 0" in periph_output + + assert "central start" in central_output + assert "found peripheral" in central_output + assert "have target True" in central_output + assert "connected True True 1" in central_output + assert "disconnected False False 0" in central_output From 907360e99cca00a3e36190f462fa8fb4bc5cbef0 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 3 Mar 2026 18:52:52 -0600 Subject: [PATCH 404/517] fix default_advance_width calculation, fix f_read(_,NULL) call, fix second slot of full width glyph overriding --- shared-module/lvfontio/OnDiskFont.c | 87 ++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/shared-module/lvfontio/OnDiskFont.c b/shared-module/lvfontio/OnDiskFont.c index 368bd381475b1..ade23280ed6d4 100644 --- a/shared-module/lvfontio/OnDiskFont.c +++ b/shared-module/lvfontio/OnDiskFont.c @@ -48,6 +48,7 @@ static inline void free_memory(lvfontio_ondiskfont_t *self, void *ptr) { // Forward declarations for helper functions static int16_t find_codepoint_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint); +static bool slot_has_active_full_width_partner(lvfontio_ondiskfont_t *self, uint16_t slot); static uint16_t find_free_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint); static FRESULT read_bits(FIL *file, size_t num_bits, uint8_t *byte_val, uint8_t *remaining_bits, uint32_t *result); static FRESULT read_glyph_dimensions(FIL *file, lvfontio_ondiskfont_t *self, uint32_t *advance_width, int32_t *bbox_x, int32_t *bbox_y, uint32_t *bbox_w, uint32_t *bbox_h, uint8_t *byte_val, uint8_t *remaining_bits); @@ -224,18 +225,24 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max // Throw away the bitmap bits. read_bits(file, self->header.bits_per_pixel * bbox_w * bbox_h, &byte_val, &remaining_bits, NULL); - if (advances[0] == glyph_advance) { - advance_count[0]++; - } else if (advances[1] == glyph_advance) { - advance_count[1]++; - } else if (advance_count[0] == 0) { - advances[0] = glyph_advance; - advance_count[0] = 1; - } else if (advance_count[1] == 0) { - advances[1] = glyph_advance; - advance_count[1] = 1; - } else { - break; + + // Ignore zero-advance glyphs when inferring the terminal cell width. + // Some fonts include placeholders/control glyphs with zero advance, + // which would otherwise skew default_advance_width too small. + if (glyph_advance > 0) { + if (advances[0] == glyph_advance) { + advance_count[0]++; + } else if (advances[1] == glyph_advance) { + advance_count[1]++; + } else if (advance_count[0] == 0) { + advances[0] = glyph_advance; + advance_count[0] = 1; + } else if (advance_count[1] == 0) { + advances[1] = glyph_advance; + advance_count[1] = 1; + } else { + break; + } } cid++; } @@ -257,6 +264,12 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max *max_slots = advance_count[0] + advance_count[1]; } + if (self->header.default_advance_width == 0) { + self->header.default_advance_width = 1; + } + if (*max_slots == 0) { + *max_slots = 1; + } found_glyf = true; } @@ -344,8 +357,9 @@ static int32_t get_char_id(lvfontio_ondiskfont_t *self, uint32_t codepoint) { for (size_t j = 0; j < self->cmap_ranges[i].entries_count; j++) { // Read code point at the index uint16_t candidate_codepoint_delta; - res = f_read(&self->file, &candidate_codepoint_delta, 2, NULL); - if (res != FR_OK) { + UINT bytes_read; + res = f_read(&self->file, &candidate_codepoint_delta, 2, &bytes_read); + if (res != FR_OK || bytes_read < 2) { return -1; } @@ -574,18 +588,18 @@ int16_t common_hal_lvfontio_ondiskfont_cache_glyph(lvfontio_ondiskfont_t *self, // Check if already cached int16_t existing_slot = find_codepoint_slot(self, codepoint); if (existing_slot >= 0) { - // Glyph is already cached, increment reference count + // Glyph is already cached, increment reference count(s). self->reference_counts[existing_slot]++; - // Check if this is a full-width character by looking for a second slot - // with the same codepoint right after this one + bool cached_is_full_width = existing_slot + 1 < self->max_glyphs && + self->codepoints[existing_slot + 1] == codepoint; + + if (cached_is_full_width) { + self->reference_counts[existing_slot + 1]++; + } + if (is_full_width != NULL) { - if (existing_slot + 1 < self->max_glyphs && - self->codepoints[existing_slot + 1] == codepoint) { - *is_full_width = true; - } else { - *is_full_width = false; - } + *is_full_width = cached_is_full_width; } return existing_slot; @@ -722,12 +736,34 @@ static int16_t find_codepoint_slot(lvfontio_ondiskfont_t *self, uint32_t codepoi for (uint16_t i = 0; i < self->max_glyphs; i++) { int16_t slot = (i + offset) % self->max_glyphs; if (self->codepoints[slot] == codepoint) { + // If this is the second slot of a full-width glyph pair, return the + // first slot so callers always get a canonical index. + if (slot > 0 && self->codepoints[slot - 1] == codepoint) { + return slot - 1; + } return slot; } } return -1; } +static bool slot_has_active_full_width_partner(lvfontio_ondiskfont_t *self, uint16_t slot) { + uint32_t codepoint = self->codepoints[slot]; + if (codepoint == LVFONTIO_INVALID_CODEPOINT) { + return false; + } + + // Don't evict one half of a full-width pair while the other half is still in use. + if (slot > 0 && self->codepoints[slot - 1] == codepoint && self->reference_counts[slot - 1] > 0) { + return true; + } + if (slot + 1 < self->max_glyphs && self->codepoints[slot + 1] == codepoint && self->reference_counts[slot + 1] > 0) { + return true; + } + + return false; +} + static uint16_t find_free_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint) { size_t offset = codepoint % self->max_glyphs; @@ -739,10 +775,11 @@ static uint16_t find_free_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint) } } - // If none found, look for slots with zero reference count, starting at the offset + // If none found, look for slots with zero reference count, starting at the offset. + // Avoid reusing one half of an active full-width glyph pair. for (uint16_t i = 0; i < self->max_glyphs; i++) { int16_t slot = (i + offset) % self->max_glyphs; - if (self->reference_counts[slot] == 0) { + if (self->reference_counts[slot] == 0 && !slot_has_active_full_width_partner(self, slot)) { return slot; } } From a2532bd5370b9c08da0f1a77bd0aebb934f67aaf Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 3 Mar 2026 19:10:39 -0600 Subject: [PATCH 405/517] return comment --- shared-module/lvfontio/OnDiskFont.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared-module/lvfontio/OnDiskFont.c b/shared-module/lvfontio/OnDiskFont.c index ade23280ed6d4..f6458346e938e 100644 --- a/shared-module/lvfontio/OnDiskFont.c +++ b/shared-module/lvfontio/OnDiskFont.c @@ -591,6 +591,8 @@ int16_t common_hal_lvfontio_ondiskfont_cache_glyph(lvfontio_ondiskfont_t *self, // Glyph is already cached, increment reference count(s). self->reference_counts[existing_slot]++; + // Check if this is a full-width character by looking for a second slot + // with the same codepoint right after this one bool cached_is_full_width = existing_slot + 1 < self->max_glyphs && self->codepoints[existing_slot + 1] == codepoint; From 69dedabf140b10d573a1066398d8902e4d15a55f Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Wed, 4 Mar 2026 19:42:56 +0100 Subject: [PATCH 406/517] Review fixes --- .../mpconfigboard.h | 4 +-- py/circuitpy_mpconfig.h | 6 ++-- shared-module/busdisplay/BusDisplay.c | 32 ++++--------------- shared-module/displayio/bus_core.c | 4 --- shared-module/displayio/bus_core.h | 4 --- .../framebufferio/FramebufferDisplay.c | 2 +- 6 files changed, 13 insertions(+), 39 deletions(-) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h index 242cca6b30dd4..9ac461ee61dde 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h @@ -11,9 +11,9 @@ #define CIRCUITPY_BOARD_I2C (0) #define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO48, .sda = &pin_GPIO47}} -// Display refresh buffer: 2048 uint32_t words = 8KB on stack. +// Display refresh buffer: 8192 bytes = 2048 uint32_t words on stack. // ESP32-S3 main task stack is 24KB; verified safe with this board. -#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (2048) +#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (8192) // AMOLED Display (displayio + qspibus path) - initialized in board_init() #define CIRCUITPY_BOARD_DISPLAY (1) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 7bad99769df67..3918296636afa 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -394,11 +394,11 @@ typedef long mp_off_t; #define CIRCUITPY_DISPLAY_LIMIT (1) #endif -// Display area buffer size in uint32_t words for _refresh_area() VLA. +// Display area buffer size in bytes for _refresh_area() VLA. // Allocated on stack; boards with larger displays can override per-board. -// Default 128 words = 512 bytes. +// Default 512 bytes = 128 uint32_t words. #ifndef CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE -#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (128) +#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (512) #endif #else diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index e83250740ba28..3626d2d2c3e6c 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -214,7 +214,7 @@ static void _send_pixels(busdisplay_busdisplay_obj_t *self, uint8_t *pixels, uin } static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_area_t *area) { - uint32_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE; // In uint32_ts + uint32_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); // In uint32_ts displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. @@ -262,8 +262,6 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are uint16_t remaining_rows = displayio_area_height(&clipped); - bool async_bus = (self->bus.flush != NULL); - for (uint16_t j = 0; j < subrectangles; j++) { displayio_area_t subrectangle = { .x1 = clipped.x1, @@ -286,31 +284,15 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are memset(mask, 0, mask_length * sizeof(mask[0])); memset(buffer, 0, buffer_size * sizeof(buffer[0])); - if (async_bus) { - // Async path: fill_area overlaps with previous DMA transfer. - // begin_transaction() waits for prior DMA to finish. - displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); - - if (!displayio_display_bus_begin_transaction(&self->bus)) { - return false; - } - displayio_display_bus_send_region_commands(&self->bus, &self->core, &subrectangle); - _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); - displayio_display_bus_end_transaction(&self->bus); - } else { - // Sync path: set region first, then fill and send. - displayio_display_bus_set_region_to_update(&self->bus, &self->core, &subrectangle); - - displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); + displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); - if (!displayio_display_bus_is_free(&self->bus)) { - return false; - } + displayio_display_bus_set_region_to_update(&self->bus, &self->core, &subrectangle); - displayio_display_bus_begin_transaction(&self->bus); - _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); - displayio_display_bus_end_transaction(&self->bus); + if (!displayio_display_bus_begin_transaction(&self->bus)) { + return false; } + _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); + displayio_display_bus_end_transaction(&self->bus); // Run background tasks so they can run during an explicit refresh. // Auto-refresh won't run background tasks here because it is a background task itself. diff --git a/shared-module/displayio/bus_core.c b/shared-module/displayio/bus_core.c index 7362120e65941..df6c5d36f4b2b 100644 --- a/shared-module/displayio/bus_core.c +++ b/shared-module/displayio/bus_core.c @@ -255,10 +255,6 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d _displayio_display_bus_send_region_commands(self, display, area, true); } -void displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { - _displayio_display_bus_send_region_commands(self, display, area, false); -} - void displayio_display_bus_flush(displayio_display_bus_t *self) { if (self->flush != NULL) { self->flush(self->bus); diff --git a/shared-module/displayio/bus_core.h b/shared-module/displayio/bus_core.h index 163ccd2f1489e..75d03d7f28430 100644 --- a/shared-module/displayio/bus_core.h +++ b/shared-module/displayio/bus_core.h @@ -50,10 +50,6 @@ void displayio_display_bus_end_transaction(displayio_display_bus_t *self); void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area); -// Send column/row window commands within an already-open transaction. -// Caller must have called displayio_display_bus_begin_transaction() first. -void displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area); - // Drain any pending asynchronous transfers on the bus. // No-op for synchronous buses (FourWire, I2C, ParallelBus). void displayio_display_bus_flush(displayio_display_bus_t *self); diff --git a/shared-module/framebufferio/FramebufferDisplay.c b/shared-module/framebufferio/FramebufferDisplay.c index b120afad4c5e1..8116f4b0347cb 100644 --- a/shared-module/framebufferio/FramebufferDisplay.c +++ b/shared-module/framebufferio/FramebufferDisplay.c @@ -117,7 +117,7 @@ static const displayio_area_t *_get_refresh_areas(framebufferio_framebufferdispl #define MARK_ROW_DIRTY(r) (dirty_row_bitmask[r / 8] |= (1 << (r & 7))) static bool _refresh_area(framebufferio_framebufferdisplay_obj_t *self, const displayio_area_t *area, uint8_t *dirty_row_bitmask) { - uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE; // In uint32_ts + uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); // In uint32_ts displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. From 7150f82c9d862db0b89f044f0192d629142bb840 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 4 Mar 2026 11:00:16 -0800 Subject: [PATCH 407/517] Address feedback --- .../ble_hci/common-hal/_bleio/Characteristic.c | 2 +- devices/ble_hci/common-hal/_bleio/Connection.h | 2 +- devices/ble_hci/common-hal/_bleio/__init__.c | 2 +- .../espressif/common-hal/_bleio/Characteristic.c | 2 +- ports/espressif/common-hal/_bleio/Connection.h | 2 +- ports/espressif/common-hal/_bleio/__init__.c | 2 +- ports/nordic/common-hal/_bleio/Attribute.h | 8 ++++---- ports/nordic/common-hal/_bleio/Characteristic.c | 10 +++++----- ports/nordic/common-hal/_bleio/Connection.h | 2 +- ports/nordic/common-hal/_bleio/Descriptor.c | 8 ++++---- ports/nordic/common-hal/_bleio/__init__.c | 16 ++++++++-------- ports/silabs/common-hal/_bleio/Characteristic.c | 2 +- ports/silabs/common-hal/_bleio/Connection.h | 2 +- ports/silabs/common-hal/_bleio/__init__.c | 2 +- shared-bindings/_bleio/Characteristic.c | 3 ++- shared-bindings/_bleio/Descriptor.c | 3 ++- 16 files changed, 35 insertions(+), 33 deletions(-) diff --git a/devices/ble_hci/common-hal/_bleio/Characteristic.c b/devices/ble_hci/common-hal/_bleio/Characteristic.c index a33b8ff478472..49b66d38e7256 100644 --- a/devices/ble_hci/common-hal/_bleio/Characteristic.c +++ b/devices/ble_hci/common-hal/_bleio/Characteristic.c @@ -180,7 +180,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, } const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); - common_hal_bleio_check_connected(conn_handle); + bleio_check_connected(conn_handle); uint16_t cccd_value = (notify ? CCCD_NOTIFY : 0) | diff --git a/devices/ble_hci/common-hal/_bleio/Connection.h b/devices/ble_hci/common-hal/_bleio/Connection.h index fa9f4c7103e7d..02a000501bfde 100644 --- a/devices/ble_hci/common-hal/_bleio/Connection.h +++ b/devices/ble_hci/common-hal/_bleio/Connection.h @@ -61,7 +61,7 @@ typedef struct { uint8_t disconnect_reason; } bleio_connection_obj_t; -void common_hal_bleio_check_connected(uint16_t conn_handle); +void bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/devices/ble_hci/common-hal/_bleio/__init__.c b/devices/ble_hci/common-hal/_bleio/__init__.c index 6376f6f10c8c9..f9fdbc50f6489 100644 --- a/devices/ble_hci/common-hal/_bleio/__init__.c +++ b/devices/ble_hci/common-hal/_bleio/__init__.c @@ -84,7 +84,7 @@ bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void) { return &common_hal_bleio_adapter_obj; } -void common_hal_bleio_check_connected(uint16_t conn_handle) { +void bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLE_CONN_HANDLE_INVALID) { mp_raise_ConnectionError(MP_ERROR_TEXT("Not connected")); } diff --git a/ports/espressif/common-hal/_bleio/Characteristic.c b/ports/espressif/common-hal/_bleio/Characteristic.c index 805c6d160f325..736c61c650eb4 100644 --- a/ports/espressif/common-hal/_bleio/Characteristic.c +++ b/ports/espressif/common-hal/_bleio/Characteristic.c @@ -320,7 +320,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, } const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); - common_hal_bleio_check_connected(conn_handle); + bleio_check_connected(conn_handle); uint16_t cccd_value = (notify ? 1 << 0 : 0) | diff --git a/ports/espressif/common-hal/_bleio/Connection.h b/ports/espressif/common-hal/_bleio/Connection.h index bb48782e84f74..5f33eb43b5df9 100644 --- a/ports/espressif/common-hal/_bleio/Connection.h +++ b/ports/espressif/common-hal/_bleio/Connection.h @@ -64,7 +64,7 @@ void bleio_connection_clear(bleio_connection_internal_t *self); int bleio_connection_event_cb(struct ble_gap_event *event, void *connection_in); -void common_hal_bleio_check_connected(uint16_t conn_handle); +void bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/ports/espressif/common-hal/_bleio/__init__.c b/ports/espressif/common-hal/_bleio/__init__.c index 119fe55921e8a..4fdd0a48a2000 100644 --- a/ports/espressif/common-hal/_bleio/__init__.c +++ b/ports/espressif/common-hal/_bleio/__init__.c @@ -155,7 +155,7 @@ void check_notify(BaseType_t result) { mp_raise_msg(&mp_type_TimeoutError, NULL); } -void common_hal_bleio_check_connected(uint16_t conn_handle) { +void bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLEIO_HANDLE_INVALID) { mp_raise_ConnectionError(MP_ERROR_TEXT("Not connected")); } diff --git a/ports/nordic/common-hal/_bleio/Attribute.h b/ports/nordic/common-hal/_bleio/Attribute.h index c9438571a86d0..9a58e16bb862e 100644 --- a/ports/nordic/common-hal/_bleio/Attribute.h +++ b/ports/nordic/common-hal/_bleio/Attribute.h @@ -14,7 +14,7 @@ extern void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode); -size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); -void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); -size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); -void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); +size_t bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); +size_t bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); diff --git a/ports/nordic/common-hal/_bleio/Characteristic.c b/ports/nordic/common-hal/_bleio/Characteristic.c index 51335be9e59be..2e6042e48e7fb 100644 --- a/ports/nordic/common-hal/_bleio/Characteristic.c +++ b/ports/nordic/common-hal/_bleio/Characteristic.c @@ -138,10 +138,10 @@ size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *sel if (self->handle != BLE_GATT_HANDLE_INVALID) { uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); if (common_hal_bleio_service_get_is_remote(self->service)) { - return common_hal_bleio_gattc_read(self->handle, conn_handle, buf, len); + return bleio_gattc_read(self->handle, conn_handle, buf, len); } else { // conn_handle is ignored for non-system attributes. - return common_hal_bleio_gatts_read(self->handle, conn_handle, buf, len); + return bleio_gatts_read(self->handle, conn_handle, buf, len); } } @@ -159,7 +159,7 @@ void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, if (common_hal_bleio_service_get_is_remote(self->service)) { uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); // Last argument is true if write-no-reponse desired. - common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, + bleio_gattc_write(self->handle, conn_handle, bufinfo, (self->props & CHAR_PROP_WRITE_NO_RESPONSE)); } else { // Validate data length for local characteristics only. @@ -172,7 +172,7 @@ void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, // Always write the value locally even if no connections are active. // conn_handle is ignored for non-system attributes, so we use BLE_CONN_HANDLE_INVALID. - common_hal_bleio_gatts_write(self->handle, BLE_CONN_HANDLE_INVALID, bufinfo); + bleio_gatts_write(self->handle, BLE_CONN_HANDLE_INVALID, bufinfo); // Check to see if we need to notify or indicate any active connections. for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { bleio_connection_internal_t *connection = &bleio_connections[i]; @@ -255,7 +255,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, } const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); - common_hal_bleio_check_connected(conn_handle); + bleio_check_connected(conn_handle); uint16_t cccd_value = (notify ? BLE_GATT_HVX_NOTIFICATION : 0) | diff --git a/ports/nordic/common-hal/_bleio/Connection.h b/ports/nordic/common-hal/_bleio/Connection.h index 87e0e9856a4d4..ea1edf1760311 100644 --- a/ports/nordic/common-hal/_bleio/Connection.h +++ b/ports/nordic/common-hal/_bleio/Connection.h @@ -65,7 +65,7 @@ typedef struct { void bleio_connection_clear(bleio_connection_internal_t *self); bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in); -void common_hal_bleio_check_connected(uint16_t conn_handle); +void bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/ports/nordic/common-hal/_bleio/Descriptor.c b/ports/nordic/common-hal/_bleio/Descriptor.c index 959c8e5c9c0c0..0d27ca5dc2edf 100644 --- a/ports/nordic/common-hal/_bleio/Descriptor.c +++ b/ports/nordic/common-hal/_bleio/Descriptor.c @@ -43,9 +43,9 @@ size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8 if (self->handle != BLE_GATT_HANDLE_INVALID) { uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection); if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { - return common_hal_bleio_gattc_read(self->handle, conn_handle, buf, len); + return bleio_gattc_read(self->handle, conn_handle, buf, len); } else { - return common_hal_bleio_gatts_read(self->handle, conn_handle, buf, len); + return bleio_gatts_read(self->handle, conn_handle, buf, len); } } @@ -58,7 +58,7 @@ void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buff uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection); if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { // false means WRITE_REQ, not write-no-response - common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false); + bleio_gattc_write(self->handle, conn_handle, bufinfo, false); } else { // Validate data length for local descriptors only. if (self->fixed_length && bufinfo->len != self->max_length) { @@ -68,7 +68,7 @@ void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buff mp_raise_ValueError(MP_ERROR_TEXT("Value length > max_length")); } - common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); + bleio_gatts_write(self->handle, conn_handle, bufinfo); } } diff --git a/ports/nordic/common-hal/_bleio/__init__.c b/ports/nordic/common-hal/_bleio/__init__.c index 454937dcd354a..9dc58d7687c90 100644 --- a/ports/nordic/common-hal/_bleio/__init__.c +++ b/ports/nordic/common-hal/_bleio/__init__.c @@ -112,14 +112,14 @@ void bleio_reset(void) { // It currently only has properties and no state. Inited by bleio_reset bleio_adapter_obj_t common_hal_bleio_adapter_obj; -void common_hal_bleio_check_connected(uint16_t conn_handle) { +void bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLE_CONN_HANDLE_INVALID) { mp_raise_ConnectionError(MP_ERROR_TEXT("Not connected")); } } // GATTS read of a Characteristic or Descriptor. -size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { +size_t bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { // conn_handle is ignored unless this is a system attribute. // If we're not connected, that's OK, because we can still read and write the local value. @@ -133,7 +133,7 @@ size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_ return gatts_value.len; } -void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { +void bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { // conn_handle is ignored unless this is a system attribute. // If we're not connected, that's OK, because we can still read and write the local value. @@ -188,8 +188,8 @@ static bool _on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) { return true; } -size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { - common_hal_bleio_check_connected(conn_handle); +size_t bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { + bleio_check_connected(conn_handle); read_info_t read_info; read_info.buf = buf; @@ -213,15 +213,15 @@ size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_ RUN_BACKGROUND_TASKS; } // Test if we were disconnected while reading - common_hal_bleio_check_connected(read_info.conn_handle); + bleio_check_connected(read_info.conn_handle); ble_drv_remove_event_handler(_on_gattc_read_rsp_evt, &read_info); check_gatt_status(read_info.status); return read_info.final_len; } -void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { - common_hal_bleio_check_connected(conn_handle); +void bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { + bleio_check_connected(conn_handle); ble_gattc_write_params_t write_params = { .write_op = write_no_response ? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ, diff --git a/ports/silabs/common-hal/_bleio/Characteristic.c b/ports/silabs/common-hal/_bleio/Characteristic.c index a707b0ef1acc3..9ccc6ad9c364e 100644 --- a/ports/silabs/common-hal/_bleio/Characteristic.c +++ b/ports/silabs/common-hal/_bleio/Characteristic.c @@ -395,7 +395,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, const uint16_t conn_handle = bleio_connection_get_conn_handle( self->service->connection); - common_hal_bleio_check_connected(conn_handle); + bleio_check_connected(conn_handle); notify = 1; indicate = 0; if (notify) { diff --git a/ports/silabs/common-hal/_bleio/Connection.h b/ports/silabs/common-hal/_bleio/Connection.h index 23976d3c84924..148caecba1101 100644 --- a/ports/silabs/common-hal/_bleio/Connection.h +++ b/ports/silabs/common-hal/_bleio/Connection.h @@ -85,7 +85,7 @@ typedef struct void bleio_connection_clear(bleio_connection_internal_t *self); -void common_hal_bleio_check_connected(uint16_t conn_handle); +void bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal( diff --git a/ports/silabs/common-hal/_bleio/__init__.c b/ports/silabs/common-hal/_bleio/__init__.c index c6ae8a0b606c8..4c5ce1f045d72 100644 --- a/ports/silabs/common-hal/_bleio/__init__.c +++ b/ports/silabs/common-hal/_bleio/__init__.c @@ -102,7 +102,7 @@ void check_ble_error(int error_code) { } } -void common_hal_bleio_check_connected(uint16_t conn_handle) { +void bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLEIO_HANDLE_INVALID) { mp_raise_ConnectionError(MP_ERROR_TEXT("Not connected")); } diff --git a/shared-bindings/_bleio/Characteristic.c b/shared-bindings/_bleio/Characteristic.c index b017295b45fad..0ed4c2b7eaa97 100644 --- a/shared-bindings/_bleio/Characteristic.c +++ b/shared-bindings/_bleio/Characteristic.c @@ -116,9 +116,10 @@ static mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ } mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - mp_arg_validate_length_max(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); if (fixed_length) { mp_arg_validate_length(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); + } else { + mp_arg_validate_length_max(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); } const char *user_description = NULL; diff --git a/shared-bindings/_bleio/Descriptor.c b/shared-bindings/_bleio/Descriptor.c index 8e721ff292d45..9e29f57d8dd2b 100644 --- a/shared-bindings/_bleio/Descriptor.c +++ b/shared-bindings/_bleio/Descriptor.c @@ -99,9 +99,10 @@ static mp_obj_t bleio_descriptor_add_to_characteristic(size_t n_args, const mp_o } } mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - mp_arg_validate_length_max(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); if (fixed_length) { mp_arg_validate_length(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); + } else { + mp_arg_validate_length_max(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); } bleio_descriptor_obj_t *descriptor = mp_obj_malloc(bleio_descriptor_obj_t, &bleio_descriptor_type); From f4f77e4e2832f29cd1ddaba0826a403789da05e6 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Wed, 4 Mar 2026 16:06:51 -0600 Subject: [PATCH 408/517] fix wrapping issue on glyph slots, refactor glyph_advance check. --- shared-module/lvfontio/OnDiskFont.c | 48 +++++++++++++++-------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/shared-module/lvfontio/OnDiskFont.c b/shared-module/lvfontio/OnDiskFont.c index f6458346e938e..efffed6307443 100644 --- a/shared-module/lvfontio/OnDiskFont.c +++ b/shared-module/lvfontio/OnDiskFont.c @@ -226,23 +226,22 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max // Throw away the bitmap bits. read_bits(file, self->header.bits_per_pixel * bbox_w * bbox_h, &byte_val, &remaining_bits, NULL); - // Ignore zero-advance glyphs when inferring the terminal cell width. - // Some fonts include placeholders/control glyphs with zero advance, - // which would otherwise skew default_advance_width too small. - if (glyph_advance > 0) { - if (advances[0] == glyph_advance) { - advance_count[0]++; - } else if (advances[1] == glyph_advance) { - advance_count[1]++; - } else if (advance_count[0] == 0) { - advances[0] = glyph_advance; - advance_count[0] = 1; - } else if (advance_count[1] == 0) { - advances[1] = glyph_advance; - advance_count[1] = 1; - } else { - break; - } + if (glyph_advance == 0) { + // Ignore zero-advance glyphs when inferring the terminal cell width. + // Some fonts include placeholders/control glyphs with zero advance, + // which would otherwise skew default_advance_width too small. + } else if (advances[0] == glyph_advance) { + advance_count[0]++; + } else if (advances[1] == glyph_advance) { + advance_count[1]++; + } else if (advance_count[0] == 0) { + advances[0] = glyph_advance; + advance_count[0] = 1; + } else if (advance_count[1] == 0) { + advances[1] = glyph_advance; + advance_count[1] = 1; + } else { + break; } cid++; } @@ -592,12 +591,12 @@ int16_t common_hal_lvfontio_ondiskfont_cache_glyph(lvfontio_ondiskfont_t *self, self->reference_counts[existing_slot]++; // Check if this is a full-width character by looking for a second slot - // with the same codepoint right after this one - bool cached_is_full_width = existing_slot + 1 < self->max_glyphs && - self->codepoints[existing_slot + 1] == codepoint; + // with the same codepoint right after this one, wrapping at the end. + uint16_t next_slot = (existing_slot + 1) % self->max_glyphs; + bool cached_is_full_width = self->codepoints[next_slot] == codepoint; if (cached_is_full_width) { - self->reference_counts[existing_slot + 1]++; + self->reference_counts[next_slot]++; } if (is_full_width != NULL) { @@ -756,10 +755,13 @@ static bool slot_has_active_full_width_partner(lvfontio_ondiskfont_t *self, uint } // Don't evict one half of a full-width pair while the other half is still in use. - if (slot > 0 && self->codepoints[slot - 1] == codepoint && self->reference_counts[slot - 1] > 0) { + uint16_t prev_slot = (slot + self->max_glyphs - 1) % self->max_glyphs; + uint16_t next_slot = (slot + 1) % self->max_glyphs; + + if (self->codepoints[prev_slot] == codepoint && self->reference_counts[prev_slot] > 0) { return true; } - if (slot + 1 < self->max_glyphs && self->codepoints[slot + 1] == codepoint && self->reference_counts[slot + 1] > 0) { + if (self->codepoints[next_slot] == codepoint && self->reference_counts[next_slot] > 0) { return true; } From 860ab98e85195112c288ce2d7dfbdc4de36c704e Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 3 Feb 2026 13:11:37 -0800 Subject: [PATCH 409/517] Add heap stat tracking to native_sim This adds two tracks "Outer Heap Used" and "VM Heap Used" to the output perfetto trace. --- ports/zephyr-cp/supervisor/port.c | 95 ++++++++++++++++++++++++-- ports/zephyr-cp/zephyr-config/west.yml | 2 +- py/gc.c | 48 +++++++++++++ 3 files changed, 138 insertions(+), 7 deletions(-) diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 08a84043e8eeb..8b73169d8a0db 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -16,6 +16,7 @@ #include #include #include +#include #if defined(CONFIG_ARCH_POSIX) #include @@ -29,7 +30,16 @@ #include "lib/tlsf/tlsf.h" #include +#if defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) +#include "perfetto_encoder.h" +#include +#define CIRCUITPY_PERFETTO_TRACK_GROUP_UUID 0x3000ULL +#define CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID 0x3001ULL +#define CIRCUITPY_PERFETTO_OUTER_HEAP_USED_UUID 0x3002ULL +#endif + static tlsf_t heap; +static size_t tlsf_heap_used = 0; // Auto generated in pins.c extern const struct device *const rams[]; @@ -72,12 +82,60 @@ static void native_sim_register_cmdline_opts(void) { NATIVE_TASK(native_sim_register_cmdline_opts, PRE_BOOT_1, 0); #endif +#if defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) +static bool perfetto_circuitpython_tracks_emitted; + +static void perfetto_emit_outer_heap_stats(void) { + if (!perfetto_start()) { + return; + } + size_t total = tlsf_heap_used; + #if defined(CONFIG_COMMON_LIBC_MALLOC) && defined(CONFIG_SYS_HEAP_RUNTIME_STATS) + extern int malloc_runtime_stats_get(struct sys_memory_stats *stats); + struct sys_memory_stats stats; + if (malloc_runtime_stats_get(&stats) == 0) { + total += stats.allocated_bytes; + } + #endif + perfetto_emit_counter(CIRCUITPY_PERFETTO_OUTER_HEAP_USED_UUID, (int64_t)total); + Z_SPIN_DELAY(1); +} + +static void perfetto_emit_circuitpython_tracks(void) { + if (perfetto_circuitpython_tracks_emitted) { + return; + } + if (!perfetto_start()) { + return; + } + perfetto_emit_track_descriptor(CIRCUITPY_PERFETTO_TRACK_GROUP_UUID, + perfetto_get_process_uuid(), + "CircuitPython"); + perfetto_emit_counter_track_descriptor(CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID, + CIRCUITPY_PERFETTO_TRACK_GROUP_UUID, + "VM Heap Used", + PERFETTO_COUNTER_UNIT_BYTES); + perfetto_emit_counter_track_descriptor(CIRCUITPY_PERFETTO_OUTER_HEAP_USED_UUID, + CIRCUITPY_PERFETTO_TRACK_GROUP_UUID, + "Outer Heap Used", + PERFETTO_COUNTER_UNIT_BYTES); + perfetto_circuitpython_tracks_emitted = true; +} +#else +static inline void perfetto_emit_outer_heap_stats(void) { +} + +static inline void perfetto_emit_circuitpython_tracks(void) { +} +#endif + static void _tick_function(struct k_timer *timer_id) { supervisor_tick(); } safe_mode_t port_init(void) { k_timer_init(&tick_timer, _tick_function, NULL); + perfetto_emit_circuitpython_tracks(); return SAFE_MODE_NONE; } @@ -233,6 +291,7 @@ void port_heap_init(void) { } valid_pool_count++; } + perfetto_emit_outer_heap_stats(); #if !DT_HAS_CHOSEN(zephyr_sram) #error "No SRAM!" #endif @@ -243,30 +302,54 @@ void *port_malloc(size_t size, bool dma_capable) { if (valid_pool_count > 0) { block = tlsf_malloc(heap, size); } + if (block != NULL) { + tlsf_heap_used += tlsf_block_size(block); + } #ifdef CONFIG_COMMON_LIBC_MALLOC if (block == NULL) { block = malloc(size); } #endif + if (block != NULL) { + perfetto_emit_outer_heap_stats(); + } return block; } void port_free(void *ptr) { + if (ptr == NULL) { + return; + } if (valid_pool_count > 0 && !(ptr >= zephyr_malloc_bottom && ptr < zephyr_malloc_top)) { + tlsf_heap_used -= tlsf_block_size(ptr); tlsf_free(heap, ptr); - return; + } else { + #ifdef CONFIG_COMMON_LIBC_MALLOC + free(ptr); + #endif } - #ifdef CONFIG_COMMON_LIBC_MALLOC - free(ptr); - #endif + perfetto_emit_outer_heap_stats(); } void *port_realloc(void *ptr, size_t size, bool dma_capable) { + if (ptr == NULL) { + return port_malloc(size, dma_capable); + } if (valid_pool_count > 0 && !(ptr >= zephyr_malloc_bottom && ptr < zephyr_malloc_top)) { - return tlsf_realloc(heap, ptr, size); + size_t old_size = tlsf_block_size(ptr); + void *new_block = tlsf_realloc(heap, ptr, size); + if (new_block != NULL) { + tlsf_heap_used = tlsf_heap_used - old_size + tlsf_block_size(new_block); + perfetto_emit_outer_heap_stats(); + } + return new_block; } #ifdef CONFIG_COMMON_LIBC_MALLOC - return realloc(ptr, size); + void *new_block = realloc(ptr, size); + if (new_block != NULL) { + perfetto_emit_outer_heap_stats(); + } + return new_block; #endif return NULL; } diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index bb5c9b4942b35..17a68bf8be79a 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -8,6 +8,6 @@ manifest: path: modules/bsim_hw_models/nrf_hw_models - name: zephyr url: https://github.com/adafruit/zephyr - revision: 5351284ac926b1352ab98f5ae692a21f38068beb + revision: 589b2139926017d4d98724bac653ceb30802be9f clone-depth: 100 import: true diff --git a/py/gc.c b/py/gc.c index c6da81d495c10..fc7de6c4d3b4f 100644 --- a/py/gc.c +++ b/py/gc.c @@ -32,6 +32,12 @@ #include "py/gc.h" #include "py/runtime.h" +#if defined(__ZEPHYR__) +#include +#include +#include +#endif + #if MICROPY_DEBUG_VALGRIND #include #endif @@ -45,6 +51,12 @@ #include "shared-module/memorymonitor/__init__.h" #endif +#if defined(__ZEPHYR__) && defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) +#include "perfetto_encoder.h" +#define CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID 0x3001ULL +#define CIRCUITPY_PERFETTO_VM_HEAP_MAX_FREE_UUID 0x3002ULL +#endif + #if MICROPY_ENABLE_GC #if MICROPY_DEBUG_VERBOSE // print debugging info @@ -158,6 +170,32 @@ void __attribute__ ((noinline)) gc_log_change(uint32_t start_block, uint32_t len #pragma GCC pop_options #endif +#if defined(__ZEPHYR__) && defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) +static void gc_perfetto_emit_heap_stats(void) { + if (!perfetto_start()) { + return; + } + gc_info_t info; + gc_info(&info); + perfetto_emit_counter(CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID, (int64_t)info.used); + Z_SPIN_DELAY(1); +} + +static void gc_perfetto_emit_heap_stopped(void) { + if (!perfetto_start()) { + return; + } + perfetto_emit_counter(CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID, 0); + Z_SPIN_DELAY(1); +} +#else +static inline void gc_perfetto_emit_heap_stats(void) { +} + +static inline void gc_perfetto_emit_heap_stopped(void) { +} +#endif + // Static functions for individual steps of the GC mark/sweep sequence static void gc_collect_start_common(void); static void *gc_get_ptr(void **ptrs, int i); @@ -284,6 +322,7 @@ void gc_init(void *start, void *end) { #endif GC_MUTEX_INIT(); + gc_perfetto_emit_heap_stats(); } #if MICROPY_GC_SPLIT_HEAP @@ -425,6 +464,7 @@ void gc_deinit(void) { // Run any finalisers before we stop using the heap. This will also free // any additional heap areas (but not the first.) gc_sweep_all(); + gc_perfetto_emit_heap_stopped(); memset(&MP_STATE_MEM(area), 0, sizeof(MP_STATE_MEM(area))); } @@ -654,6 +694,7 @@ void gc_collect_end(void) { } MP_STATE_THREAD(gc_lock_depth) &= ~GC_COLLECT_FLAG; GC_EXIT(); + gc_perfetto_emit_heap_stats(); } static void gc_deal_with_stack_overflow(void) { @@ -1069,6 +1110,8 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { memorymonitor_track_allocation(end_block - start_block + 1); #endif + gc_perfetto_emit_heap_stats(); + return ret_ptr; } @@ -1150,6 +1193,7 @@ void gc_free(void *ptr) { } while (ATB_GET_KIND(area, block) == AT_TAIL); GC_EXIT(); + gc_perfetto_emit_heap_stats(); #if EXTENSIVE_HEAP_PROFILING gc_dump_alloc_table(&mp_plat_print); @@ -1290,6 +1334,8 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { memorymonitor_track_allocation(new_blocks); #endif + gc_perfetto_emit_heap_stats(); + return ptr_in; } @@ -1327,6 +1373,8 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { memorymonitor_track_allocation(new_blocks); #endif + gc_perfetto_emit_heap_stats(); + return ptr_in; } From fdab0971ed4712bb5fe14f2a7acd9297c8d724e6 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 4 Mar 2026 16:31:14 -0800 Subject: [PATCH 410/517] Tweak peripheral tests --- .../boards/nrf5340bsim_nrf5340_cpuapp.conf | 2 + ports/zephyr-cp/common-hal/_bleio/Adapter.c | 47 ++++++++++- ports/zephyr-cp/tests/bsim/conftest.py | 8 +- .../tests/bsim/test_bsim_ble_advertising.py | 82 +++++++++++++++++++ .../tests/bsim/test_bsim_ble_peripheral.py | 61 +++++--------- 5 files changed, 152 insertions(+), 48 deletions(-) diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf index d996ec05ac9a6..57628a61e2057 100644 --- a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf @@ -12,7 +12,9 @@ CONFIG_BT_OBSERVER=y CONFIG_BT_BROADCASTER=y CONFIG_BT_L2CAP_TX_MTU=253 +CONFIG_BT_BUF_CMD_TX_COUNT=2 CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_HCI_VS=y CONFIG_BT_BUF_EVT_RX_COUNT=16 CONFIG_BT_BUF_EVT_RX_SIZE=255 CONFIG_BT_BUF_ACL_TX_COUNT=3 diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.c b/ports/zephyr-cp/common-hal/_bleio/Adapter.c index 5ba562146c8e4..d1410f02e1b12 100644 --- a/ports/zephyr-cp/common-hal/_bleio/Adapter.c +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "py/gc.h" #include "py/runtime.h" @@ -278,11 +279,48 @@ bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self) { } mp_int_t common_hal_bleio_adapter_get_tx_power(bleio_adapter_obj_t *self) { - mp_raise_NotImplementedError(NULL); + struct bt_hci_cp_vs_read_tx_power_level *cp; + struct bt_hci_rp_vs_read_tx_power_level *rp; + struct net_buf *buf, *rsp = NULL; + + buf = bt_hci_cmd_alloc(K_MSEC(1000)); + if (!buf) { + mp_raise_msg(&mp_type_MemoryError, NULL); + } + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle_type = BT_HCI_VS_LL_HANDLE_TYPE_ADV; + cp->handle = 0; + + int err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_TX_POWER_LEVEL, buf, &rsp); + if (err) { + raise_zephyr_error(err); + } + + rp = (void *)rsp->data; + int8_t power = rp->tx_power_level; + net_buf_unref(rsp); + return power; } void common_hal_bleio_adapter_set_tx_power(bleio_adapter_obj_t *self, mp_int_t tx_power) { - mp_raise_NotImplementedError(NULL); + struct bt_hci_cp_vs_write_tx_power_level *cp; + struct net_buf *buf, *rsp = NULL; + + buf = bt_hci_cmd_alloc(K_MSEC(3000)); + if (!buf) { + mp_raise_msg(&mp_type_MemoryError, NULL); + } + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle_type = BT_HCI_VS_LL_HANDLE_TYPE_ADV; + cp->handle = 0; + cp->tx_power_level = (int8_t)tx_power; + + int err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL, buf, &rsp); + if (err) { + raise_zephyr_error(err); + } + + net_buf_unref(rsp); } bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self) { @@ -321,7 +359,6 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, mp_buffer_info_t *advertising_data_bufinfo, mp_buffer_info_t *scan_response_data_bufinfo, mp_int_t tx_power, const bleio_address_obj_t *directed_to) { - (void)tx_power; (void)directed_to; (void)interval; @@ -391,6 +428,8 @@ void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, NULL); } + common_hal_bleio_adapter_set_tx_power(self, tx_power); + raise_zephyr_error(bt_le_adv_start(&adv_params, adv_data, adv_count, @@ -555,7 +594,7 @@ mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_addre if (info.state == BT_CONN_STATE_DISCONNECTED) { bt_conn_unref(conn); - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Failed to connect")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Failed to connect: timeout")); } } else if (err != -ENOTCONN) { bt_conn_unref(conn); diff --git a/ports/zephyr-cp/tests/bsim/conftest.py b/ports/zephyr-cp/tests/bsim/conftest.py index 67b76469fa030..493f4c92b3ba0 100644 --- a/ports/zephyr-cp/tests/bsim/conftest.py +++ b/ports/zephyr-cp/tests/bsim/conftest.py @@ -107,6 +107,8 @@ def bsim_phy(request, bsim_phy_binary, native_sim_env, sim_id): "-v=9", # Cleaning up level is on 9. Connecting is 7. f"-s={sim_id}", f"-D={devices}", + "-argschannel", + "-at=40", # 40 dB attenuation (default 60) so RSSI ~ -40 dBm ] print("Running:", " ".join(cmd)) proc = subprocess.Popen( @@ -213,11 +215,13 @@ def zephyr_sample(request, bsim_phy, native_sim_env, sim_id): print(sample_proc.serial.all_output) +# pytest markers are defined inside out meaning the bottom one is first in the +# list and the top is last. So use negative indices to reverse them. @pytest.fixture def circuitpython1(circuitpython): - return circuitpython[0] + return circuitpython[-1] @pytest.fixture def circuitpython2(circuitpython): - return circuitpython[1] + return circuitpython[-2] diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py index 35d85b416d627..33680fe2506f5 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py @@ -4,6 +4,7 @@ """BLE advertising tests for nrf5340bsim.""" import logging +import re import pytest @@ -44,6 +45,39 @@ """ +BSIM_TX_POWER_DEFAULT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"CPTXPWR" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("advertising default") +adapter.start_advertising(advertisement) +time.sleep(4) +adapter.stop_advertising() +print("done") +""" + +BSIM_TX_POWER_LOW_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"CPTXPWR" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("advertising low") +adapter.start_advertising(advertisement, tx_power=-20) +time.sleep(4) +adapter.stop_advertising() +print("done") +""" + + @pytest.mark.zephyr_sample("bluetooth/observer") @pytest.mark.circuitpy_drive({"code.py": BSIM_ADV_CODE}) def test_bsim_advertise_and_scan(bsim_phy, circuitpython, zephyr_sample): @@ -90,3 +124,51 @@ def test_bsim_advertise_ctrl_c_reload(bsim_phy, circuitpython, zephyr_sample): assert cp_output.count("adv run done") >= 1 assert observer_output.count("Device found:") >= observer_count_before + 1 assert "Already advertising" not in cp_output + + +@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.circuitpy_drive({"code.py": BSIM_TX_POWER_DEFAULT_CODE}) +def test_bsim_tx_power_default_rssi(bsim_phy, circuitpython, zephyr_sample): + """Verify default TX power produces expected RSSI.""" + observer = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + obs_output = observer.serial.all_output + + assert "advertising default" in cp_output + assert "done" in cp_output + + # Observer: "Device found: (RSSI ), type , AD data len " + # Advertisement is 12 bytes: flags (3) + name (9). + # With 40 dB channel attenuation and 0 dBm TX → RSSI ~ -39 + rssi_pattern = re.compile(r"RSSI (-?\d+)\), type \d+, AD data len 12") + all_rssi = [int(m.group(1)) for m in rssi_pattern.finditer(obs_output)] + logger.info("RSSI values: %s", all_rssi) + + assert len(all_rssi) > 0, "Observer saw no advertisements" + assert all_rssi[0] == -39, f"Expected RSSI -39 (0 dBm TX), got {all_rssi[0]}" + + +@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.circuitpy_drive({"code.py": BSIM_TX_POWER_LOW_CODE}) +def test_bsim_tx_power_low_rssi(bsim_phy, circuitpython, zephyr_sample): + """Verify low TX power reduces RSSI.""" + observer = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + obs_output = observer.serial.all_output + + assert "advertising low" in cp_output + assert "done" in cp_output + + # With 40 dB channel attenuation and -20 dBm TX → RSSI ~ -59 + rssi_pattern = re.compile(r"RSSI (-?\d+)\), type \d+, AD data len 12") + all_rssi = [int(m.group(1)) for m in rssi_pattern.finditer(obs_output)] + logger.info("RSSI values: %s", all_rssi) + + assert len(all_rssi) > 0, "Observer saw no advertisements" + assert all_rssi[0] < -39, f"Expected lower RSSI with -20 dBm TX, got {all_rssi[0]}" diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py index f1db308bbfc40..7a4bbfaecd942 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py @@ -10,6 +10,7 @@ BSIM_PERIPHERAL_CODE = """\ import _bleio import time +import sys adapter = _bleio.adapter @@ -20,50 +21,27 @@ adapter.start_advertising(advertisement, connectable=True) print("advertising", adapter.advertising) -for _ in range(80): - if adapter.connected: - break - time.sleep(0.1) - -print("connected", adapter.connected, "advertising", adapter.advertising) - -for _ in range(80): - if not adapter.connected: - break - time.sleep(0.1) - -print("disconnected", adapter.connected, len(adapter.connections)) -""" +was_connected = False +timeout = time.monotonic() + 8.0 +while not was_connected and time.monotonic() < timeout: + time.sleep(0.01) + was_connected = adapter.connected -BSIM_PERIPHERAL_CP_CENTRAL_PERIPHERAL_CODE = """\ -import _bleio -import time +if not was_connected: + print("connect timed out") + sys.exit(-1) -adapter = _bleio.adapter +print("connected", was_connected, "advertising", adapter.advertising) -name = b"CPPERIPH" -advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name - -print("peripheral start") -adapter.start_advertising(advertisement, connectable=True) -print("advertising", adapter.advertising) - -for _ in range(80): - if adapter.connected: - break - time.sleep(0.1) - -print("connected", adapter.connected, "advertising", adapter.advertising) - -for _ in range(80): - if not adapter.connected: - break - time.sleep(0.1) +if was_connected: + timeout = time.monotonic() + 8.0 + while adapter.connected and time.monotonic() < timeout: + time.sleep(0.1) print("disconnected", adapter.connected, len(adapter.connections)) """ -BSIM_PERIPHERAL_CP_CENTRAL_CODE = """\ +BSIM_CENTRAL_CODE = """\ import _bleio import time @@ -86,9 +64,8 @@ print("connected", connection.connected, adapter.connected, len(adapter.connections)) connection.disconnect() -for _ in range(40): - if not connection.connected and not adapter.connected: - break +timeout = time.monotonic() + 4.0 +while (connection.connected or adapter.connected) and time.monotonic() < timeout: time.sleep(0.1) print("disconnected", connection.connected, adapter.connected, len(adapter.connections)) @@ -118,8 +95,8 @@ def test_bsim_peripheral_zephyr_central(bsim_phy, circuitpython, zephyr_sample): @pytest.mark.duration(14) -@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CP_CENTRAL_CODE}) -@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CP_CENTRAL_PERIPHERAL_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_CENTRAL_CODE}) def test_bsim_peripheral_cp_central(bsim_phy, circuitpython1, circuitpython2): """Two CP instances: device 0 peripheral, device 1 central.""" peripheral = circuitpython1 From 2555baec7581415c95d7b93b7d2ddc9f224e5f16 Mon Sep 17 00:00:00 2001 From: Liz Date: Wed, 4 Mar 2026 20:22:50 -0500 Subject: [PATCH 411/517] working display! --- ports/espressif/boards/xteink_x4/board.c | 42 +++++++++++------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/ports/espressif/boards/xteink_x4/board.c b/ports/espressif/boards/xteink_x4/board.c index bf6fbddebc948..b5b193c65fec6 100644 --- a/ports/espressif/boards/xteink_x4/board.c +++ b/ports/espressif/boards/xteink_x4/board.c @@ -16,11 +16,11 @@ #define DELAY 0x80 -// SSD1677 controller driving a GDEQ0426T82 4.26" 800x480 grayscale E-Ink display. +// SSD1677 controller driving a GDEQ0426T82 4.26" 800x480 E-Ink display. const uint8_t ssd1677_display_start_sequence[] = { // Software Reset - 0x12, DELAY, 0x00, 0x14, // SWRESET + wait 20ms + 0x12, DELAY, 0x00, 0x14, // Temperature Sensor Control (use internal sensor) 0x18, 0x00, 0x01, 0x80, @@ -28,49 +28,45 @@ const uint8_t ssd1677_display_start_sequence[] = { // Booster Soft Start 0x0C, 0x00, 0x05, 0xAE, 0xC7, 0xC3, 0xC0, 0x40, - // Driver Output Control: 479 gates (HEIGHT-1 = 0x01DF) + // Driver Output Control: 480 gates, GD=0, SM=1, TB=0 = 0x02 0x01, 0x00, 0x03, 0xDF, 0x01, 0x02, - // Data Entry Mode: X increment, Y increment - 0x11, 0x00, 0x01, 0x02, + // Data Entry Mode: X increment, Y decrement = 0x01 + 0x11, 0x00, 0x01, 0x01, // Border Waveform Control 0x3C, 0x00, 0x01, 0x01, // Set RAM X Address Start/End: 0 to 799 - - // X start = 0 (LE: 0x00, 0x00), X end = 799 (LE: 0x1F, 0x03) 0x44, 0x00, 0x04, 0x00, 0x00, 0x1F, 0x03, - // Set RAM Y Address Start/End: 0 to 479 - 0x45, 0x00, 0x04, 0x00, 0x00, 0xDF, 0x01, + // Set RAM Y Address Start/End: 479 down to 0 + 0x45, 0x00, 0x04, 0xDF, 0x01, 0x00, 0x00, // Set RAM X Counter to 0 0x4E, 0x00, 0x02, 0x00, 0x00, - // Set RAM Y Counter to 0 - 0x4F, 0x00, 0x02, 0x00, 0x00, + // Set RAM Y Counter to 479 + 0x4F, 0x00, 0x02, 0xDF, 0x01, // Auto Write BW RAM (clear to white) - 0x46, DELAY, 0x01, 0xF7, 0xFF, // + wait 255ms + 0x46, DELAY, 0x01, 0xF7, 0xFF, - // Display Update Control 1: bypass RED buffer for mono mode + // Display Update Control 1: bypass RED 0x21, 0x00, 0x02, 0x40, 0x00, - // Display Update Control 2: full refresh sequence with OTP LUT + // Display Update Control 2: full refresh with OTP LUT 0x22, 0x00, 0x01, 0xF7, }; const uint8_t ssd1677_display_stop_sequence[] = { - // Power off sequence - 0x22, 0x00, 0x01, 0x83, // Display update control: power off - 0x20, 0x00, 0x00, // Master activation - // Deep sleep - 0x10, 0x00, 0x01, 0x01, // Enter deep sleep mode + 0x22, 0x00, 0x01, 0x83, + 0x20, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x01, }; const uint8_t ssd1677_display_refresh_sequence[] = { - 0x20, 0x00, 0x00 + 0x20, 0x00, 0x00, }; void board_init(void) { @@ -104,12 +100,12 @@ void board_init(void) { args.ram_height = 480; args.rotation = 0; args.write_black_ram_command = 0x24; - args.black_bits_inverted = true; + args.black_bits_inverted = false; args.refresh_sequence = ssd1677_display_refresh_sequence; args.refresh_sequence_len = sizeof(ssd1677_display_refresh_sequence); - args.refresh_time = 1.6; // ~1600ms full refresh + args.refresh_time = 1.6; args.busy_pin = &pin_GPIO6; - args.busy_state = true; // BUSY is active HIGH on SSD1677 + args.busy_state = true; args.seconds_per_frame = 5.0; args.grayscale = false; args.two_byte_sequence_length = true; From 4813ed60e6677b8f0bdd2bb6dfdd5bd2988c6512 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 5 Mar 2026 15:05:49 +0100 Subject: [PATCH 412/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 40 ++++++++++++++++++++-------------------- locale/el.po | 34 ++++++++++++++-------------------- locale/hi.po | 27 +++++++-------------------- locale/ko.po | 34 ++++++++++++++-------------------- locale/ru.po | 40 ++++++++++++++++++++-------------------- locale/tr.po | 34 ++++++++++++++-------------------- 6 files changed, 89 insertions(+), 120 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index 92979bdd31b9b..1b653528f192b 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -487,7 +487,7 @@ msgstr "pow() nepodporuje 3 argumenty" msgid "AP could not be started" msgstr "AP nemohl být spuštěn" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "Adresa musí být %d bajtů dlouhá" @@ -686,11 +686,6 @@ msgstr "RX a TX jsou vyžadovány pro kontrolu toku" msgid "Brightness not adjustable" msgstr "Jas není nastavitelný" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Vyrovnávací paměť + offset je příliš malý %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "Prvky bufferu musí být 4 bajty dlouhé nebo méně" @@ -733,10 +728,6 @@ msgstr "Buffer příliš malý" msgid "Bus pin %d is already in use" msgstr "Sběrnicový pin %d je již používán" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "Buffer musí být dlouhý 16 bajtů." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "Bloky CBC musí být násobky 16 bajtů" @@ -1047,12 +1038,18 @@ msgstr "Nepodařilo se alokovat paměť pro wifi scan" msgid "Failed to buffer the sample" msgstr "Nepodařilo se nabufferovat sample" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect" +msgstr "" + #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "Připojení se nezdařilo: interní chyba" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "Nepodařilo se připojit: časový limit" @@ -2271,10 +2268,6 @@ msgstr "USB zařízení používají příliš mnoho názvů rozhraní." msgid "USB error" msgstr "Chyba USB" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "UUID integer musí být 0-0xffff" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "UUID řetězec neodpovídá 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" @@ -3422,10 +3415,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "výchozí hodnoty musí být iterovatelné" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "délka initial_value je chybná" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3837,7 +3826,6 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "nenulový timeout musí být > 0.01" @@ -4321,7 +4309,6 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "timeout překročil maximální podporovanou hodnotu" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "timeout musí být < 655.35 s" @@ -4614,6 +4601,19 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Vyrovnávací paměť + offset je příliš malý %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "Buffer musí být dlouhý 16 bajtů." + +#~ msgid "UUID integer value must be 0-0xffff" +#~ msgstr "UUID integer musí být 0-0xffff" + +#~ msgid "initial_value length is wrong" +#~ msgstr "délka initial_value je chybná" + #, c-format #~ msgid "Invalid byte %.*s" #~ msgstr "Špatný byte %.*s" diff --git a/locale/el.po b/locale/el.po index 9ad4e3ccfc31d..74e0da7b28bd2 100644 --- a/locale/el.po +++ b/locale/el.po @@ -491,7 +491,7 @@ msgstr "pow() με 3 παραμέτρους δεν υποστηρίζεται" msgid "AP could not be started" msgstr "AP δεν μπόρεσε να εκκινηθεί" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "Η διεύθυνση πρέπει να είναι %d bytes μεγάλη" @@ -690,11 +690,6 @@ msgstr "Και RX και TX απαιτούνται για έλεγχο flow" msgid "Brightness not adjustable" msgstr "H φωτεινότητα δεν μπορεί να προσαρμοστεί" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Buffer + offset είναι πολύ μικρά %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "Στοιχεία του buffer πρέπει να είναι το πολύ 4 bytes" @@ -737,10 +732,6 @@ msgstr "Buffer πολύ μικρός" msgid "Bus pin %d is already in use" msgstr "Bus pin %d είναι ήδη σε χρήση" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "Byte buffer πρέπει να είναι 16 bytes." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "CBC blocks πρέπει να είναι πολλαπλάσια του 16 bytes" @@ -1055,12 +1046,18 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect" +msgstr "" + #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "" @@ -2275,10 +2272,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "" @@ -3421,10 +3414,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3836,7 +3825,6 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4320,7 +4308,6 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" @@ -4613,6 +4600,13 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Buffer + offset είναι πολύ μικρά %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "Byte buffer πρέπει να είναι 16 bytes." + #~ msgid "%q moved from %q to %q" #~ msgstr "%q μετακινήθηκε από το %q στο %q" diff --git a/locale/hi.po b/locale/hi.po index 046cea6b3d449..f9a35f0b41b2e 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -478,7 +478,7 @@ msgstr "" msgid "AP could not be started" msgstr "" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "" @@ -675,11 +675,6 @@ msgstr "" msgid "Brightness not adjustable" msgstr "" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "" @@ -722,10 +717,6 @@ msgstr "" msgid "Bus pin %d is already in use" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "" - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "" @@ -1031,12 +1022,18 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect" +msgstr "" + #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "" @@ -2249,10 +2246,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "" @@ -3395,10 +3388,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3810,7 +3799,6 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4294,7 +4282,6 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 311803d763007..03c658395e0d9 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -507,7 +507,7 @@ msgstr "pow() 는 3개의 인수를 지원하지 않습니다" msgid "AP could not be started" msgstr "AP를 시작할 수 없습니다" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "주소 길이는 %d 바이트 여야합니다" @@ -716,11 +716,6 @@ msgstr "플로우 제어에 RX와 TX가 모두 필요합니다" msgid "Brightness not adjustable" msgstr "밝기를 조절할 수 없습니다" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Buffer + offset이 너무 작습니다 %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "버퍼 요소는 4바이트 이하여야 합니다" @@ -763,10 +758,6 @@ msgstr "버퍼가 너무 작습니다" msgid "Bus pin %d is already in use" msgstr "Bus 핀 %d은 이미 사용 중입니다" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "잘못된 크기의 버퍼. 16 바이트 여야합니다." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "CBC 블록은 16 바이트의 배수여야 합니다" @@ -1079,12 +1070,18 @@ msgstr "wifi 검색 메모리 할당에 실패했습니다" msgid "Failed to buffer the sample" msgstr "샘플 버퍼링에 실패했습니다" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect" +msgstr "" + #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "연결에 실패했습니다: 내부 오류" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "연결에 실패했습니다: 시간 초과" @@ -2322,10 +2319,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "UUID문자열이 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'형식이 아닙니다" @@ -3469,10 +3462,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3884,7 +3873,6 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4368,7 +4356,6 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" @@ -4661,6 +4648,13 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Buffer + offset이 너무 작습니다 %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "잘못된 크기의 버퍼. 16 바이트 여야합니다." + #, c-format #~ msgid "Invalid byte %.*s" #~ msgstr "잘못된 바이트 %.*s" diff --git a/locale/ru.po b/locale/ru.po index 1eac7b309a6ab..2beb4b1ba388a 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -491,7 +491,7 @@ msgstr "Pow() с 3 аргументами не поддерживается" msgid "AP could not be started" msgstr "AP не может быть запущен" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "Адрес должен быть длиной %d байт" @@ -691,11 +691,6 @@ msgstr "Для управления потоком требуется как RX msgid "Brightness not adjustable" msgstr "Яркость не регулируется" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Буфер + сдвиг слишком малы %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "Элементы буфера должны иметь длину не более 4 байт" @@ -738,10 +733,6 @@ msgstr "Слишком маленький буфер" msgid "Bus pin %d is already in use" msgstr "Вывод шины %d уже используется" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "Буфер байтов должен быть размером 16 байтам." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "Блоки CBC должны быть кратны 16 байтам" @@ -1060,12 +1051,18 @@ msgstr "Не удалось выделить память для сканиро msgid "Failed to buffer the sample" msgstr "Не удалось выполнить буферизацию образца" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect" +msgstr "" + #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "Не удалось подключиться: внутренняя ошибка" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "Не удалось подключиться: таймаут" @@ -2305,10 +2302,6 @@ msgstr "USB-устройства указывают слишком много и msgid "USB error" msgstr "Ошибка USB" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "Целое значение UUID должно быть равно 0-0xffff" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "UUID строка не 'xxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxxxx \"" @@ -3476,10 +3469,6 @@ msgstr "индексы должны быть целыми числами, сре msgid "initial values must be iterable" msgstr "Начальные значения должны быть итерируемыми" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "длина первоначального_значения ошибочна" - #: py/compile.c msgid "inline assembler must be a function" msgstr "Встроенный ассемблер должен быть функцией" @@ -3899,7 +3888,6 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "Ненулевое время ожидания должно быть > 0,01" @@ -4384,7 +4372,6 @@ msgstr "" "Продолжительность таймаута превысила максимальное поддерживаемое значение" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "таймаут должен быть < 655.35 сек" @@ -4680,6 +4667,19 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Буфер + сдвиг слишком малы %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "Буфер байтов должен быть размером 16 байтам." + +#~ msgid "UUID integer value must be 0-0xffff" +#~ msgstr "Целое значение UUID должно быть равно 0-0xffff" + +#~ msgid "initial_value length is wrong" +#~ msgstr "длина первоначального_значения ошибочна" + #, c-format #~ msgid "Invalid byte %.*s" #~ msgstr "Неверный байт %.*s" diff --git a/locale/tr.po b/locale/tr.po index 4e75d975f55a4..34d654b11e47b 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -489,7 +489,7 @@ msgstr "3-argümanlı pow() desteklenmemektedir" msgid "AP could not be started" msgstr "" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "Adres %d byte uzunluğunda olmalıdır" @@ -688,11 +688,6 @@ msgstr "Hem RX hem de TX akış kontrolü için gerekli" msgid "Brightness not adjustable" msgstr "Parlaklık ayarlanabilir değil" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Buffer + offset çok küçük %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "Buffer elementleri 4 bit olmak zorunda" @@ -735,10 +730,6 @@ msgstr "" msgid "Bus pin %d is already in use" msgstr "Veriyolu pini %d kullanımda" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "Bit buffer'ı 16bit olmalı." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "CBC blokları 16 baytın katları şeklinde olmalı" @@ -1045,12 +1036,18 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect" +msgstr "" + #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "Bağlantı kurulamadı: internal error" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "Bağlantı kurulamadı: timeout" @@ -2271,10 +2268,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "" @@ -3417,10 +3410,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3832,7 +3821,6 @@ msgid "non-hex digit" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" msgstr "" @@ -4316,7 +4304,6 @@ msgid "timeout duration exceeded the maximum supported value" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" msgstr "" @@ -4609,6 +4596,13 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Buffer + offset çok küçük %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "Bit buffer'ı 16bit olmalı." + #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c int veya char tipine ihtiyaç duyar" From 687b4b3c401a6201fec0034cb3d8b3e0ce4e8333 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 5 Mar 2026 13:29:08 -0500 Subject: [PATCH 413/517] in *_make_new(), allocate obj only after all validations This was provoked by a specific safe-mode with SPI objects that were copied over VM instantiations, but there were some other cases. Also did some style cleanup. --- shared-bindings/_bleio/Address.c | 3 +- shared-bindings/_bleio/UUID.c | 10 ++-- shared-bindings/_stage/Layer.c | 48 +++++++++---------- shared-bindings/_stage/Text.c | 40 ++++++++-------- .../i2c_device/I2CDevice.c | 6 +-- .../spi_device/SPIDevice.c | 25 ++++++---- shared-bindings/aesio/aes.c | 4 +- shared-bindings/alarm/pin/PinAlarm.c | 2 +- shared-bindings/alarm/time/TimeAlarm.c | 3 +- shared-bindings/alarm/touch/TouchAlarm.c | 3 +- shared-bindings/analogbufio/BufferedIn.c | 6 +-- shared-bindings/audiobusio/PDMIn.c | 4 +- shared-bindings/audiocore/RawSample.c | 14 ++++-- shared-bindings/audiocore/WaveFile.c | 3 +- shared-bindings/audiodelays/PitchShift.c | 14 +++++- shared-bindings/audiofilters/Distortion.c | 17 ++++++- shared-bindings/audiofilters/Filter.c | 9 +++- shared-bindings/audiofilters/Phaser.c | 11 ++++- shared-bindings/audioio/AudioOut.c | 5 +- shared-bindings/audiomixer/MixerVoice.c | 2 +- shared-bindings/audiomp3/MP3Decoder.c | 4 +- shared-bindings/audiopwmio/PWMAudioOut.c | 6 ++- shared-bindings/bitbangio/I2C.c | 3 +- shared-bindings/bitbangio/SPI.c | 2 +- shared-bindings/busio/I2C.c | 2 +- shared-bindings/busio/SPI.c | 2 +- shared-bindings/busio/UART.c | 2 +- shared-bindings/camera/Camera.c | 2 +- shared-bindings/canio/Match.c | 3 +- shared-bindings/canio/Message.c | 3 +- .../canio/RemoteTransmissionRequest.c | 3 +- shared-bindings/countio/Counter.c | 2 +- shared-bindings/digitalio/DigitalInOut.c | 12 ++--- shared-bindings/displayio/ColorConverter.c | 1 - shared-bindings/displayio/TileGrid.c | 1 + shared-bindings/gnss/GNSS.c | 3 +- shared-bindings/i2cioexpander/IOExpander.c | 4 +- shared-bindings/i2ctarget/I2CTarget.c | 8 ++-- .../imagecapture/ParallelImageCapture.c | 1 - shared-bindings/ipaddress/IPv4Address.c | 1 - shared-bindings/is31fl3741/IS31FL3741.c | 1 - shared-bindings/jpegio/JpegDecoder.c | 1 - shared-bindings/keypad/Event.c | 3 +- shared-bindings/keypad/KeyMatrix.c | 3 +- shared-bindings/keypad/Keys.c | 2 +- shared-bindings/keypad/ShiftRegisterKeys.c | 4 +- shared-bindings/keypad_demux/DemuxKeyMatrix.c | 3 +- shared-bindings/memorymap/AddressRange.c | 1 - .../memorymonitor/AllocationAlarm.c | 1 - shared-bindings/msgpack/ExtType.c | 6 +-- shared-bindings/onewireio/OneWire.c | 2 +- shared-bindings/os/__init__.c | 2 +- shared-bindings/ps2io/Ps2.c | 1 - shared-bindings/pulseio/PulseIn.c | 1 - shared-bindings/pulseio/PulseOut.c | 1 + shared-bindings/qrio/QRDecoder.c | 2 +- shared-bindings/rclcpy/Node.c | 5 +- shared-bindings/rclcpy/__init__.c | 3 +- shared-bindings/sdcardio/SDCard.c | 3 +- shared-bindings/sdioio/SDCard.c | 2 +- shared-bindings/socketpool/SocketPool.c | 2 +- shared-bindings/spitarget/SPITarget.c | 3 +- shared-bindings/ssl/SSLContext.c | 1 - shared-bindings/ssl/__init__.c | 4 +- shared-bindings/synthio/LFO.c | 8 ++-- shared-bindings/synthio/Math.c | 6 +-- shared-bindings/synthio/MidiTrack.c | 1 - shared-bindings/synthio/Note.c | 6 +-- shared-bindings/synthio/Synthesizer.c | 1 - shared-bindings/synthio/__init__.c | 1 - shared-bindings/terminalio/Terminal.c | 2 +- .../tilepalettemapper/TilePaletteMapper.c | 1 - shared-bindings/touchio/TouchIn.c | 2 +- shared-bindings/usb/core/__init__.c | 3 +- shared-bindings/usb_hid/Device.c | 4 +- shared-bindings/usb_host/Port.c | 2 +- shared-bindings/vectorio/VectorShape.c | 2 +- 77 files changed, 223 insertions(+), 167 deletions(-) diff --git a/shared-bindings/_bleio/Address.c b/shared-bindings/_bleio/Address.c index 58f8a8adc1e61..a3c5e3ed7f050 100644 --- a/shared-bindings/_bleio/Address.c +++ b/shared-bindings/_bleio/Address.c @@ -37,8 +37,6 @@ static mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - bleio_address_obj_t *self = mp_obj_malloc(bleio_address_obj_t, &bleio_address_type); - const mp_obj_t address = args[ARG_address].u_obj; mp_buffer_info_t buf_info; mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); @@ -51,6 +49,7 @@ static mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_error_invalid(MP_QSTR_address_type); } + bleio_address_obj_t *self = mp_obj_malloc(bleio_address_obj_t, &bleio_address_type); common_hal_bleio_address_construct(self, buf_info.buf, address_type); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/_bleio/UUID.c b/shared-bindings/_bleio/UUID.c index 2d28d5a9b61a6..af618ebe8fe81 100644 --- a/shared-bindings/_bleio/UUID.c +++ b/shared-bindings/_bleio/UUID.c @@ -34,8 +34,6 @@ static mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - bleio_uuid_obj_t *self = mp_obj_malloc(bleio_uuid_obj_t, &bleio_uuid_type); - const mp_obj_t value = all_args[0]; uint8_t uuid128[16]; @@ -46,8 +44,10 @@ static mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, si } // NULL means no 128-bit value. + bleio_uuid_obj_t *self = mp_obj_malloc(bleio_uuid_obj_t, &bleio_uuid_type); common_hal_bleio_uuid_construct(self, uuid16, NULL); + return MP_OBJ_FROM_PTR(self); } else { if (mp_obj_is_str(value)) { // 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' @@ -93,10 +93,12 @@ static mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, si uint32_t uuid16 = (uuid128[13] << 8) | uuid128[12]; uuid128[12] = 0; uuid128[13] = 0; + + bleio_uuid_obj_t *self = mp_obj_malloc(bleio_uuid_obj_t, &bleio_uuid_type); common_hal_bleio_uuid_construct(self, uuid16, uuid128); - } - return MP_OBJ_FROM_PTR(self); + return MP_OBJ_FROM_PTR(self); + } } //| uuid16: int diff --git a/shared-bindings/_stage/Layer.c b/shared-bindings/_stage/Layer.c index de4861a70eedd..0d7a1bfbd5efc 100644 --- a/shared-bindings/_stage/Layer.c +++ b/shared-bindings/_stage/Layer.c @@ -38,38 +38,38 @@ static mp_obj_t layer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 4, 5, false); - layer_obj_t *self = mp_obj_malloc(layer_obj_t, type); - - self->width = mp_obj_get_int(args[0]); - self->height = mp_obj_get_int(args[1]); - self->x = 0; - self->y = 0; - self->frame = 0; - self->rotation = false; + mp_uint_t width = mp_arg_validate_int_min(mp_obj_get_int(args[0]), 0, MP_QSTR_width); + mp_uint_t height = mp_arg_validate_int_min(mp_obj_get_int(args[1]), 0, MP_QSTR_height); - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); - self->graphic = bufinfo.buf; - if (bufinfo.len != 2048) { - mp_raise_ValueError(MP_ERROR_TEXT("graphic must be 2048 bytes long")); - } + mp_buffer_info_t graphic_bufinfo; + mp_get_buffer_raise(args[2], &graphic_bufinfo, MP_BUFFER_READ); + mp_arg_validate_length(graphic_bufinfo.len, 2048, MP_QSTR_graphic); - mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); - self->palette = bufinfo.buf; - if (bufinfo.len != 32) { - mp_raise_ValueError(MP_ERROR_TEXT("palette must be 32 bytes long")); - } + mp_buffer_info_t palette_bufinfo; + mp_get_buffer_raise(args[3], &palette_bufinfo, MP_BUFFER_READ); + mp_arg_validate_length(palette_bufinfo.len, 32, MP_QSTR_palette); + mp_buffer_info_t map_bufinfo = { .buf = NULL }; if (n_args > 4) { - mp_get_buffer_raise(args[4], &bufinfo, MP_BUFFER_READ); - self->map = bufinfo.buf; - if (bufinfo.len < (self->width * self->height) / 2) { + mp_get_buffer_raise(args[4], &map_bufinfo, MP_BUFFER_READ); + if (map_bufinfo.len < (width * height) / 2) { mp_raise_ValueError(MP_ERROR_TEXT("map buffer too small")); } - } else { - self->map = NULL; } + // Only allocate after validation is finished. + layer_obj_t *self = mp_obj_malloc(layer_obj_t, type); + + self->width = width; + self->height = height; + self->x = 0; + self->y = 0; + self->frame = 0; + self->rotation = false; + self->graphic = graphic_bufinfo.buf; + self->palette = palette_bufinfo.buf; + self->map = map_bufinfo.buf; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/_stage/Text.c b/shared-bindings/_stage/Text.c index ecd4f644a781f..f64a1b381901c 100644 --- a/shared-bindings/_stage/Text.c +++ b/shared-bindings/_stage/Text.c @@ -38,32 +38,32 @@ static mp_obj_t text_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 5, 5, false); - text_obj_t *self = mp_obj_malloc(text_obj_t, type); + mp_uint_t width = mp_arg_validate_int_min(mp_obj_get_int(args[0]), 0, MP_QSTR_width); + mp_uint_t height = mp_arg_validate_int_min(mp_obj_get_int(args[1]), 0, MP_QSTR_height); - self->width = mp_obj_get_int(args[0]); - self->height = mp_obj_get_int(args[1]); - self->x = 0; - self->y = 0; - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); - self->font = bufinfo.buf; - if (bufinfo.len != 2048) { - mp_raise_ValueError(MP_ERROR_TEXT("font must be 2048 bytes long")); - } + mp_buffer_info_t font_bufinfo; + mp_get_buffer_raise(args[2], &font_bufinfo, MP_BUFFER_READ); + mp_arg_validate_length(font_bufinfo.len, 2048, MP_QSTR_font); - mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); - self->palette = bufinfo.buf; - if (bufinfo.len != 32) { - mp_raise_ValueError(MP_ERROR_TEXT("palette must be 32 bytes long")); - } + mp_buffer_info_t palette_bufinfo; + mp_get_buffer_raise(args[3], &palette_bufinfo, MP_BUFFER_READ); + mp_arg_validate_length(font_bufinfo.len, 32, MP_QSTR_palette); - mp_get_buffer_raise(args[4], &bufinfo, MP_BUFFER_READ); - self->chars = bufinfo.buf; - if (bufinfo.len < self->width * self->height) { + mp_buffer_info_t chars_bufinfo; + mp_get_buffer_raise(args[4], &chars_bufinfo, MP_BUFFER_READ); + if (chars_bufinfo.len < width * height) { mp_raise_ValueError(MP_ERROR_TEXT("chars buffer too small")); } + text_obj_t *self = mp_obj_malloc(text_obj_t, type); + self->width = width; + self->height = height; + self->x = 0; + self->y = 0; + self->font = font_bufinfo.buf; + self->palette = palette_bufinfo.buf; + self->chars = chars_bufinfo.buf; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c b/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c index 32d418923fb92..5fb821ceade80 100644 --- a/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c +++ b/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c @@ -47,8 +47,6 @@ //| ... //| static mp_obj_t adafruit_bus_device_i2cdevice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - adafruit_bus_device_i2cdevice_obj_t *self = - mp_obj_malloc(adafruit_bus_device_i2cdevice_obj_t, &adafruit_bus_device_i2cdevice_type); enum { ARG_i2c, ARG_device_address, ARG_probe }; static const mp_arg_t allowed_args[] = { { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -60,12 +58,14 @@ static mp_obj_t adafruit_bus_device_i2cdevice_make_new(const mp_obj_type_t *type mp_obj_t *i2c = args[ARG_i2c].u_obj; + adafruit_bus_device_i2cdevice_obj_t *self = + mp_obj_malloc(adafruit_bus_device_i2cdevice_obj_t, &adafruit_bus_device_i2cdevice_type); common_hal_adafruit_bus_device_i2cdevice_construct(MP_OBJ_TO_PTR(self), i2c, args[ARG_device_address].u_int); if (args[ARG_probe].u_bool == true) { common_hal_adafruit_bus_device_i2cdevice_probe_for_device(self); } - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } //| def __enter__(self) -> I2CDevice: diff --git a/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c b/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c index f62aedc47e3d0..2f4895e38ce92 100644 --- a/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c +++ b/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c @@ -59,8 +59,6 @@ //| ... //| static mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - adafruit_bus_device_spidevice_obj_t *self = - mp_obj_malloc(adafruit_bus_device_spidevice_obj_t, &adafruit_bus_device_spidevice_type); enum { ARG_spi, ARG_chip_select, ARG_cs_active_value, ARG_baudrate, ARG_polarity, ARG_phase, ARG_extra_clocks }; static const mp_arg_t allowed_args[] = { { MP_QSTR_spi, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -78,12 +76,11 @@ static mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type mp_arg_validate_type_or_none(args[ARG_chip_select].u_obj, &digitalio_digitalinout_type, MP_QSTR_chip_select); - common_hal_adafruit_bus_device_spidevice_construct(MP_OBJ_TO_PTR(self), spi, args[ARG_chip_select].u_obj, args[ARG_cs_active_value].u_bool, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, - args[ARG_phase].u_int, args[ARG_extra_clocks].u_int); - if (args[ARG_chip_select].u_obj != mp_const_none) { - digitalinout_result_t result = common_hal_digitalio_digitalinout_switch_to_output(MP_OBJ_TO_PTR(args[ARG_chip_select].u_obj), - true, DRIVE_MODE_PUSH_PULL); + digitalinout_result_t result = + common_hal_digitalio_digitalinout_switch_to_output(MP_OBJ_TO_PTR(args[ARG_chip_select].u_obj), + true, + DRIVE_MODE_PUSH_PULL); #if CIRCUITPY_DIGITALIO_HAVE_INPUT_ONLY if (result == DIGITALINOUT_INPUT_ONLY) { mp_raise_NotImplementedError(MP_ERROR_TEXT("Pin is input only")); @@ -93,7 +90,19 @@ static mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type #endif } - return (mp_obj_t)self; + adafruit_bus_device_spidevice_obj_t *self = + mp_obj_malloc(adafruit_bus_device_spidevice_obj_t, &adafruit_bus_device_spidevice_type); + common_hal_adafruit_bus_device_spidevice_construct(MP_OBJ_TO_PTR(self), + spi, + args[ARG_chip_select].u_obj, + args[ARG_cs_active_value].u_bool, + args[ARG_baudrate].u_int, + args[ARG_polarity].u_int, + args[ARG_phase].u_int, + args[ARG_extra_clocks].u_int); + + + return MP_OBJ_FROM_PTR(self); } //| def __enter__(self) -> busio.SPI: diff --git a/shared-bindings/aesio/aes.c b/shared-bindings/aesio/aes.c index a6916042fda19..75f2850a7ef96 100644 --- a/shared-bindings/aesio/aes.c +++ b/shared-bindings/aesio/aes.c @@ -55,8 +55,6 @@ static mp_obj_t aesio_aes_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - aesio_aes_obj_t *self = mp_obj_malloc(aesio_aes_obj_t, &aesio_aes_type); - enum { ARG_key, ARG_mode, ARG_IV, ARG_counter, ARG_segment_size }; static const mp_arg_t allowed_args[] = { {MP_QSTR_key, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, @@ -100,8 +98,10 @@ static mp_obj_t aesio_aes_make_new(const mp_obj_type_t *type, size_t n_args, iv = bufinfo.buf; } + aesio_aes_obj_t *self = mp_obj_malloc(aesio_aes_obj_t, &aesio_aes_type); common_hal_aesio_aes_construct(self, key, key_length, iv, mode, args[ARG_counter].u_int); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/alarm/pin/PinAlarm.c b/shared-bindings/alarm/pin/PinAlarm.c index 8d93ca8d0de6a..4633fdb233c8d 100644 --- a/shared-bindings/alarm/pin/PinAlarm.c +++ b/shared-bindings/alarm/pin/PinAlarm.c @@ -42,7 +42,6 @@ //| ... //| static mp_obj_t alarm_pin_pinalarm_make_new(const mp_obj_type_t *type, mp_uint_t n_args, size_t n_kw, const mp_obj_t *all_args) { - alarm_pin_pinalarm_obj_t *self = mp_obj_malloc(alarm_pin_pinalarm_obj_t, &alarm_pin_pinalarm_type); enum { ARG_pin, ARG_value, ARG_edge, ARG_pull }; static const mp_arg_t allowed_args[] = { { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -55,6 +54,7 @@ static mp_obj_t alarm_pin_pinalarm_make_new(const mp_obj_type_t *type, mp_uint_t const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); + alarm_pin_pinalarm_obj_t *self = mp_obj_malloc(alarm_pin_pinalarm_obj_t, &alarm_pin_pinalarm_type); common_hal_alarm_pin_pinalarm_construct(self, pin, args[ARG_value].u_bool, diff --git a/shared-bindings/alarm/time/TimeAlarm.c b/shared-bindings/alarm/time/TimeAlarm.c index 0277c22fc44bf..a3df6ddf7fd2e 100644 --- a/shared-bindings/alarm/time/TimeAlarm.c +++ b/shared-bindings/alarm/time/TimeAlarm.c @@ -46,8 +46,6 @@ mp_obj_t MP_WEAK rtc_get_time_source_time(void) { //| static mp_obj_t alarm_time_timealarm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - alarm_time_timealarm_obj_t *self = mp_obj_malloc(alarm_time_timealarm_obj_t, &alarm_time_timealarm_type); - enum { ARG_monotonic_time, ARG_epoch_time }; static const mp_arg_t allowed_args[] = { { MP_QSTR_monotonic_time, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -92,6 +90,7 @@ static mp_obj_t alarm_time_timealarm_make_new(const mp_obj_type_t *type, mp_raise_ValueError(MP_ERROR_TEXT("Time is in the past.")); } + alarm_time_timealarm_obj_t *self = mp_obj_malloc(alarm_time_timealarm_obj_t, &alarm_time_timealarm_type); common_hal_alarm_time_timealarm_construct(self, monotonic_time); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/alarm/touch/TouchAlarm.c b/shared-bindings/alarm/touch/TouchAlarm.c index f25e826cff01c..42b149c9eeea7 100644 --- a/shared-bindings/alarm/touch/TouchAlarm.c +++ b/shared-bindings/alarm/touch/TouchAlarm.c @@ -26,8 +26,6 @@ //| static mp_obj_t alarm_touch_touchalarm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - alarm_touch_touchalarm_obj_t *self = mp_obj_malloc(alarm_touch_touchalarm_obj_t, &alarm_touch_touchalarm_type); - enum { ARG_pin }; static const mp_arg_t allowed_args[] = { { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -38,6 +36,7 @@ static mp_obj_t alarm_touch_touchalarm_make_new(const mp_obj_type_t *type, const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); + alarm_touch_touchalarm_obj_t *self = mp_obj_malloc(alarm_touch_touchalarm_obj_t, &alarm_touch_touchalarm_type); common_hal_alarm_touch_touchalarm_construct(self, pin); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/analogbufio/BufferedIn.c b/shared-bindings/analogbufio/BufferedIn.c index a25610207b49a..2c42ee1f32571 100644 --- a/shared-bindings/analogbufio/BufferedIn.c +++ b/shared-bindings/analogbufio/BufferedIn.c @@ -60,10 +60,8 @@ static mp_obj_t analogbufio_bufferedin_make_new(const mp_obj_type_t *type, size_ // Validate Pin const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); - // Create local object - analogbufio_bufferedin_obj_t *self = mp_obj_malloc_with_finaliser(analogbufio_bufferedin_obj_t, &analogbufio_bufferedin_type); - - // Call local interface in ports/common-hal/analogbufio + analogbufio_bufferedin_obj_t *self = + mp_obj_malloc_with_finaliser(analogbufio_bufferedin_obj_t, &analogbufio_bufferedin_type); common_hal_analogbufio_bufferedin_construct(self, pin, args[ARG_sample_rate].u_int); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/audiobusio/PDMIn.c b/shared-bindings/audiobusio/PDMIn.c index 2890afda0fd3a..092897a15cff3 100644 --- a/shared-bindings/audiobusio/PDMIn.c +++ b/shared-bindings/audiobusio/PDMIn.c @@ -96,9 +96,6 @@ static mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_ar const mcu_pin_obj_t *clock_pin = validate_obj_is_free_pin(args[ARG_clock_pin].u_obj, MP_QSTR_clock_pin); const mcu_pin_obj_t *data_pin = validate_obj_is_free_pin(args[ARG_data_pin].u_obj, MP_QSTR_data_pin); - // create PDMIn object from the given pin - audiobusio_pdmin_obj_t *self = mp_obj_malloc_with_finaliser(audiobusio_pdmin_obj_t, &audiobusio_pdmin_type); - uint32_t sample_rate = args[ARG_sample_rate].u_int; uint8_t bit_depth = args[ARG_bit_depth].u_int; if (bit_depth % 8 != 0) { @@ -115,6 +112,7 @@ static mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_ar : mp_obj_get_float(args[ARG_startup_delay].u_obj); mp_arg_validate_float_range(startup_delay, 0.0f, 1.0f, MP_QSTR_startup_delay); + audiobusio_pdmin_obj_t *self = mp_obj_malloc_with_finaliser(audiobusio_pdmin_obj_t, &audiobusio_pdmin_type); common_hal_audiobusio_pdmin_construct(self, clock_pin, data_pin, sample_rate, bit_depth, mono, oversample); diff --git a/shared-bindings/audiocore/RawSample.c b/shared-bindings/audiocore/RawSample.c index 30c1d1ad600d3..8dc57903286d3 100644 --- a/shared-bindings/audiocore/RawSample.c +++ b/shared-bindings/audiocore/RawSample.c @@ -90,7 +90,6 @@ static mp_obj_t audioio_rawsample_make_new(const mp_obj_type_t *type, size_t n_a mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - audioio_rawsample_obj_t *self = mp_obj_malloc(audioio_rawsample_obj_t, &audioio_rawsample_type); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); uint8_t bytes_per_sample = 1; @@ -103,9 +102,16 @@ static mp_obj_t audioio_rawsample_make_new(const mp_obj_type_t *type, size_t n_a if (!args[ARG_single_buffer].u_bool && bufinfo.len % (bytes_per_sample * args[ARG_channel_count].u_int * 2) != 0) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Length of %q must be an even multiple of channel_count * type_size"), MP_QSTR_buffer); } - common_hal_audioio_rawsample_construct(self, ((uint8_t *)bufinfo.buf), bufinfo.len, - bytes_per_sample, signed_samples, args[ARG_channel_count].u_int, - args[ARG_sample_rate].u_int, args[ARG_single_buffer].u_bool); + + audioio_rawsample_obj_t *self = mp_obj_malloc(audioio_rawsample_obj_t, &audioio_rawsample_type); + common_hal_audioio_rawsample_construct(self, + ((uint8_t *)bufinfo.buf), + bufinfo.len, + bytes_per_sample, + signed_samples, + args[ARG_channel_count].u_int, + args[ARG_sample_rate].u_int, + args[ARG_single_buffer].u_bool); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiocore/WaveFile.c b/shared-bindings/audiocore/WaveFile.c index 4ba7d24bfd2c5..c93bedd4213ea 100644 --- a/shared-bindings/audiocore/WaveFile.c +++ b/shared-bindings/audiocore/WaveFile.c @@ -60,7 +60,6 @@ static mp_obj_t audioio_wavefile_make_new(const mp_obj_type_t *type, size_t n_ar arg = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), arg, MP_ROM_QSTR(MP_QSTR_rb)); } - audioio_wavefile_obj_t *self = mp_obj_malloc(audioio_wavefile_obj_t, &audioio_wavefile_type); if (!mp_obj_is_type(arg, &mp_type_vfs_fat_fileio)) { mp_raise_TypeError(MP_ERROR_TEXT("file must be a file opened in byte mode")); } @@ -72,6 +71,8 @@ static mp_obj_t audioio_wavefile_make_new(const mp_obj_type_t *type, size_t n_ar buffer = bufinfo.buf; buffer_size = mp_arg_validate_length_range(bufinfo.len, 8, 1024, MP_QSTR_buffer); } + + audioio_wavefile_obj_t *self = mp_obj_malloc(audioio_wavefile_obj_t, &audioio_wavefile_type); common_hal_audioio_wavefile_construct(self, MP_OBJ_TO_PTR(arg), buffer, buffer_size); diff --git a/shared-bindings/audiodelays/PitchShift.c b/shared-bindings/audiodelays/PitchShift.c index b94df7d9edd08..ee3bf5afdd7bd 100644 --- a/shared-bindings/audiodelays/PitchShift.c +++ b/shared-bindings/audiodelays/PitchShift.c @@ -94,8 +94,18 @@ static mp_obj_t audiodelays_pitch_shift_make_new(const mp_obj_type_t *type, size mp_raise_ValueError(MP_ERROR_TEXT("bits_per_sample must be 8 or 16")); } - audiodelays_pitch_shift_obj_t *self = mp_obj_malloc(audiodelays_pitch_shift_obj_t, &audiodelays_pitch_shift_type); - common_hal_audiodelays_pitch_shift_construct(self, args[ARG_semitones].u_obj, args[ARG_mix].u_obj, args[ARG_window].u_int, args[ARG_overlap].u_int, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + audiodelays_pitch_shift_obj_t *self = + mp_obj_malloc(audiodelays_pitch_shift_obj_t, &audiodelays_pitch_shift_type); + common_hal_audiodelays_pitch_shift_construct(self, + args[ARG_semitones].u_obj, + args[ARG_mix].u_obj, + args[ARG_window].u_int, + args[ARG_overlap].u_int, + args[ARG_buffer_size].u_int, + bits_per_sample, + args[ARG_samples_signed].u_bool, + channel_count, + sample_rate); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiofilters/Distortion.c b/shared-bindings/audiofilters/Distortion.c index 8de4c1d1ea331..bed28f5ed4973 100644 --- a/shared-bindings/audiofilters/Distortion.c +++ b/shared-bindings/audiofilters/Distortion.c @@ -145,8 +145,21 @@ static mp_obj_t audiofilters_distortion_make_new(const mp_obj_type_t *type, size mode = validate_distortion_mode(args[ARG_mode].u_obj, MP_QSTR_mode); } - audiofilters_distortion_obj_t *self = mp_obj_malloc(audiofilters_distortion_obj_t, &audiofilters_distortion_type); - common_hal_audiofilters_distortion_construct(self, args[ARG_drive].u_obj, args[ARG_pre_gain].u_obj, args[ARG_post_gain].u_obj, mode, args[ARG_soft_clip].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + audiofilters_distortion_obj_t *self = + mp_obj_malloc(audiofilters_distortion_obj_t, &audiofilters_distortion_type); + common_hal_audiofilters_distortion_construct(self, + args[ARG_drive].u_obj, + args[ARG_pre_gain].u_obj, + args[ARG_post_gain].u_obj, + mode, + args[ARG_soft_clip].u_obj, + args[ARG_mix].u_obj, + args[ARG_buffer_size].u_int, + bits_per_sample, + args[ARG_samples_signed].u_bool, + channel_count, + sample_rate); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiofilters/Filter.c b/shared-bindings/audiofilters/Filter.c index 426fff226168f..2d0d12b226abe 100644 --- a/shared-bindings/audiofilters/Filter.c +++ b/shared-bindings/audiofilters/Filter.c @@ -91,7 +91,14 @@ static mp_obj_t audiofilters_filter_make_new(const mp_obj_type_t *type, size_t n } audiofilters_filter_obj_t *self = mp_obj_malloc(audiofilters_filter_obj_t, &audiofilters_filter_type); - common_hal_audiofilters_filter_construct(self, args[ARG_filter].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + common_hal_audiofilters_filter_construct(self, + args[ARG_filter].u_obj, + args[ARG_mix].u_obj, + args[ARG_buffer_size].u_int, + bits_per_sample, + args[ARG_samples_signed].u_bool, + channel_count, + sample_rate); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiofilters/Phaser.c b/shared-bindings/audiofilters/Phaser.c index 9f713afce5f72..2aed9623b7219 100644 --- a/shared-bindings/audiofilters/Phaser.c +++ b/shared-bindings/audiofilters/Phaser.c @@ -91,7 +91,16 @@ static mp_obj_t audiofilters_phaser_make_new(const mp_obj_type_t *type, size_t n } audiofilters_phaser_obj_t *self = mp_obj_malloc(audiofilters_phaser_obj_t, &audiofilters_phaser_type); - common_hal_audiofilters_phaser_construct(self, args[ARG_frequency].u_obj, args[ARG_feedback].u_obj, args[ARG_mix].u_obj, args[ARG_stages].u_int, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + common_hal_audiofilters_phaser_construct(self, + args[ARG_frequency].u_obj, + args[ARG_feedback].u_obj, + args[ARG_mix].u_obj, + args[ARG_stages].u_int, + args[ARG_buffer_size].u_int, + bits_per_sample, + args[ARG_samples_signed].u_bool, + channel_count, + sample_rate); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audioio/AudioOut.c b/shared-bindings/audioio/AudioOut.c index 82aecefa370ba..8ea068ceb8f3a 100644 --- a/shared-bindings/audioio/AudioOut.c +++ b/shared-bindings/audioio/AudioOut.c @@ -103,7 +103,10 @@ static mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_ar // create AudioOut object from the given pin audioio_audioout_obj_t *self = mp_obj_malloc_with_finaliser(audioio_audioout_obj_t, &audioio_audioout_type); - common_hal_audioio_audioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); + common_hal_audioio_audioout_construct(self, + left_channel_pin, + right_channel_pin, + args[ARG_quiescent_value].u_int); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiomixer/MixerVoice.c b/shared-bindings/audiomixer/MixerVoice.c index 128c214d447f8..4957fab388460 100644 --- a/shared-bindings/audiomixer/MixerVoice.c +++ b/shared-bindings/audiomixer/MixerVoice.c @@ -29,8 +29,8 @@ // TODO: support mono or stereo voices static mp_obj_t audiomixer_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { mp_arg_check_num(n_args, n_kw, 0, 0, false); - audiomixer_mixervoice_obj_t *self = mp_obj_malloc(audiomixer_mixervoice_obj_t, &audiomixer_mixervoice_type); + audiomixer_mixervoice_obj_t *self = mp_obj_malloc(audiomixer_mixervoice_obj_t, &audiomixer_mixervoice_type); common_hal_audiomixer_mixervoice_construct(self); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/audiomp3/MP3Decoder.c b/shared-bindings/audiomp3/MP3Decoder.c index ff6c77e85726b..433b308c156c8 100644 --- a/shared-bindings/audiomp3/MP3Decoder.c +++ b/shared-bindings/audiomp3/MP3Decoder.c @@ -91,8 +91,6 @@ static mp_obj_t audiomp3_mp3file_make_new(const mp_obj_type_t *type, size_t n_ar stream = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), stream, MP_ROM_QSTR(MP_QSTR_rb)); } - audiomp3_mp3file_obj_t *self = mp_obj_malloc_with_finaliser(audiomp3_mp3file_obj_t, &audiomp3_mp3file_type); - const mp_stream_p_t *stream_p = mp_get_stream_raise(stream, MP_STREAM_OP_READ); if (stream_p->is_text) { @@ -106,6 +104,8 @@ static mp_obj_t audiomp3_mp3file_make_new(const mp_obj_type_t *type, size_t n_ar buffer = bufinfo.buf; buffer_size = bufinfo.len; } + + audiomp3_mp3file_obj_t *self = mp_obj_malloc_with_finaliser(audiomp3_mp3file_obj_t, &audiomp3_mp3file_type); common_hal_audiomp3_mp3file_construct(self, stream, buffer, buffer_size); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/audiopwmio/PWMAudioOut.c b/shared-bindings/audiopwmio/PWMAudioOut.c index 3c96143364183..5b8d071924b83 100644 --- a/shared-bindings/audiopwmio/PWMAudioOut.c +++ b/shared-bindings/audiopwmio/PWMAudioOut.c @@ -100,8 +100,10 @@ static mp_obj_t audiopwmio_pwmaudioout_make_new(const mp_obj_type_t *type, size_ // create AudioOut object from the given pin // The object is created with a finaliser as some ports use these (rather than 'reset' functions) // to ensure resources are collected at interpreter shutdown. - audiopwmio_pwmaudioout_obj_t *self = mp_obj_malloc_with_finaliser(audiopwmio_pwmaudioout_obj_t, &audiopwmio_pwmaudioout_type); - common_hal_audiopwmio_pwmaudioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); + audiopwmio_pwmaudioout_obj_t *self = + mp_obj_malloc_with_finaliser(audiopwmio_pwmaudioout_obj_t, &audiopwmio_pwmaudioout_type); + common_hal_audiopwmio_pwmaudioout_construct(self, + left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/bitbangio/I2C.c b/shared-bindings/bitbangio/I2C.c index ddd8824eb58ed..e14133926994a 100644 --- a/shared-bindings/bitbangio/I2C.c +++ b/shared-bindings/bitbangio/I2C.c @@ -59,7 +59,8 @@ static mp_obj_t bitbangio_i2c_make_new(const mp_obj_type_t *type, size_t n_args, bitbangio_i2c_obj_t *self = mp_obj_malloc_with_finaliser(bitbangio_i2c_obj_t, &bitbangio_i2c_type); shared_module_bitbangio_i2c_construct(self, args[ARG_scl].u_obj, args[ARG_sda].u_obj, args[ARG_frequency].u_int, args[ARG_timeout].u_int); - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: diff --git a/shared-bindings/bitbangio/SPI.c b/shared-bindings/bitbangio/SPI.c index f2e635b47bb86..de021867f3ca9 100644 --- a/shared-bindings/bitbangio/SPI.c +++ b/shared-bindings/bitbangio/SPI.c @@ -67,7 +67,7 @@ static mp_obj_t bitbangio_spi_make_new(const mp_obj_type_t *type, size_t n_args, bitbangio_spi_obj_t *self = mp_obj_malloc(bitbangio_spi_obj_t, &bitbangio_spi_type); shared_module_bitbangio_spi_construct(self, args[ARG_clock].u_obj, args[ARG_MOSI].u_obj, args[ARG_MISO].u_obj); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: diff --git a/shared-bindings/busio/I2C.c b/shared-bindings/busio/I2C.c index a2724e60c3422..c93566c0b62e4 100644 --- a/shared-bindings/busio/I2C.c +++ b/shared-bindings/busio/I2C.c @@ -67,7 +67,7 @@ static mp_obj_t busio_i2c_make_new(const mp_obj_type_t *type, size_t n_args, siz busio_i2c_obj_t *self = mp_obj_malloc_with_finaliser(busio_i2c_obj_t, &busio_i2c_type); common_hal_busio_i2c_construct(self, scl, sda, args[ARG_frequency].u_int, args[ARG_timeout].u_int); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); #else mp_raise_NotImplementedError(NULL); #endif // CIRCUITPY_BUSIO_I2C diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 24c02a16e634b..132da473af94e 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -88,7 +88,6 @@ // TODO(tannewt): Support LSB SPI. static mp_obj_t busio_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_BUSIO_SPI - busio_spi_obj_t *self = mp_obj_malloc_with_finaliser(busio_spi_obj_t, &busio_spi_type); enum { ARG_clock, ARG_MOSI, ARG_MISO, ARG_half_duplex }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -107,6 +106,7 @@ static mp_obj_t busio_spi_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_raise_ValueError(MP_ERROR_TEXT("Must provide MISO or MOSI pin")); } + busio_spi_obj_t *self = mp_obj_malloc_with_finaliser(busio_spi_obj_t, &busio_spi_type); common_hal_busio_spi_construct(self, clock, mosi, miso, args[ARG_half_duplex].u_bool); return MP_OBJ_FROM_PTR(self); #else diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 4714c3ee0733a..017b983778115 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -157,7 +157,7 @@ static mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, si common_hal_busio_uart_construct(self, tx, rx, rts, cts, rs485_dir, rs485_invert, args[ARG_baudrate].u_int, bits, parity, stop, timeout, buffer_size, NULL, false); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); #else mp_raise_NotImplementedError(NULL); #endif // CIRCUITPY_BUSIO_UART diff --git a/shared-bindings/camera/Camera.c b/shared-bindings/camera/Camera.c index 6f42fa08bcfc4..f0991082e5039 100644 --- a/shared-bindings/camera/Camera.c +++ b/shared-bindings/camera/Camera.c @@ -41,10 +41,10 @@ //| ... //| static mp_obj_t camera_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - camera_obj_t *self = mp_obj_malloc(camera_obj_t, &camera_type); // No arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); + camera_obj_t *self = mp_obj_malloc(camera_obj_t, &camera_type); common_hal_camera_construct(self); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/canio/Match.c b/shared-bindings/canio/Match.c index 5e44a7ce4ed53..e7c3dba42aab8 100644 --- a/shared-bindings/canio/Match.c +++ b/shared-bindings/canio/Match.c @@ -47,7 +47,8 @@ static mp_obj_t canio_match_make_new(const mp_obj_type_t *type, size_t n_args, s canio_match_obj_t *self = mp_obj_malloc(canio_match_obj_t, &canio_match_type); common_hal_canio_match_construct(self, id, mask, args[ARG_extended].u_bool); - return self; + + return MP_OBJ_FROM_PTR(self); } //| id: int diff --git a/shared-bindings/canio/Message.c b/shared-bindings/canio/Message.c index c40892b5edb06..848b5b15e43cb 100644 --- a/shared-bindings/canio/Message.c +++ b/shared-bindings/canio/Message.c @@ -41,7 +41,8 @@ static mp_obj_t canio_message_make_new(const mp_obj_type_t *type, size_t n_args, canio_message_obj_t *self = mp_obj_malloc(canio_message_obj_t, &canio_message_type); common_hal_canio_message_construct(self, args[ARG_id].u_int, data.buf, data.len, args[ARG_extended].u_bool); - return self; + + return MP_OBJ_FROM_PTR(self); } //| id: int diff --git a/shared-bindings/canio/RemoteTransmissionRequest.c b/shared-bindings/canio/RemoteTransmissionRequest.c index 966fee71215b2..bdc91c494a4a3 100644 --- a/shared-bindings/canio/RemoteTransmissionRequest.c +++ b/shared-bindings/canio/RemoteTransmissionRequest.c @@ -42,7 +42,8 @@ static mp_obj_t canio_remote_transmission_request_make_new(const mp_obj_type_t * canio_remote_transmission_request_obj_t *self = mp_obj_malloc(canio_remote_transmission_request_obj_t, &canio_remote_transmission_request_type); common_hal_canio_remote_transmission_request_construct(self, args[ARG_id].u_int, length, args[ARG_extended].u_bool); - return self; + + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/countio/Counter.c b/shared-bindings/countio/Counter.c index c2e3ddc740495..9f8bd00645ce5 100644 --- a/shared-bindings/countio/Counter.c +++ b/shared-bindings/countio/Counter.c @@ -66,8 +66,8 @@ static mp_obj_t countio_counter_make_new(const mp_obj_type_t *type, size_t n_arg const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); const countio_edge_t edge = validate_edge(args[ARG_edge].u_obj, MP_QSTR_edge); const digitalio_pull_t pull = validate_pull(args[ARG_pull].u_obj, MP_QSTR_pull); - countio_counter_obj_t *self = mp_obj_malloc_with_finaliser(countio_counter_obj_t, &countio_counter_type); + countio_counter_obj_t *self = mp_obj_malloc_with_finaliser(countio_counter_obj_t, &countio_counter_type); common_hal_countio_counter_construct(self, pin, edge, pull); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/digitalio/DigitalInOut.c b/shared-bindings/digitalio/DigitalInOut.c index c868ca7d443b9..58f90b0b1419f 100644 --- a/shared-bindings/digitalio/DigitalInOut.c +++ b/shared-bindings/digitalio/DigitalInOut.c @@ -71,9 +71,9 @@ static mp_obj_t digitalio_digitalinout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - digitalio_digitalinout_obj_t *self = mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); - const mcu_pin_obj_t *pin = common_hal_digitalio_validate_pin(args[0]); + + digitalio_digitalinout_obj_t *self = mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); common_hal_digitalio_digitalinout_construct(self, pin); return MP_OBJ_FROM_PTR(self); @@ -191,9 +191,9 @@ static mp_obj_t digitalio_digitalinout_obj_get_direction(mp_obj_t self_in) { check_for_deinit(self); digitalio_direction_t direction = common_hal_digitalio_digitalinout_get_direction(self); if (direction == DIRECTION_INPUT) { - return (mp_obj_t)&digitalio_direction_input_obj; + return MP_OBJ_FROM_PTR(&digitalio_direction_input_obj); } - return (mp_obj_t)&digitalio_direction_output_obj; + return MP_OBJ_FROM_PTR(&digitalio_direction_output_obj); } MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_get_direction_obj, digitalio_digitalinout_obj_get_direction); @@ -255,9 +255,9 @@ static mp_obj_t digitalio_digitalinout_obj_get_drive_mode(mp_obj_t self_in) { } digitalio_drive_mode_t drive_mode = common_hal_digitalio_digitalinout_get_drive_mode(self); if (drive_mode == DRIVE_MODE_PUSH_PULL) { - return (mp_obj_t)&digitalio_drive_mode_push_pull_obj; + return MP_OBJ_FROM_PTR(&digitalio_drive_mode_push_pull_obj); } - return (mp_obj_t)&digitalio_drive_mode_open_drain_obj; + return MP_OBJ_FROM_PTR(&digitalio_drive_mode_open_drain_obj); } MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_get_drive_mode_obj, digitalio_digitalinout_obj_get_drive_mode); diff --git a/shared-bindings/displayio/ColorConverter.c b/shared-bindings/displayio/ColorConverter.c index e2721cfdef244..bfd037016d41f 100644 --- a/shared-bindings/displayio/ColorConverter.c +++ b/shared-bindings/displayio/ColorConverter.c @@ -39,7 +39,6 @@ static mp_obj_t displayio_colorconverter_make_new(const mp_obj_type_t *type, siz mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); displayio_colorconverter_t *self = mp_obj_malloc(displayio_colorconverter_t, &displayio_colorconverter_type); - common_hal_displayio_colorconverter_construct(self, args[ARG_dither].u_bool, (displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_input_colorspace].u_obj, MP_QSTR_input_colorspace)); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/displayio/TileGrid.c b/shared-bindings/displayio/TileGrid.c index 560fb66a67553..5d54c1e3e306b 100644 --- a/shared-bindings/displayio/TileGrid.c +++ b/shared-bindings/displayio/TileGrid.c @@ -133,6 +133,7 @@ static mp_obj_t displayio_tilegrid_make_new(const mp_obj_type_t *type, size_t n_ bitmap_width / tile_width, bitmap_height / tile_height, pixel_shader, args[ARG_width].u_int, args[ARG_height].u_int, tile_width, tile_height, x, y, args[ARG_default_tile].u_int); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/gnss/GNSS.c b/shared-bindings/gnss/GNSS.c index 95c3ed975d501..3df23979c9ee1 100644 --- a/shared-bindings/gnss/GNSS.c +++ b/shared-bindings/gnss/GNSS.c @@ -38,7 +38,6 @@ //| ... //| static mp_obj_t gnss_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - gnss_obj_t *self = mp_obj_malloc(gnss_obj_t, &gnss_type); enum { ARG_system }; static const mp_arg_t allowed_args[] = { { MP_QSTR_system, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -63,7 +62,9 @@ static mp_obj_t gnss_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_raise_TypeError(MP_ERROR_TEXT("System entry must be gnss.SatelliteSystem")); } + gnss_obj_t *self = mp_obj_malloc(gnss_obj_t, &gnss_type); common_hal_gnss_construct(self, selection); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/i2cioexpander/IOExpander.c b/shared-bindings/i2cioexpander/IOExpander.c index c828c6dc5c7fd..3efa0fd18685f 100644 --- a/shared-bindings/i2cioexpander/IOExpander.c +++ b/shared-bindings/i2cioexpander/IOExpander.c @@ -72,8 +72,6 @@ static mp_obj_t i2cioexpander_ioexpander_make_new(const mp_obj_type_t *type, siz mp_raise_ValueError(MP_ERROR_TEXT("num_pins must be 8 or 16")); } - i2cioexpander_ioexpander_obj_t *self = mp_obj_malloc(i2cioexpander_ioexpander_obj_t, &i2cioexpander_ioexpander_type); - // Convert and validate register parameters uint16_t set_value_reg = NO_REGISTER; if (args[ARG_set_value_reg].u_obj != mp_const_none) { @@ -96,6 +94,8 @@ static mp_obj_t i2cioexpander_ioexpander_make_new(const mp_obj_type_t *type, siz set_direction_reg = reg; } + i2cioexpander_ioexpander_obj_t *self = + mp_obj_malloc(i2cioexpander_ioexpander_obj_t, &i2cioexpander_ioexpander_type); common_hal_i2cioexpander_ioexpander_construct( self, i2c, diff --git a/shared-bindings/i2ctarget/I2CTarget.c b/shared-bindings/i2ctarget/I2CTarget.c index 8586d1843cc77..94dbf4e8be4f6 100644 --- a/shared-bindings/i2ctarget/I2CTarget.c +++ b/shared-bindings/i2ctarget/I2CTarget.c @@ -26,7 +26,8 @@ static mp_obj_t mp_obj_new_i2ctarget_i2c_target_request(i2ctarget_i2c_target_obj self->address = address; self->is_read = is_read; self->is_restart = is_restart; - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } //| class I2CTarget: @@ -50,7 +51,6 @@ static mp_obj_t mp_obj_new_i2ctarget_i2c_target_request(i2ctarget_i2c_target_obj //| ... //| static mp_obj_t i2ctarget_i2c_target_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - i2ctarget_i2c_target_obj_t *self = mp_obj_malloc_with_finaliser(i2ctarget_i2c_target_obj_t, &i2ctarget_i2c_target_type); enum { ARG_scl, ARG_sda, ARG_addresses, ARG_smbus }; static const mp_arg_t allowed_args[] = { { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -78,8 +78,10 @@ static mp_obj_t i2ctarget_i2c_target_make_new(const mp_obj_type_t *type, size_t mp_raise_ValueError(MP_ERROR_TEXT("addresses is empty")); } + i2ctarget_i2c_target_obj_t *self = mp_obj_malloc_with_finaliser(i2ctarget_i2c_target_obj_t, &i2ctarget_i2c_target_type); common_hal_i2ctarget_i2c_target_construct(self, scl, sda, addresses, i, args[ARG_smbus].u_bool); - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: diff --git a/shared-bindings/imagecapture/ParallelImageCapture.c b/shared-bindings/imagecapture/ParallelImageCapture.c index 866edb489ce3e..427eaeb379373 100644 --- a/shared-bindings/imagecapture/ParallelImageCapture.c +++ b/shared-bindings/imagecapture/ParallelImageCapture.c @@ -58,7 +58,6 @@ static mp_obj_t imagecapture_parallelimagecapture_make_new(const mp_obj_type_t * imagecapture_parallelimagecapture_obj_t *self = mp_obj_malloc(imagecapture_parallelimagecapture_obj_t, &imagecapture_parallelimagecapture_type); - common_hal_imagecapture_parallelimagecapture_construct(self, pins, pin_count, clock, vsync, href); return self; diff --git a/shared-bindings/ipaddress/IPv4Address.c b/shared-bindings/ipaddress/IPv4Address.c index 5aab02e9f5cce..7705e500e7e6a 100644 --- a/shared-bindings/ipaddress/IPv4Address.c +++ b/shared-bindings/ipaddress/IPv4Address.c @@ -59,7 +59,6 @@ static mp_obj_t ipaddress_ipv4address_make_new(const mp_obj_type_t *type, size_t ipaddress_ipv4address_obj_t *self = mp_obj_malloc(ipaddress_ipv4address_obj_t, &ipaddress_ipv4address_type); - common_hal_ipaddress_ipv4address_construct(self, buf, 4); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/is31fl3741/IS31FL3741.c b/shared-bindings/is31fl3741/IS31FL3741.c index 0c6b3479b047c..71c4437a1d23f 100644 --- a/shared-bindings/is31fl3741/IS31FL3741.c +++ b/shared-bindings/is31fl3741/IS31FL3741.c @@ -38,7 +38,6 @@ static mp_obj_t is31fl3741_IS31FL3741_make_new(const mp_obj_type_t *type, size_t mp_obj_t i2c = mp_arg_validate_type(args[ARG_i2c].u_obj, &busio_i2c_type, MP_QSTR_i2c_bus); is31fl3741_IS31FL3741_obj_t *self = mp_obj_malloc(is31fl3741_IS31FL3741_obj_t, &is31fl3741_IS31FL3741_type); - common_hal_is31fl3741_IS31FL3741_construct(self, MP_OBJ_TO_PTR(i2c), args[ARG_addr].u_int diff --git a/shared-bindings/jpegio/JpegDecoder.c b/shared-bindings/jpegio/JpegDecoder.c index 13287a23650cc..ad2a0622e7a61 100644 --- a/shared-bindings/jpegio/JpegDecoder.c +++ b/shared-bindings/jpegio/JpegDecoder.c @@ -43,7 +43,6 @@ static mp_obj_t jpegio_jpegdecoder_make_new(const mp_obj_type_t *type, size_t n_ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); jpegio_jpegdecoder_obj_t *self = mp_obj_malloc(jpegio_jpegdecoder_obj_t, &jpegio_jpegdecoder_type); - self->base.type = &jpegio_jpegdecoder_type; common_hal_jpegio_jpegdecoder_construct(self); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/keypad/Event.c b/shared-bindings/keypad/Event.c index 51e446e8362d7..f83e01e785e6a 100644 --- a/shared-bindings/keypad/Event.c +++ b/shared-bindings/keypad/Event.c @@ -26,7 +26,6 @@ //| ... //| static mp_obj_t keypad_event_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - keypad_event_obj_t *self = mp_obj_malloc(keypad_event_obj_t, &keypad_event_type); enum { ARG_key_number, ARG_pressed, ARG_timestamp }; static const mp_arg_t allowed_args[] = { { MP_QSTR_key_number, MP_ARG_INT, {.u_int = 0} }, @@ -45,7 +44,9 @@ static mp_obj_t keypad_event_make_new(const mp_obj_type_t *type, size_t n_args, } (void)mp_obj_get_int_truncated(timestamp); // ensure that timestamp is an integer + keypad_event_obj_t *self = mp_obj_malloc(keypad_event_obj_t, &keypad_event_type); common_hal_keypad_event_construct(self, key_number, args[ARG_pressed].u_bool, timestamp); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/keypad/KeyMatrix.c b/shared-bindings/keypad/KeyMatrix.c index edb32c2c5c700..01416a2f8a190 100644 --- a/shared-bindings/keypad/KeyMatrix.c +++ b/shared-bindings/keypad/KeyMatrix.c @@ -81,7 +81,6 @@ static mp_obj_t keypad_keymatrix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_KEYPAD_KEYMATRIX - keypad_keymatrix_obj_t *self = mp_obj_malloc(keypad_keymatrix_obj_t, &keypad_keymatrix_type); enum { ARG_row_pins, ARG_column_pins, ARG_columns_to_anodes, ARG_interval, ARG_max_events, ARG_debounce_threshold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_row_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -123,7 +122,9 @@ static mp_obj_t keypad_keymatrix_make_new(const mp_obj_type_t *type, size_t n_ar column_pins_array[column] = pin; } + keypad_keymatrix_obj_t *self = mp_obj_malloc(keypad_keymatrix_obj_t, &keypad_keymatrix_type); common_hal_keypad_keymatrix_construct(self, num_row_pins, row_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, interval, max_events, debounce_threshold); + return MP_OBJ_FROM_PTR(self); #else mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_KeyMatrix); diff --git a/shared-bindings/keypad/Keys.c b/shared-bindings/keypad/Keys.c index 64e8e51a67ded..5fd065fc6271c 100644 --- a/shared-bindings/keypad/Keys.c +++ b/shared-bindings/keypad/Keys.c @@ -81,7 +81,6 @@ static mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_KEYPAD_KEYS - keypad_keys_obj_t *self = mp_obj_malloc(keypad_keys_obj_t, &keypad_keys_type); enum { ARG_pins, ARG_value_when_pressed, ARG_pull, ARG_interval, ARG_max_events, ARG_debounce_threshold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -112,6 +111,7 @@ static mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, s validate_obj_is_free_pin(mp_obj_subscr(pins, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL), MP_QSTR_pin); } + keypad_keys_obj_t *self = mp_obj_malloc(keypad_keys_obj_t, &keypad_keys_type); common_hal_keypad_keys_construct(self, num_pins, pins_array, value_when_pressed, args[ARG_pull].u_bool, interval, max_events, debounce_threshold); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/keypad/ShiftRegisterKeys.c b/shared-bindings/keypad/ShiftRegisterKeys.c index 1347735b5d2f8..7f89a11ecf169 100644 --- a/shared-bindings/keypad/ShiftRegisterKeys.c +++ b/shared-bindings/keypad/ShiftRegisterKeys.c @@ -88,8 +88,6 @@ static mp_obj_t keypad_shiftregisterkeys_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS - keypad_shiftregisterkeys_obj_t *self = - mp_obj_malloc(keypad_shiftregisterkeys_obj_t, &keypad_shiftregisterkeys_type); enum { ARG_clock, ARG_data, ARG_latch, ARG_value_to_latch, ARG_key_count, ARG_value_when_pressed, ARG_interval, ARG_max_events, ARG_debounce_threshold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -160,6 +158,8 @@ static mp_obj_t keypad_shiftregisterkeys_make_new(const mp_obj_type_t *type, siz const size_t max_events = (size_t)mp_arg_validate_int_min(args[ARG_max_events].u_int, 1, MP_QSTR_max_events); const uint8_t debounce_threshold = (uint8_t)mp_arg_validate_int_range(args[ARG_debounce_threshold].u_int, 1, 127, MP_QSTR_debounce_threshold); + keypad_shiftregisterkeys_obj_t *self = + mp_obj_malloc(keypad_shiftregisterkeys_obj_t, &keypad_shiftregisterkeys_type); common_hal_keypad_shiftregisterkeys_construct( self, clock, num_data_pins, data_pins_array, latch, value_to_latch, num_key_counts, key_count_array, value_when_pressed, interval, max_events, debounce_threshold); diff --git a/shared-bindings/keypad_demux/DemuxKeyMatrix.c b/shared-bindings/keypad_demux/DemuxKeyMatrix.c index f1b5ecae9ceb6..554e461c2289b 100644 --- a/shared-bindings/keypad_demux/DemuxKeyMatrix.c +++ b/shared-bindings/keypad_demux/DemuxKeyMatrix.c @@ -80,7 +80,6 @@ //| static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - keypad_demux_demuxkeymatrix_obj_t *self = mp_obj_malloc(keypad_demux_demuxkeymatrix_obj_t, &keypad_demux_demuxkeymatrix_type); enum { ARG_row_addr_pins, ARG_column_pins, ARG_columns_to_anodes, ARG_transpose, ARG_interval, ARG_max_events, ARG_debounce_threshold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_row_addr_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -123,8 +122,10 @@ static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type, column_pins_array[column] = pin; } + keypad_demux_demuxkeymatrix_obj_t *self = mp_obj_malloc(keypad_demux_demuxkeymatrix_obj_t, &keypad_demux_demuxkeymatrix_type); // Last arg is use_gc_allocator, true during VM use. common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, args[ARG_transpose].u_bool, interval, max_events, debounce_threshold, true); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/memorymap/AddressRange.c b/shared-bindings/memorymap/AddressRange.c index efc55ad37d775..5f7ee0deb9554 100644 --- a/shared-bindings/memorymap/AddressRange.c +++ b/shared-bindings/memorymap/AddressRange.c @@ -92,7 +92,6 @@ static mp_obj_t memorymap_addressrange_make_new(const mp_obj_type_t *type, size_ } memorymap_addressrange_obj_t *self = mp_obj_malloc(memorymap_addressrange_obj_t, &memorymap_addressrange_type); - common_hal_memorymap_addressrange_construct(self, (uint8_t *)start, length); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/memorymonitor/AllocationAlarm.c b/shared-bindings/memorymonitor/AllocationAlarm.c index 5475e2d7db411..00cfbbd9fe194 100644 --- a/shared-bindings/memorymonitor/AllocationAlarm.c +++ b/shared-bindings/memorymonitor/AllocationAlarm.c @@ -48,7 +48,6 @@ static mp_obj_t memorymonitor_allocationalarm_make_new(const mp_obj_type_t *type memorymonitor_allocationalarm_obj_t *self = mp_obj_malloc(memorymonitor_allocationalarm_obj_t, &memorymonitor_allocationalarm_type); - common_hal_memorymonitor_allocationalarm_construct(self, minimum_block_count); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/msgpack/ExtType.c b/shared-bindings/msgpack/ExtType.c index f66abda8fa21e..c69416f9773e7 100644 --- a/shared-bindings/msgpack/ExtType.c +++ b/shared-bindings/msgpack/ExtType.c @@ -18,7 +18,6 @@ //| :param bytes data: representation.""" //| static mp_obj_t mod_msgpack_exttype_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - mod_msgpack_extype_obj_t *self = mp_obj_malloc(mod_msgpack_extype_obj_t, &mod_msgpack_exttype_type); enum { ARG_code, ARG_data }; static const mp_arg_t allowed_args[] = { { MP_QSTR_code, MP_ARG_INT | MP_ARG_REQUIRED }, @@ -28,11 +27,12 @@ static mp_obj_t mod_msgpack_exttype_make_new(const mp_obj_type_t *type, size_t n mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); int code = mp_arg_validate_int_range(args[ARG_code].u_int, 0, 127, MP_QSTR_code); + mp_obj_t data = args[ARG_data].u_obj; + mod_msgpack_extype_obj_t *self = mp_obj_malloc(mod_msgpack_extype_obj_t, &mod_msgpack_exttype_type); self->code = code; - - mp_obj_t data = args[ARG_data].u_obj; self->data = data; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/onewireio/OneWire.c b/shared-bindings/onewireio/OneWire.c index a3750ae5e074d..2f69424cc234b 100644 --- a/shared-bindings/onewireio/OneWire.c +++ b/shared-bindings/onewireio/OneWire.c @@ -43,8 +43,8 @@ static mp_obj_t onewireio_onewire_make_new(const mp_obj_type_t *type, size_t n_a const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); onewireio_onewire_obj_t *self = mp_obj_malloc(onewireio_onewire_obj_t, &onewireio_onewire_type); - common_hal_onewireio_onewire_construct(self, pin); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/os/__init__.c b/shared-bindings/os/__init__.c index 5e28c02452d05..678b444de6401 100644 --- a/shared-bindings/os/__init__.c +++ b/shared-bindings/os/__init__.c @@ -67,7 +67,7 @@ static MP_DEFINE_ATTRTUPLE( ); static mp_obj_t os_uname(void) { - return (mp_obj_t)&os_uname_info_obj; + return MP_OBJ_FROM_PTR(&os_uname_info_obj); } static MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); diff --git a/shared-bindings/ps2io/Ps2.c b/shared-bindings/ps2io/Ps2.c index 8766ea7a2a11b..90e6b0b604561 100644 --- a/shared-bindings/ps2io/Ps2.c +++ b/shared-bindings/ps2io/Ps2.c @@ -58,7 +58,6 @@ static mp_obj_t ps2io_ps2_make_new(const mp_obj_type_t *type, size_t n_args, siz const mcu_pin_obj_t *data_pin = validate_obj_is_free_pin(args[ARG_data_pin].u_obj, MP_QSTR_data_pin); ps2io_ps2_obj_t *self = mp_obj_malloc(ps2io_ps2_obj_t, &ps2io_ps2_type); - common_hal_ps2io_ps2_construct(self, data_pin, clock_pin); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/pulseio/PulseIn.c b/shared-bindings/pulseio/PulseIn.c index d9e6bfd7aecc3..830f7b7c62ce3 100644 --- a/shared-bindings/pulseio/PulseIn.c +++ b/shared-bindings/pulseio/PulseIn.c @@ -69,7 +69,6 @@ static mp_obj_t pulseio_pulsein_make_new(const mp_obj_type_t *type, size_t n_arg const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); pulseio_pulsein_obj_t *self = mp_obj_malloc_with_finaliser(pulseio_pulsein_obj_t, &pulseio_pulsein_type); - common_hal_pulseio_pulsein_construct(self, pin, args[ARG_maxlen].u_int, args[ARG_idle_state].u_bool); diff --git a/shared-bindings/pulseio/PulseOut.c b/shared-bindings/pulseio/PulseOut.c index 947aa4b518ec8..58c98b8e2cce8 100644 --- a/shared-bindings/pulseio/PulseOut.c +++ b/shared-bindings/pulseio/PulseOut.c @@ -62,6 +62,7 @@ static mp_obj_t pulseio_pulseout_make_new(const mp_obj_type_t *type, size_t n_ar pulseio_pulseout_obj_t *self = mp_obj_malloc_with_finaliser(pulseio_pulseout_obj_t, &pulseio_pulseout_type); common_hal_pulseio_pulseout_construct(self, pin, frequency, duty_cycle); + return MP_OBJ_FROM_PTR(self); #else mp_raise_NotImplementedError(NULL); diff --git a/shared-bindings/qrio/QRDecoder.c b/shared-bindings/qrio/QRDecoder.c index 6a36d8b026582..e8977bc1efee3 100644 --- a/shared-bindings/qrio/QRDecoder.c +++ b/shared-bindings/qrio/QRDecoder.c @@ -34,7 +34,7 @@ static mp_obj_t qrio_qrdecoder_make_new(const mp_obj_type_t *type, size_t n_args qrio_qrdecoder_obj_t *self = mp_obj_malloc(qrio_qrdecoder_obj_t, &qrio_qrdecoder_type_obj); shared_module_qrio_qrdecoder_construct(self, args[ARG_width].u_int, args[ARG_height].u_int); - return self; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/rclcpy/Node.c b/shared-bindings/rclcpy/Node.c index a2fa9bd36a656..4097fbc1515f4 100644 --- a/shared-bindings/rclcpy/Node.c +++ b/shared-bindings/rclcpy/Node.c @@ -50,7 +50,8 @@ static mp_obj_t rclcpy_node_make_new(const mp_obj_type_t *type, size_t n_args, s rclcpy_node_obj_t *self = mp_obj_malloc_with_finaliser(rclcpy_node_obj_t, &rclcpy_node_type); common_hal_rclcpy_node_construct(self, node_name, namespace); - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: @@ -90,7 +91,7 @@ static mp_obj_t rclcpy_node_create_publisher(mp_obj_t self_in, mp_obj_t topic) { rclcpy_publisher_obj_t *publisher = mp_obj_malloc_with_finaliser(rclcpy_publisher_obj_t, &rclcpy_publisher_type); common_hal_rclcpy_publisher_construct(publisher, self, topic_name); - return (mp_obj_t)publisher; + return MP_OBJ_FROM_PTR(publisher); } static MP_DEFINE_CONST_FUN_OBJ_2(rclcpy_node_create_publisher_obj, rclcpy_node_create_publisher); diff --git a/shared-bindings/rclcpy/__init__.c b/shared-bindings/rclcpy/__init__.c index a6631642ea7c0..e195294cde61b 100644 --- a/shared-bindings/rclcpy/__init__.c +++ b/shared-bindings/rclcpy/__init__.c @@ -112,7 +112,8 @@ static mp_obj_t rclcpy_create_node(size_t n_args, const mp_obj_t *pos_args, mp_m rclcpy_node_obj_t *self = mp_obj_malloc_with_finaliser(rclcpy_node_obj_t, &rclcpy_node_type); common_hal_rclcpy_node_construct(self, node_name, namespace); - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } static MP_DEFINE_CONST_FUN_OBJ_KW(rclcpy_create_node_obj, 2, rclcpy_create_node); diff --git a/shared-bindings/sdcardio/SDCard.c b/shared-bindings/sdcardio/SDCard.c index 65c5f80b52b02..2802499956c36 100644 --- a/shared-bindings/sdcardio/SDCard.c +++ b/shared-bindings/sdcardio/SDCard.c @@ -82,10 +82,9 @@ static mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_arg const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); sdcardio_sdcard_obj_t *self = mp_obj_malloc_with_finaliser(sdcardio_sdcard_obj_t, &sdcardio_SDCard_type); - common_hal_sdcardio_sdcard_construct(self, spi, cs, args[ARG_baudrate].u_int); - return self; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/sdioio/SDCard.c b/shared-bindings/sdioio/SDCard.c index 892cecd3fb4af..baf1e1660e845 100644 --- a/shared-bindings/sdioio/SDCard.c +++ b/shared-bindings/sdioio/SDCard.c @@ -67,7 +67,6 @@ //| static mp_obj_t sdioio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - sdioio_sdcard_obj_t *self = mp_obj_malloc(sdioio_sdcard_obj_t, &sdioio_SDCard_type); enum { ARG_clock, ARG_command, ARG_data, ARG_frequency, NUM_ARGS }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ }, @@ -86,6 +85,7 @@ static mp_obj_t sdioio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, uint8_t num_data; validate_list_is_free_pins(MP_QSTR_data, data_pins, MP_ARRAY_SIZE(data_pins), args[ARG_data].u_obj, &num_data); + sdioio_sdcard_obj_t *self = mp_obj_malloc(sdioio_sdcard_obj_t, &sdioio_SDCard_type); common_hal_sdioio_sdcard_construct(self, clock, command, num_data, data_pins, args[ARG_frequency].u_int); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/socketpool/SocketPool.c b/shared-bindings/socketpool/SocketPool.c index e139e3a077ab2..72b15ba9edd55 100644 --- a/shared-bindings/socketpool/SocketPool.c +++ b/shared-bindings/socketpool/SocketPool.c @@ -36,9 +36,9 @@ static mp_obj_t socketpool_socketpool_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - socketpool_socketpool_obj_t *s = mp_obj_malloc_with_finaliser(socketpool_socketpool_obj_t, &socketpool_socketpool_type); mp_obj_t radio = args[0]; + socketpool_socketpool_obj_t *s = mp_obj_malloc_with_finaliser(socketpool_socketpool_obj_t, &socketpool_socketpool_type); common_hal_socketpool_socketpool_construct(s, radio); return MP_OBJ_FROM_PTR(s); diff --git a/shared-bindings/spitarget/SPITarget.c b/shared-bindings/spitarget/SPITarget.c index eca92d800277b..08f6d554f3a45 100644 --- a/shared-bindings/spitarget/SPITarget.c +++ b/shared-bindings/spitarget/SPITarget.c @@ -33,7 +33,6 @@ //| ... //| static mp_obj_t spitarget_spi_target_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - spitarget_spi_target_obj_t *self = mp_obj_malloc(spitarget_spi_target_obj_t, &spitarget_spi_target_type); enum { ARG_sck, ARG_mosi, ARG_miso, ARG_ss }; static const mp_arg_t allowed_args[] = { { MP_QSTR_sck, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -49,7 +48,9 @@ static mp_obj_t spitarget_spi_target_make_new(const mp_obj_type_t *type, size_t const mcu_pin_obj_t *miso = validate_obj_is_free_pin(args[ARG_miso].u_obj, MP_QSTR_miso); const mcu_pin_obj_t *ss = validate_obj_is_free_pin(args[ARG_ss].u_obj, MP_QSTR_ss); + spitarget_spi_target_obj_t *self = mp_obj_malloc(spitarget_spi_target_obj_t, &spitarget_spi_target_type); common_hal_spitarget_spi_target_construct(self, sck, mosi, miso, ss); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/ssl/SSLContext.c b/shared-bindings/ssl/SSLContext.c index 078a716cdb801..9546c50ed7c41 100644 --- a/shared-bindings/ssl/SSLContext.c +++ b/shared-bindings/ssl/SSLContext.c @@ -26,7 +26,6 @@ static mp_obj_t ssl_sslcontext_make_new(const mp_obj_type_t *type, size_t n_args mp_arg_check_num(n_args, n_kw, 0, 1, false); ssl_sslcontext_obj_t *s = mp_obj_malloc(ssl_sslcontext_obj_t, &ssl_sslcontext_type); - common_hal_ssl_sslcontext_construct(s); return MP_OBJ_FROM_PTR(s); diff --git a/shared-bindings/ssl/__init__.c b/shared-bindings/ssl/__init__.c index 0d10090e9cb99..51a3ad3cfc42a 100644 --- a/shared-bindings/ssl/__init__.c +++ b/shared-bindings/ssl/__init__.c @@ -27,9 +27,9 @@ static mp_obj_t ssl_create_default_context(void) { ssl_sslcontext_obj_t *s = mp_obj_malloc(ssl_sslcontext_obj_t, &ssl_sslcontext_type); - common_hal_ssl_create_default_context(s); - return s; + + return MP_OBJ_FROM_PTR(s); } MP_DEFINE_CONST_FUN_OBJ_0(ssl_create_default_context_obj, ssl_create_default_context); diff --git a/shared-bindings/synthio/LFO.c b/shared-bindings/synthio/LFO.c index ee2d67d308903..2dfae22a57774 100644 --- a/shared-bindings/synthio/LFO.c +++ b/shared-bindings/synthio/LFO.c @@ -103,17 +103,17 @@ static mp_obj_t synthio_lfo_make_new(const mp_obj_type_t *type_in, size_t n_args } self->waveform_obj = args[ARG_waveform].u_obj; - mp_obj_t result = MP_OBJ_FROM_PTR(self); - properties_construct_helper(result, lfo_properties + 1, args + 1, MP_ARRAY_SIZE(lfo_properties) - 1); + mp_obj_t self_obj = MP_OBJ_FROM_PTR(self); + properties_construct_helper(self_obj, lfo_properties + 1, args + 1, MP_ARRAY_SIZE(lfo_properties) - 1); // Force computation of the LFO's initial output synthio_global_rate_scale = 0; self->base.last_tick = synthio_global_tick - 1; synthio_block_slot_t slot; - synthio_block_assign_slot(MP_OBJ_FROM_PTR(result), &slot, MP_QSTR_self); + synthio_block_assign_slot(self_obj, &slot, MP_QSTR_self); (void)synthio_block_slot_get(&slot); - return result; + return self_obj; }; //| waveform: Optional[ReadableBuffer] diff --git a/shared-bindings/synthio/Math.c b/shared-bindings/synthio/Math.c index 5e943b44d0fcc..96857fb351373 100644 --- a/shared-bindings/synthio/Math.c +++ b/shared-bindings/synthio/Math.c @@ -157,10 +157,10 @@ static mp_obj_t synthio_math_make_new_common(mp_arg_val_t args[MP_ARRAY_SIZE(mat self->base.last_tick = synthio_global_tick; - mp_obj_t result = MP_OBJ_FROM_PTR(self); - properties_construct_helper(result, math_properties, args, MP_ARRAY_SIZE(math_properties)); + mp_obj_t self_obj = MP_OBJ_FROM_PTR(self); + properties_construct_helper(self_obj, math_properties, args, MP_ARRAY_SIZE(math_properties)); - return result; + return self_obj; }; //| a: BlockInput diff --git a/shared-bindings/synthio/MidiTrack.c b/shared-bindings/synthio/MidiTrack.c index db75a543c613d..0304df12a8191 100644 --- a/shared-bindings/synthio/MidiTrack.c +++ b/shared-bindings/synthio/MidiTrack.c @@ -70,7 +70,6 @@ static mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_a mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); synthio_miditrack_obj_t *self = mp_obj_malloc(synthio_miditrack_obj_t, &synthio_miditrack_type); - common_hal_synthio_miditrack_construct(self, (uint8_t *)bufinfo.buf, bufinfo.len, args[ARG_tempo].u_int, diff --git a/shared-bindings/synthio/Note.c b/shared-bindings/synthio/Note.c index 95dd51fe6b0fc..183c59d5fbd32 100644 --- a/shared-bindings/synthio/Note.c +++ b/shared-bindings/synthio/Note.c @@ -61,11 +61,11 @@ static mp_obj_t synthio_note_make_new(const mp_obj_type_t *type_in, size_t n_arg mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(note_properties), note_properties, args); synthio_note_obj_t *self = mp_obj_malloc(synthio_note_obj_t, &synthio_note_type); + mp_obj_t self_obj = MP_OBJ_FROM_PTR(self); - mp_obj_t result = MP_OBJ_FROM_PTR(self); - properties_construct_helper(result, note_properties, args, MP_ARRAY_SIZE(note_properties)); + properties_construct_helper(self_obj, note_properties, args, MP_ARRAY_SIZE(note_properties)); - return result; + return self_obj; }; //| frequency: float diff --git a/shared-bindings/synthio/Synthesizer.c b/shared-bindings/synthio/Synthesizer.c index fb39e8ef50ede..35cc42a20371f 100644 --- a/shared-bindings/synthio/Synthesizer.c +++ b/shared-bindings/synthio/Synthesizer.c @@ -62,7 +62,6 @@ static mp_obj_t synthio_synthesizer_make_new(const mp_obj_type_t *type, size_t n mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); synthio_synthesizer_obj_t *self = mp_obj_malloc(synthio_synthesizer_obj_t, &synthio_synthesizer_type); - common_hal_synthio_synthesizer_construct(self, args[ARG_sample_rate].u_int, args[ARG_channel_count].u_int, diff --git a/shared-bindings/synthio/__init__.c b/shared-bindings/synthio/__init__.c index 6d42880541df4..6a21834751b63 100644 --- a/shared-bindings/synthio/__init__.c +++ b/shared-bindings/synthio/__init__.c @@ -252,7 +252,6 @@ static mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma } synthio_miditrack_obj_t *result = mp_obj_malloc(synthio_miditrack_obj_t, &synthio_miditrack_type); - common_hal_synthio_miditrack_construct(result, buffer, track_size, tempo, args[ARG_sample_rate].u_int, args[ARG_waveform].u_obj, mp_const_none, diff --git a/shared-bindings/terminalio/Terminal.c b/shared-bindings/terminalio/Terminal.c index 199128e56e925..72c43ba2dc152 100644 --- a/shared-bindings/terminalio/Terminal.c +++ b/shared-bindings/terminalio/Terminal.c @@ -176,8 +176,8 @@ static mp_obj_t terminalio_terminal_make_new(const mp_obj_type_t *type, size_t n mp_arg_validate_int_min(scroll_area->width_in_tiles * scroll_area->height_in_tiles, 2, MP_QSTR_scroll_area_area); terminalio_terminal_obj_t *self = mp_obj_malloc(terminalio_terminal_obj_t, &terminalio_terminal_type); - common_hal_terminalio_terminal_construct(self, scroll_area, font, status_bar); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/tilepalettemapper/TilePaletteMapper.c b/shared-bindings/tilepalettemapper/TilePaletteMapper.c index ed3e928957f9f..c22ea47bf2652 100644 --- a/shared-bindings/tilepalettemapper/TilePaletteMapper.c +++ b/shared-bindings/tilepalettemapper/TilePaletteMapper.c @@ -45,7 +45,6 @@ static mp_obj_t tilepalettemapper_tilepalettemapper_make_new(const mp_obj_type_t mp_raise_TypeError_varg(MP_ERROR_TEXT("unsupported %q type"), MP_QSTR_pixel_shader); } - tilepalettemapper_tilepalettemapper_t *self = mp_obj_malloc(tilepalettemapper_tilepalettemapper_t, &tilepalettemapper_tilepalettemapper_type); common_hal_tilepalettemapper_tilepalettemapper_construct(self, pixel_shader, args[ARG_input_color_count].u_int); diff --git a/shared-bindings/touchio/TouchIn.c b/shared-bindings/touchio/TouchIn.c index 19d97bd9d7c56..2250e88f599f1 100644 --- a/shared-bindings/touchio/TouchIn.c +++ b/shared-bindings/touchio/TouchIn.c @@ -56,7 +56,7 @@ static mp_obj_t touchio_touchin_make_new(const mp_obj_type_t *type, touchio_touchin_obj_t *self = mp_obj_malloc(touchio_touchin_obj_t, &touchio_touchin_type); common_hal_touchio_touchin_construct(self, pin, pull); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: diff --git a/shared-bindings/usb/core/__init__.c b/shared-bindings/usb/core/__init__.c index 545e182ea99a3..3907effb8ff25 100644 --- a/shared-bindings/usb/core/__init__.c +++ b/shared-bindings/usb/core/__init__.c @@ -90,9 +90,10 @@ static mp_obj_t _next_device(usb_core_devices_obj_t *iter) { // We passed the filters. Now make a properly allocated object to // return to the user. usb_core_device_obj_t *self = mp_obj_malloc(usb_core_device_obj_t, &usb_core_device_type); - common_hal_usb_core_device_construct(self, i); + iter->next_index = i + 1; + return MP_OBJ_FROM_PTR(self); } // Iter is done. diff --git a/shared-bindings/usb_hid/Device.c b/shared-bindings/usb_hid/Device.c index a4a0e8680e531..66c9c01ad6e43 100644 --- a/shared-bindings/usb_hid/Device.c +++ b/shared-bindings/usb_hid/Device.c @@ -75,7 +75,6 @@ //| static mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - usb_hid_device_obj_t *self = mp_obj_malloc(usb_hid_device_obj_t, &usb_hid_device_type); enum { ARG_report_descriptor, ARG_usage_page, ARG_usage, ARG_report_ids, ARG_in_report_lengths, ARG_out_report_lengths }; static const mp_arg_t allowed_args[] = { { MP_QSTR_report_descriptor, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -141,9 +140,10 @@ static mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args mp_raise_ValueError_varg(MP_ERROR_TEXT("%q length must be %d"), MP_QSTR_report_id_space_0, 1); } + usb_hid_device_obj_t *self = mp_obj_malloc(usb_hid_device_obj_t, &usb_hid_device_type); common_hal_usb_hid_device_construct( self, descriptor, usage_page, usage, report_ids_count, report_ids_array, in_report_lengths_array, out_report_lengths_array); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/usb_host/Port.c b/shared-bindings/usb_host/Port.c index 41e8513a1e8b2..2c61e641a05be 100644 --- a/shared-bindings/usb_host/Port.c +++ b/shared-bindings/usb_host/Port.c @@ -42,7 +42,7 @@ static mp_obj_t usb_host_port_make_new(const mp_obj_type_t *type, usb_host_port_obj_t *self = common_hal_usb_host_port_construct(dp, dm); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } static const mp_rom_map_elem_t usb_host_port_locals_dict_table[] = { diff --git a/shared-bindings/vectorio/VectorShape.c b/shared-bindings/vectorio/VectorShape.c index ff29609fc4e91..834f1fbff5083 100644 --- a/shared-bindings/vectorio/VectorShape.c +++ b/shared-bindings/vectorio/VectorShape.c @@ -69,7 +69,7 @@ mp_obj_t vectorio_vector_shape_make_new(const mp_obj_t shape, const mp_obj_t pix } else if (mp_obj_is_type(shape, &vectorio_circle_type)) { common_hal_vectorio_circle_set_on_dirty(self->ishape.shape, on_dirty); } else { - mp_raise_TypeError_varg(MP_ERROR_TEXT("unsupported %q type"), MP_QSTR_shape); + // Already excluded due to previous else-if chain. } return MP_OBJ_FROM_PTR(self); From 2fac7c9572ffac3eb156f33028685887b2e5b975 Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Fri, 6 Mar 2026 11:49:17 +0100 Subject: [PATCH 414/517] Review fixes --- shared-module/displayio/bus_core.c | 36 +++++++++--------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/shared-module/displayio/bus_core.c b/shared-module/displayio/bus_core.c index df6c5d36f4b2b..6bba0065ad9aa 100644 --- a/shared-module/displayio/bus_core.c +++ b/shared-module/displayio/bus_core.c @@ -117,7 +117,7 @@ void displayio_display_bus_end_transaction(displayio_display_bus_t *self) { self->end_transaction(self->bus); } -static void _displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area, bool manage_transactions) { +static void _displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { uint16_t x1 = area->x1 + self->colstart; uint16_t x2 = area->x2 + self->colstart; uint16_t y1 = area->y1 + self->rowstart; @@ -144,9 +144,7 @@ static void _displayio_display_bus_send_region_commands(displayio_display_bus_t } // Set column. - if (manage_transactions) { - displayio_display_bus_begin_transaction(self); - } + displayio_display_bus_begin_transaction(self); uint8_t data[5]; data[0] = self->column_command; uint8_t data_length = 1; @@ -181,28 +179,20 @@ static void _displayio_display_bus_send_region_commands(displayio_display_bus_t } self->send(self->bus, data_type, chip_select, data, data_length); - if (manage_transactions) { - displayio_display_bus_end_transaction(self); - } + displayio_display_bus_end_transaction(self); if (self->set_current_column_command != NO_COMMAND) { uint8_t command = self->set_current_column_command; - if (manage_transactions) { - displayio_display_bus_begin_transaction(self); - } + displayio_display_bus_begin_transaction(self); self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); // Only send the first half of data because it is the first coordinate. self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); - if (manage_transactions) { - displayio_display_bus_end_transaction(self); - } + displayio_display_bus_end_transaction(self); } // Set row. - if (manage_transactions) { - displayio_display_bus_begin_transaction(self); - } + displayio_display_bus_begin_transaction(self); data[0] = self->row_command; data_length = 1; if (!self->data_as_commands) { @@ -233,26 +223,20 @@ static void _displayio_display_bus_send_region_commands(displayio_display_bus_t } self->send(self->bus, data_type, chip_select, data, data_length); - if (manage_transactions) { - displayio_display_bus_end_transaction(self); - } + displayio_display_bus_end_transaction(self); if (self->set_current_row_command != NO_COMMAND) { uint8_t command = self->set_current_row_command; - if (manage_transactions) { - displayio_display_bus_begin_transaction(self); - } + displayio_display_bus_begin_transaction(self); self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); // Only send the first half of data because it is the first coordinate. self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); - if (manage_transactions) { - displayio_display_bus_end_transaction(self); - } + displayio_display_bus_end_transaction(self); } } void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { - _displayio_display_bus_send_region_commands(self, display, area, true); + _displayio_display_bus_send_region_commands(self, display, area); } void displayio_display_bus_flush(displayio_display_bus_t *self) { From a2d29c55085ab7157c26298a550b2384d546db88 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 6 Mar 2026 13:07:59 -0500 Subject: [PATCH 415/517] atmel-samd SPI: don't mark_deinit before resetting pin --- ports/atmel-samd/common-hal/busio/SPI.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index fe53580afdf23..19fd81e425d3e 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -194,14 +194,14 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { } allow_reset_sercom(self->spi_desc.dev.prvt); - // Mark as deinit early in case we are used in an interrupt. - common_hal_busio_spi_mark_deinit(self); - spi_m_sync_disable(&self->spi_desc); spi_m_sync_deinit(&self->spi_desc); reset_pin_number(self->clock_pin); reset_pin_number(self->MOSI_pin); reset_pin_number(self->MISO_pin); + + // This smashes self->clock_pin, so don't do it before resetting the pin above. + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, From c2ab75323aeefe98c508922f63f6c64b885c2755 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 8 Mar 2026 19:07:26 -0400 Subject: [PATCH 416/517] shared-bindings/busdisplay/BusDisplay.c: remove obsolete set_vertical_scroll arg Fixes #5189. --- shared-bindings/busdisplay/BusDisplay.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shared-bindings/busdisplay/BusDisplay.c b/shared-bindings/busdisplay/BusDisplay.c index 297a869057700..0bf171a5d089c 100644 --- a/shared-bindings/busdisplay/BusDisplay.c +++ b/shared-bindings/busdisplay/BusDisplay.c @@ -122,7 +122,6 @@ //| :param int native_frames_per_second: Number of display refreshes per second that occur with the given init_sequence. //| :param bool backlight_on_high: If True, pulling the backlight pin high turns the backlight on. //| :param bool SH1107_addressing: Special quirk for SH1107, use upper/lower column set and page set -//| :param int set_vertical_scroll: This parameter is accepted but ignored for backwards compatibility. It will be removed in a future release. //| :param int backlight_pwm_frequency: The frequency to use to drive the PWM for backlight brightness control. Default is 50000. //| """ //| ... @@ -133,7 +132,7 @@ static mp_obj_t busdisplay_busdisplay_make_new(const mp_obj_type_t *type, size_t ARG_rotation, ARG_color_depth, ARG_grayscale, ARG_pixels_in_byte_share_row, ARG_bytes_per_cell, ARG_reverse_pixels_in_byte, ARG_reverse_bytes_in_word, ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, - ARG_set_vertical_scroll, ARG_backlight_pin, ARG_brightness_command, + ARG_backlight_pin, ARG_brightness_command, ARG_brightness, ARG_single_byte_bounds, ARG_data_as_commands, ARG_auto_refresh, ARG_native_frames_per_second, ARG_backlight_on_high, ARG_SH1107_addressing, ARG_backlight_pwm_frequency }; @@ -154,7 +153,6 @@ static mp_obj_t busdisplay_busdisplay_make_new(const mp_obj_type_t *type, size_t { MP_QSTR_set_column_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2a} }, { MP_QSTR_set_row_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2b} }, { MP_QSTR_write_ram_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2c} }, - { MP_QSTR_set_vertical_scroll, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x0} }, { MP_QSTR_backlight_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, { MP_QSTR_brightness_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_BRIGHTNESS_COMMAND} }, { MP_QSTR_brightness, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, From 113688a090d9cfa49ec4c1633e7f100defe65741 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 8 Mar 2026 19:10:28 -0400 Subject: [PATCH 417/517] espressif SPI: wait_for_lock impl specific to FreeRTOS --- ports/espressif/common-hal/busio/SPI.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ports/espressif/common-hal/busio/SPI.c b/ports/espressif/common-hal/busio/SPI.c index fc12d40fccb59..21dc8c383794e 100644 --- a/ports/espressif/common-hal/busio/SPI.c +++ b/ports/espressif/common-hal/busio/SPI.c @@ -157,11 +157,17 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self, return true; } -bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { +// Wait as long as needed for the lock. This is used by SD card access from USB. +// Overrides the default busy-wait implementation in shared-bindings/busio/SPI.c +bool common_hal_busio_spi_wait_for_lock(busio_spi_obj_t *self, uint32_t timeout_ms) { if (common_hal_busio_spi_deinited(self)) { return false; } - return xSemaphoreTake(self->mutex, 0) == pdTRUE; + return xSemaphoreTake(self->mutex, pdMS_TO_TICKS(timeout_ms)) == pdTRUE; +} + +bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { + return common_hal_busio_spi_wait_for_lock(self, 0); } bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { From 76aa0c7ea24464fa15f8fc086f13d07e5df77bc9 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 8 Mar 2026 20:25:32 -0400 Subject: [PATCH 418/517] shared-module/displayio/__init__.c: fix keep_primary logic in common_hal_displayio_release_displays_impl() --- shared-module/displayio/__init__.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index e325a7857f464..c4e8a14af9f95 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -146,6 +146,9 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { displays[i].display_base.type = &mp_type_NoneType; } for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { + if (i == primary_display_number) { + continue; + } mp_const_obj_t bus_type = display_buses[i].bus_base.type; if (bus_type == NULL || bus_type == &mp_type_NoneType) { continue; @@ -193,7 +196,9 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { display_buses[i].bus_base.type = &mp_type_NoneType; } - supervisor_stop_terminal(); + if (!keep_primary) { + supervisor_stop_terminal(); + } } void common_hal_displayio_release_displays(void) { @@ -201,8 +206,9 @@ void common_hal_displayio_release_displays(void) { } void reset_displays(void) { - // In CircuitPython 10, release secondary displays before doing anything else: - // common_hal_displayio_release_displays_impl(true); + // TODO: In CircuitPython 11, uncomment the call. + // Release secondary displays. + // common_hal_displayio_release_displays_impl(/*keep_primary*/ true); // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline. for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { From 8808458a9b401711acea7a173d4d85dcc1054160 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 8 Mar 2026 20:28:24 -0400 Subject: [PATCH 419/517] add common_hal_busio_spi_wait_for_lock() --- shared-bindings/busio/SPI.c | 23 ++++++++++++++++++----- shared-bindings/busio/SPI.h | 4 ++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 132da473af94e..39cf2516d5120 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -9,16 +9,16 @@ #include +#include "py/binary.h" +#include "py/mperrno.h" +#include "py/objproperty.h" +#include "py/runtime.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/util.h" - #include "shared/runtime/buffer_helper.h" #include "shared/runtime/context_manager_helpers.h" -#include "py/binary.h" -#include "py/mperrno.h" -#include "py/objproperty.h" -#include "py/runtime.h" +#include "supervisor/shared/tick.h" //| class SPI: @@ -494,3 +494,16 @@ MP_DEFINE_CONST_OBJ_TYPE( busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj, qstr arg_name) { return mp_arg_validate_type(obj, &busio_spi_type, arg_name); } + +// Wait as long as needed for the lock. This is used by SD card access from USB. +// The default implementation is to busy-wait while running the background tasks. espressif is different. +bool common_hal_busio_spi_wait_for_lock(busio_spi_obj_t *self, uint32_t timeout_ms) { + uint64_t deadline = supervisor_ticks_ms64() + timeout_ms; + while (supervisor_ticks_ms64() < deadline) { + if (common_hal_busio_spi_try_lock(self)) { + return true; + } + RUN_BACKGROUND_TASKS; + } + return false; +} diff --git a/shared-bindings/busio/SPI.h b/shared-bindings/busio/SPI.h index 34f34c927f613..76ed697d66531 100644 --- a/shared-bindings/busio/SPI.h +++ b/shared-bindings/busio/SPI.h @@ -54,3 +54,7 @@ uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self); extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self); extern busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj_in, qstr arg_name); + +// Wait as long as needed for the lock. This is used by SD card access from USB. +// For most ports, busy-wait while running the background tasks. +MP_WEAK bool common_hal_busio_spi_wait_for_lock(busio_spi_obj_t *self, uint32_t timeout_ms); From 77e1fa3ed1a67c4e4e161dc562b5d66080596edb Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 8 Mar 2026 20:30:15 -0400 Subject: [PATCH 420/517] SDCard: do not return until readblocks(), writeblocks(), or sync() have completed --- shared-module/sdcardio/SDCard.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c index f9b937686b75f..a93736056e437 100644 --- a/shared-module/sdcardio/SDCard.c +++ b/shared-module/sdcardio/SDCard.c @@ -7,13 +7,13 @@ // This implementation largely follows the structure of adafruit_sdcard.py #include "extmod/vfs.h" -#include "esp_log.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/sdcardio/SDCard.h" #include "shared-bindings/time/__init__.h" #include "shared-bindings/util.h" #include "shared-module/sdcardio/SDCard.h" +#include "supervisor/shared/tick.h" #include "py/mperrno.h" @@ -29,6 +29,7 @@ // So let's allow a nice long time, but don't wait in a tight loop: allow background tasks to run. #define CMD_TIMEOUT_MS (500) #define TIMEOUT_MS (500) +#define SPI_TIMEOUT_MS (10000) #define R1_IDLE_STATE (1 << 0) #define R1_ILLEGAL_COMMAND (1 << 2) @@ -59,9 +60,8 @@ static bool lock_and_configure_bus(sdcardio_sdcard_obj_t *self) { if (common_hal_sdcardio_sdcard_deinited(self)) { return false; } - common_hal_sdcardio_check_for_deinit(self); - if (!common_hal_busio_spi_try_lock(self->bus)) { + if (!common_hal_busio_spi_wait_for_lock(self->bus, SPI_TIMEOUT_MS)) { return false; } @@ -268,7 +268,6 @@ static mp_rom_error_text_t init_card(sdcardio_sdcard_obj_t *self) { { bool reached_idle_state = false; for (int i = 0; i < 5; i++) { - ESP_LOGW("init_card", "loop: %d", i); // do not call cmd with wait=true, because that will return // prematurely if the idle state is not reached. we can't depend on // this when the card is not yet in SPI mode @@ -403,7 +402,7 @@ mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t st // deinit check is in lock_and_configure_bus() sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!lock_and_configure_bus(self)) { - return -MP_EAGAIN; + return -MP_ETIMEDOUT; } int r = 0; size_t buflen = 512 * nblocks; @@ -503,7 +502,7 @@ mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t s // deinit check is in lock_and_configure_bus() sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!lock_and_configure_bus(self)) { - return -MP_EAGAIN; + return -MP_ETIMEDOUT; } if (!self->in_cmd25 || start_block != self->next_block) { @@ -538,7 +537,7 @@ mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t s mp_negative_errno_t common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self) { // deinit check is in lock_and_configure_bus() if (!lock_and_configure_bus(self)) { - return -MP_EAGAIN; + return -MP_ETIMEDOUT; } int r = exit_cmd25(self); extraclock_and_unlock_bus(self); From 5cdcf88e4bef6b0c2a0a905256233e27c8c3520a Mon Sep 17 00:00:00 2001 From: "P. Patrick Socha" Date: Mon, 9 Mar 2026 18:56:10 +0100 Subject: [PATCH 421/517] Fixes after the review --- shared-module/busdisplay/BusDisplay.c | 4 ++-- shared-module/displayio/bus_core.c | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index 3626d2d2c3e6c..5d8d396cbb46f 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -214,7 +214,7 @@ static void _send_pixels(busdisplay_busdisplay_obj_t *self, uint8_t *pixels, uin } static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_area_t *area) { - uint32_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); // In uint32_ts + uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); // In uint32_ts displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. @@ -223,7 +223,7 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } uint16_t rows_per_buffer = displayio_area_height(&clipped); uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; - uint32_t pixels_per_buffer = displayio_area_size(&clipped); + uint16_t pixels_per_buffer = displayio_area_size(&clipped); uint16_t subrectangles = 1; // for SH1107 and other boundary constrained controllers diff --git a/shared-module/displayio/bus_core.c b/shared-module/displayio/bus_core.c index 6bba0065ad9aa..bfa6192351110 100644 --- a/shared-module/displayio/bus_core.c +++ b/shared-module/displayio/bus_core.c @@ -117,7 +117,7 @@ void displayio_display_bus_end_transaction(displayio_display_bus_t *self) { self->end_transaction(self->bus); } -static void _displayio_display_bus_send_region_commands(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { +void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { uint16_t x1 = area->x1 + self->colstart; uint16_t x2 = area->x2 + self->colstart; uint16_t y1 = area->y1 + self->rowstart; @@ -235,10 +235,6 @@ static void _displayio_display_bus_send_region_commands(displayio_display_bus_t } } -void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area) { - _displayio_display_bus_send_region_commands(self, display, area); -} - void displayio_display_bus_flush(displayio_display_bus_t *self) { if (self->flush != NULL) { self->flush(self->bus); From 8961a09a795225186ce4ccc4798dc40788eae782 Mon Sep 17 00:00:00 2001 From: Liz Date: Tue, 10 Mar 2026 10:57:49 -0400 Subject: [PATCH 422/517] Update mpconfigboard.mk to use creators vid/pid --- ports/espressif/boards/xteink_x4/mpconfigboard.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/xteink_x4/mpconfigboard.mk b/ports/espressif/boards/xteink_x4/mpconfigboard.mk index bed8b2c3fa931..f14b23b8fa06e 100644 --- a/ports/espressif/boards/xteink_x4/mpconfigboard.mk +++ b/ports/espressif/boards/xteink_x4/mpconfigboard.mk @@ -1,5 +1,5 @@ -CIRCUITPY_CREATOR_ID = 0x0000303A -CIRCUITPY_CREATION_ID = 0x00001001 +CIRCUITPY_CREATOR_ID = 0x000C303C +CIRCUITPY_CREATION_ID = 0x00C30001 IDF_TARGET = esp32c3 From ef23f87a9bc1ebefe62a14bf8eb822d584bab988 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 10 Mar 2026 19:01:29 +0100 Subject: [PATCH 423/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 10 +++++++--- locale/el.po | 10 +++++++--- locale/hi.po | 10 +++++++--- locale/ko.po | 10 +++++++--- locale/ru.po | 10 +++++++--- locale/tr.po | 10 +++++++--- 6 files changed, 42 insertions(+), 18 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index 1b653528f192b..759d65d876004 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -110,6 +110,7 @@ msgid "%q contains duplicate pins" msgstr "%q obsahuje duplicitní piny" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q: selhání %d" @@ -872,7 +873,7 @@ msgstr "Pole souřadnic mají různé délky" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c +#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -976,6 +977,7 @@ msgstr "ECB operuje najednou pouze 16 bajtů" #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "ESP-IDF alokace paměti selhala" @@ -1259,7 +1261,8 @@ msgstr "Interní audio buffer je příliš malý" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "Interní chyba" @@ -1534,7 +1537,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "Žádný %q pin" @@ -1764,6 +1767,7 @@ msgid "Operation or feature not supported" msgstr "Operace nebo funkce není podporována" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "Časový limit operace vypršel" diff --git a/locale/el.po b/locale/el.po index 74e0da7b28bd2..872166169dbda 100644 --- a/locale/el.po +++ b/locale/el.po @@ -114,6 +114,7 @@ msgid "%q contains duplicate pins" msgstr "%q περιέχει διπλότυπα pins" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q αποτυχία: %d" @@ -878,7 +879,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c +#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -984,6 +985,7 @@ msgstr "ECB δουλεύει μόνο σε 16 bytes κάθε φορά" #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "ESP-IDF δέσμευση μνήμης απέτυχε" @@ -1265,7 +1267,8 @@ msgstr "" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1540,7 +1543,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "" @@ -1769,6 +1772,7 @@ msgid "Operation or feature not supported" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index f9a35f0b41b2e..e0bb53003d7e4 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -101,6 +101,7 @@ msgid "%q contains duplicate pins" msgstr "" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "" @@ -857,7 +858,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c +#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -960,6 +961,7 @@ msgstr "" #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "" @@ -1241,7 +1243,8 @@ msgstr "" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1516,7 +1519,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "" @@ -1745,6 +1748,7 @@ msgid "Operation or feature not supported" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 03c658395e0d9..09f4776dfde25 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -112,6 +112,7 @@ msgid "%q contains duplicate pins" msgstr "%q에 중복된 핀이 포함" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q 실패: %d" @@ -901,7 +902,7 @@ msgstr "좌표 배열의 길이가 다릅니다" msgid "Coordinate arrays types have different sizes" msgstr "좌표 배열 유형은 크기가 다릅니다" -#: shared-module/usb/core/Device.c +#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1005,6 +1006,7 @@ msgstr "ECB는 한 번에 16 바이트에서만 작동합니다" #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "ESP-IDF 메모리 할당에 실패하였습니다" @@ -1292,7 +1294,8 @@ msgstr "내부 오디오 버퍼가 너무 작습니다" msgid "Internal define error" msgstr "내부 정의 오류" -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "내부 오류" @@ -1571,7 +1574,7 @@ msgstr "빠른 메모리 부족" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "%q 핀이 없습니다" @@ -1808,6 +1811,7 @@ msgid "Operation or feature not supported" msgstr "작업 또는 기능이 지원되지 않습니다" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #, fuzzy msgid "Operation timed out" msgstr "작업 시간 초과되었습니다" diff --git a/locale/ru.po b/locale/ru.po index 2beb4b1ba388a..2c68943f6e45c 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -114,6 +114,7 @@ msgid "%q contains duplicate pins" msgstr "%q содержит пины дупликаты" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q сбой: %d" @@ -881,7 +882,7 @@ msgstr "Координатные массивы имеют разные длин msgid "Coordinate arrays types have different sizes" msgstr "Типы массивов координат имеют разные размеры" -#: shared-module/usb/core/Device.c +#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -987,6 +988,7 @@ msgstr "ECB работает только с 16 байтами за раз" #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "Ошибка выделения памяти ESP-IDF" @@ -1280,7 +1282,8 @@ msgstr "Внутренний звуковой буфер слишком мал" msgid "Internal define error" msgstr "Внутренняя ошибка определения" -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "Внутренняя ошибка" @@ -1557,7 +1560,7 @@ msgstr "Изображение памяти" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "Нет пина %q" @@ -1790,6 +1793,7 @@ msgid "Operation or feature not supported" msgstr "Операция или функция, не поддерживаемые" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "Истекло время ожидания операции" diff --git a/locale/tr.po b/locale/tr.po index 34d654b11e47b..d9d90eddfe87a 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -112,6 +112,7 @@ msgid "%q contains duplicate pins" msgstr "%q yinelenen pinler içeriyor" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q hata: %d" @@ -871,7 +872,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c +#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -974,6 +975,7 @@ msgstr "ECB aynı anda yalnızca 16 baytla çalışır" #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "" @@ -1259,7 +1261,8 @@ msgstr "Dahili ses arabelleği çok küçük" msgid "Internal define error" msgstr "Dahili tanımlama hatası" -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "Dahili hata" @@ -1535,7 +1538,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "%q pini yok" @@ -1764,6 +1767,7 @@ msgid "Operation or feature not supported" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "" From 74e5edd86f693d94a9d821b761dd082423979c1f Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 11 Mar 2026 12:35:39 -0400 Subject: [PATCH 424/517] wip --- ports/atmel-samd/common-hal/busio/SPI.c | 6 +++--- ports/espressif/background.c | 4 ++-- ports/espressif/supervisor/port.c | 13 +++++++++---- ports/espressif/supervisor/usb.c | 5 +++-- ports/raspberrypi/supervisor/port.c | 2 +- ports/renode/supervisor/port.c | 2 +- ports/zephyr-cp/supervisor/port.c | 6 +++++- supervisor/port.h | 14 +++++++++++--- supervisor/shared/background_callback.c | 4 ++-- supervisor/shared/port.c | 7 ++++++- supervisor/shared/safe_mode.c | 2 +- supervisor/shared/usb/usb.c | 6 +++--- supervisor/shared/web_workflow/web_workflow.c | 2 +- 13 files changed, 48 insertions(+), 25 deletions(-) diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index fe53580afdf23..19fd81e425d3e 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -194,14 +194,14 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { } allow_reset_sercom(self->spi_desc.dev.prvt); - // Mark as deinit early in case we are used in an interrupt. - common_hal_busio_spi_mark_deinit(self); - spi_m_sync_disable(&self->spi_desc); spi_m_sync_deinit(&self->spi_desc); reset_pin_number(self->clock_pin); reset_pin_number(self->MOSI_pin); reset_pin_number(self->MISO_pin); + + // This smashes self->clock_pin, so don't do it before resetting the pin above. + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, diff --git a/ports/espressif/background.c b/ports/espressif/background.c index 61615c01615b2..b1b81ac2c83a4 100644 --- a/ports/espressif/background.c +++ b/ports/espressif/background.c @@ -13,8 +13,8 @@ #include "freertos/task.h" void port_background_tick(void) { - // Zero delay in case FreeRTOS wants to switch to something else. - vTaskDelay(0); + // Yield with zero delay in case FreeRTOS wants to switch to something else. + port_task_yield(); } void port_background_task(void) { diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 4691433079876..b3045026e12d9 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -404,8 +404,8 @@ void reset_port(void) { watchdog_reset(); #endif - // Yield so the idle task can run and do any IDF cleanup needed. - port_yield(); + // Yield so the idle task, at priority 0, can run and do any IDF cleanup needed. + port_task_sleep_ms(4); } void reset_to_bootloader(void) { @@ -483,8 +483,13 @@ void port_wake_main_task_from_isr(void) { } } -void port_yield(void) { - vTaskDelay(4); +// Yield to other tasks at the same priority. +void port_task_yield(void) { + vTaskDelay(0); +} + +void port_task_sleep_ms(uint32_t msecs) { + vTaskDelay(pdMS_TO_TICKS(msecs)); } void sleep_timer_cb(void *arg) { diff --git a/ports/espressif/supervisor/usb.c b/ports/espressif/supervisor/usb.c index 62feea6981e05..e4d34ee7694f3 100644 --- a/ports/espressif/supervisor/usb.c +++ b/ports/espressif/supervisor/usb.c @@ -56,7 +56,8 @@ static void usb_device_task(void *param) { tud_task(); tud_cdc_write_flush(); } - vTaskDelay(1); + // Yield with zero delay to switch to any other tasks at same priority. + port_task_yield(); } } #endif // CIRCUITPY_USB_DEVICE @@ -112,7 +113,7 @@ void init_usb_hardware(void) { "usbd", USBD_STACK_SIZE, NULL, - 5, + 1, usb_device_stack, &usb_device_taskdef, xPortGetCoreID()); diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 5cfbdfa66a32b..bb6482240a697 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -600,7 +600,7 @@ __attribute__((used)) void __not_in_flash_func(isr_hardfault)(void) { } } -void port_yield(void) { +void port_task_yield(void) { #if CIRCUITPY_CYW43 cyw43_arch_poll(); #endif diff --git a/ports/renode/supervisor/port.c b/ports/renode/supervisor/port.c index d12ebd2efddb9..a40e222d34df3 100644 --- a/ports/renode/supervisor/port.c +++ b/ports/renode/supervisor/port.c @@ -210,7 +210,7 @@ __attribute__((used)) void HardFault_Handler(void) { } } -void port_yield(void) { +void port_task_yield(void) { } void port_boot_info(void) { diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 7be2beb1d2085..ff194fcbc8d89 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -65,7 +65,7 @@ void port_wake_main_task_from_isr(void) { k_event_set(&main_needed, 1); } -void port_yield(void) { +void port_task_yield(void) { k_yield(); // Make sure time advances in the simulator. #if defined(CONFIG_ARCH_POSIX) @@ -73,6 +73,10 @@ void port_yield(void) { #endif } +void port_task_sleep_ms(uint32_t msecs) { + k_msleep(msecs)); +} + void port_boot_info(void) { } diff --git a/supervisor/port.h b/supervisor/port.h index 192307a5f5714..7e2463032f74f 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -92,9 +92,17 @@ void port_wake_main_task(void); void port_wake_main_task_from_isr(void); // Some ports may use real RTOS tasks besides the background task framework of -// CircuitPython. Calling this will yield to other tasks and then return to the -// CircuitPython task when others are done. -void port_yield(void); +// CircuitPython. Calling this will yield to other tasks at the same priority level +// (or higher priority level if pre-emption is not immediate in the RTOS) +// and then return to the CircuitPython task when others are done. +// Note that this does NOT yield to lower priority tasks. Use port_task_sleep_ms() instead. +void port_task_yield(void); + +// On ports using real RTOS tasks, yield to other tasks for at least msecs. +// This will allow lower priority tasks to run. +// On non-RTOS implementations, this just sleeps for msecs and will run CircuitPython +// background tasks. +void port_task_sleep_ms(uint32_t msecs); // Some ports want to add information to boot_out.txt. // A default weak implementation is provided that does nothing. diff --git a/supervisor/shared/background_callback.c b/supervisor/shared/background_callback.c index 79b18f7a13753..afb5544696180 100644 --- a/supervisor/shared/background_callback.c +++ b/supervisor/shared/background_callback.c @@ -63,7 +63,7 @@ void PLACE_IN_ITCM(background_callback_run_all)(void) { if (!background_callback_pending()) { // TEMPORARY to fix #10822 #ifdef __ZEPHYR__ - port_yield(); + port_task_yield(); #endif return; } @@ -93,7 +93,7 @@ void PLACE_IN_ITCM(background_callback_run_all)(void) { CALLBACK_CRITICAL_END; // TEMPORARY to fix #10822 #ifdef __ZEPHYR__ - port_yield(); + port_task_yield(); #endif } diff --git a/supervisor/shared/port.c b/supervisor/shared/port.c index f908e3f0bd877..3c95f2b740931 100644 --- a/supervisor/shared/port.c +++ b/supervisor/shared/port.c @@ -8,6 +8,7 @@ #include +#include "py/mphal.h" #include "py/runtime.h" #include "py/gc.h" @@ -26,7 +27,11 @@ MP_WEAK void port_wake_main_task(void) { MP_WEAK void port_wake_main_task_from_isr(void) { } -MP_WEAK void port_yield(void) { +MP_WEAK void port_task_yield(void) { +} + +MP_WEAK void port_task_sleep_ms(uint32_t msecs) { + mp_hal_delay_ms(msecs); } MP_WEAK void port_boot_info(void) { diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index bfe27ac7c6513..d697cad675654 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -84,7 +84,7 @@ safe_mode_t wait_for_safe_mode_reset(void) { } // TEMPORARY to fix #10822 #ifdef __ZEPHYR__ - port_yield(); + port_task_yield(); #endif diff = supervisor_ticks_ms64() - start_ticks; } diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index fbe8be9788207..5061fee00633e 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -7,6 +7,7 @@ #include "py/objstr.h" #include "supervisor/background_callback.h" #include "supervisor/linker.h" +#include "supervisor/port.h" #include "supervisor/shared/tick.h" #include "supervisor/usb.h" #include "shared/readline/readline.h" @@ -160,9 +161,8 @@ void usb_background(void) { tuh_task(); #endif #elif CFG_TUSB_OS == OPT_OS_FREERTOS - // Yield to FreeRTOS in case TinyUSB runs in a separate task. Don't use - // port_yield() because it has a longer delay. - vTaskDelay(0); + // TinyUSB may run in a separate task, at the same priority as CircuitPython. + port_task_yield(); #endif // No need to flush if there's no REPL. #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index 553fe8694c6ea..10c0239ec5cb9 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -373,7 +373,7 @@ void web_workflow_send_raw(socketpool_socket_obj_t *socket, bool flush, const ui total_sent += sent; if (total_sent < len) { // Yield so that network code can run. - port_yield(); + port_task_sleep_ms(4); } } } From ac38142a90ac3bba013a8e1bdd1e05e509a11e26 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 12 Mar 2026 11:37:20 -0500 Subject: [PATCH 425/517] add type validation for Bitmaps in bitmaptools --- shared-bindings/bitmaptools/__init__.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index f3aad1b67b046..4d56f393189e4 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -206,9 +206,9 @@ static mp_obj_t bitmaptools_obj_rotozoom(size_t n_args, const mp_obj_t *pos_args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap - displayio_bitmap_t *source = MP_OBJ_TO_PTR(args[ARG_source_bitmap].u_obj); // the source bitmap + displayio_bitmap_t *source = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_source_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap)); // the source bitmap // ensure that the destination bitmap has at least as many `bits_per_value` as the source if (destination->bits_per_value < source->bits_per_value) { @@ -485,7 +485,7 @@ static mp_obj_t bitmaptools_obj_fill_region(size_t n_args, const mp_obj_t *pos_a mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap uint32_t value, color_depth; value = args[ARG_value].u_int; @@ -598,7 +598,7 @@ static mp_obj_t bitmaptools_obj_draw_line(size_t n_args, const mp_obj_t *pos_arg mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap uint32_t value, color_depth; value = args[ARG_value].u_int; @@ -684,7 +684,7 @@ static mp_obj_t bitmaptools_obj_draw_polygon(size_t n_args, const mp_obj_t *pos_ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap mp_buffer_info_t xs_buf, ys_buf; mp_get_buffer_raise(args[ARG_xs].u_obj, &xs_buf, MP_BUFFER_READ); @@ -1017,7 +1017,7 @@ static mp_obj_t bitmaptools_obj_draw_circle(size_t n_args, const mp_obj_t *pos_a mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap uint32_t value, color_depth; value = args[ARG_value].u_int; From 5bec6a617cc91db7bb58b0fc77bda65616316580 Mon Sep 17 00:00:00 2001 From: Liz Date: Thu, 12 Mar 2026 12:57:01 -0400 Subject: [PATCH 426/517] update creators id --- ports/espressif/boards/xteink_x4/mpconfigboard.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/espressif/boards/xteink_x4/mpconfigboard.mk b/ports/espressif/boards/xteink_x4/mpconfigboard.mk index f14b23b8fa06e..8668aad18180c 100644 --- a/ports/espressif/boards/xteink_x4/mpconfigboard.mk +++ b/ports/espressif/boards/xteink_x4/mpconfigboard.mk @@ -1,4 +1,4 @@ -CIRCUITPY_CREATOR_ID = 0x000C303C +CIRCUITPY_CREATOR_ID = 0x0EEE0000 CIRCUITPY_CREATION_ID = 0x00C30001 IDF_TARGET = esp32c3 From 236dec029c2c13ab0c72fcfb4acaefb9110b3dac Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 12 Mar 2026 20:45:28 -0400 Subject: [PATCH 427/517] shorten SDCard SPI timeouts, which were freezing out ESP32SPI access; remove RUN_BACKGROUND_TASKS in SDCard --- .../adafruit_metro_esp32s3/mpconfigboard.mk | 2 -- shared-bindings/busio/SPI.c | 5 +-- shared-module/sdcardio/SDCard.c | 34 +++++++++---------- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk b/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk index 57ccf4a4074e9..2fd5a40f25913 100644 --- a/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_metro_esp32s3/mpconfigboard.mk @@ -12,5 +12,3 @@ CIRCUITPY_ESP_FLASH_SIZE = 16MB CIRCUITPY_ESP_PSRAM_MODE = opi CIRCUITPY_ESP_PSRAM_FREQ = 80m CIRCUITPY_ESP_PSRAM_SIZE = 8MB - -CIRCUITPY_ULAB = 0 diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 39cf2516d5120..962080648ce4d 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -18,9 +18,9 @@ #include "shared-bindings/util.h" #include "shared/runtime/buffer_helper.h" #include "shared/runtime/context_manager_helpers.h" +#include "shared/runtime/interrupt_char.h" #include "supervisor/shared/tick.h" - //| class SPI: //| """A 3-4 wire serial protocol //| @@ -499,7 +499,8 @@ busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj, qstr arg_name) { // The default implementation is to busy-wait while running the background tasks. espressif is different. bool common_hal_busio_spi_wait_for_lock(busio_spi_obj_t *self, uint32_t timeout_ms) { uint64_t deadline = supervisor_ticks_ms64() + timeout_ms; - while (supervisor_ticks_ms64() < deadline) { + while (supervisor_ticks_ms64() < deadline && + !mp_hal_is_interrupted()) { if (common_hal_busio_spi_try_lock(self)) { return true; } diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c index a93736056e437..3af4c89a1c32a 100644 --- a/shared-module/sdcardio/SDCard.c +++ b/shared-module/sdcardio/SDCard.c @@ -16,6 +16,7 @@ #include "supervisor/shared/tick.h" #include "py/mperrno.h" +#include "py/mphal.h" #if 0 #define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print,##__VA_ARGS__)) @@ -23,13 +24,16 @@ #define DEBUG_PRINT(...) ((void)0) #endif +// https://nodeloop.org/guides/sd-card-spi-init-guide/ is an excellent source of info for SPI card use. + // https://www.taterli.com/wp-content/uploads/2017/05/Physical-Layer-Simplified-SpecificationV6.0.pdf // specifies timeouts for read (100 ms), write (250 ms), erase (depends on size), and other operations. // But the document also suggests allowing 500 ms even if a shorter timeout is specified. -// So let's allow a nice long time, but don't wait in a tight loop: allow background tasks to run. -#define CMD_TIMEOUT_MS (500) -#define TIMEOUT_MS (500) -#define SPI_TIMEOUT_MS (10000) +#define CMD_TIMEOUT_MS (250) +#define SPI_TIMEOUT_MS (250) +// Init ready timeout. +#define READY_TIMEOUT_MS (300) + #define R1_IDLE_STATE (1 << 0) #define R1_ILLEGAL_COMMAND (1 << 2) @@ -84,11 +88,10 @@ static void lock_bus_or_throw(sdcardio_sdcard_obj_t *self) { } static void clock_card(sdcardio_sdcard_obj_t *self, int bytes) { - uint8_t buf[] = {0xff}; + uint8_t buf[bytes]; + memset(buf, 0xff, bytes); common_hal_digitalio_digitalinout_set_value(&self->cs, true); - for (int i = 0; i < bytes; i++) { - common_hal_busio_spi_write(self->bus, buf, 1); - } + common_hal_busio_spi_write(self->bus, buf, bytes); } static void extraclock_and_unlock_bus(sdcardio_sdcard_obj_t *self) { @@ -124,19 +127,16 @@ static int32_t wait_for_masked_response(sdcardio_sdcard_obj_t *self, uint8_t mas if (((b & mask) == response) ^ not_match) { return b; } - RUN_BACKGROUND_TASKS; } return -1; } // Wait for the given response byte. static bool wait_for_response(sdcardio_sdcard_obj_t *self, uint8_t response) { - return wait_for_masked_response(self, 0xff, response, false, TIMEOUT_MS) != -1; + return wait_for_masked_response(self, 0xff, response, false, CMD_TIMEOUT_MS) != -1; } -#define READY_TIMEOUT_MS (300) - -// Wait for 0xff, with a shorter timeout. +// Wait for 0xff, with a specific timeout. static bool wait_for_ready(sdcardio_sdcard_obj_t *self) { return wait_for_masked_response(self, 0xff, 0xff, false, READY_TIMEOUT_MS) != -1; } @@ -232,7 +232,6 @@ static mp_rom_error_text_t init_card_v1(sdcardio_sdcard_obj_t *self) { if (cmd(self, 41, 0, NULL, 0, true, true) == 0) { return NULL; } - RUN_BACKGROUND_TASKS; } return MP_ERROR_TEXT("timeout waiting for v1 card"); } @@ -251,12 +250,13 @@ static mp_rom_error_text_t init_card_v2(sdcardio_sdcard_obj_t *self) { } return NULL; } - RUN_BACKGROUND_TASKS; } return MP_ERROR_TEXT("timeout waiting for v2 card"); } static mp_rom_error_text_t init_card(sdcardio_sdcard_obj_t *self) { + // https://nodeloop.org/guides/sd-card-spi-init-guide/ recommends at least 74 bit clocks + // and says 80 bit clocks(10*8) is common. Value below is bytes, not bits. clock_card(self, 10); common_hal_digitalio_digitalinout_set_value(&self->cs, false); @@ -476,7 +476,6 @@ static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t uint64_t deadline = supervisor_ticks_ms64() + CMD_TIMEOUT_MS; while (supervisor_ticks_ms64() < deadline) { common_hal_busio_spi_read(self->bus, cmd, 1, 0xff); - DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]); if ((cmd[0] & 0b00010001) == 0b00000001) { if ((cmd[0] & 0x1f) != 0x5) { return -MP_EIO; @@ -484,13 +483,12 @@ static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t break; } } - RUN_BACKGROUND_TASKS; } // Wait for the write to finish // Wait for a non-zero value. - if (wait_for_masked_response(self, 0xff /*mask*/, 0, true /*not_match*/, TIMEOUT_MS) == -1) { + if (wait_for_masked_response(self, 0xff /*mask*/, 0, true /*not_match*/, CMD_TIMEOUT_MS) == -1) { return -MP_EIO; } From c7b687d8b3a1a90247db1e8b5a0ffe39321d19f3 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 17 Mar 2026 16:24:41 -0500 Subject: [PATCH 428/517] fix return type --- shared-bindings/busio/I2C.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/busio/I2C.c b/shared-bindings/busio/I2C.c index c93566c0b62e4..5fa62f9df3859 100644 --- a/shared-bindings/busio/I2C.c +++ b/shared-bindings/busio/I2C.c @@ -112,7 +112,7 @@ static void check_lock(busio_i2c_obj_t *self) { } } -//| def probe(self, address: int) -> List[int]: +//| def probe(self, address: int) -> bool: //| """Check if a device at the specified address responds. //| //| :param int address: 7-bit device address From 9dc83288f444a2f2a3d4cec67b5cddc2e8545d80 Mon Sep 17 00:00:00 2001 From: Kong Wai Weng Date: Thu, 19 Mar 2026 17:41:05 +0800 Subject: [PATCH 429/517] Added pin definitions for GP29. --- ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c b/ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c index 53fae76ac1b4f..dbfc713930b6e 100644 --- a/ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c +++ b/ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c @@ -62,6 +62,8 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP29_A3), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_VBATT), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, From 0d6017538c7f4abc839a29c5f1d48c27209db0ea Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 17 Feb 2026 10:09:52 -0800 Subject: [PATCH 430/517] Add support for fixed Zephyr displays This adds zephyr_display. It acts similar to BusDisplay because we write regions to update to Zephyr. --- .../actions/deps/ports/zephyr-cp/action.yml | 11 +- AGENTS.md | 1 + locale/circuitpython.pot | 36 +- ports/zephyr-cp/AGENTS.md | 1 + ports/zephyr-cp/CLAUDE.md | 1 + ports/zephyr-cp/Makefile | 28 +- ports/zephyr-cp/README.md | 30 ++ .../bindings/zephyr_display/Display.c | 195 ++++++++ .../bindings/zephyr_display/Display.h | 37 ++ .../bindings/zephyr_display/__init__.c | 24 + .../bindings/zephyr_display/__init__.h | 7 + .../autogen_board_info.toml | 1 + ports/zephyr-cp/boards/board_aliases.cmake | 1 + ports/zephyr-cp/boards/ek_ra8d1.conf | 3 + .../native/native_sim/autogen_board_info.toml | 3 +- .../nrf5340bsim/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/native_sim.conf | 4 + .../nordic/nrf5340dk/autogen_board_info.toml | 1 + .../nordic/nrf54h20dk/autogen_board_info.toml | 1 + .../nordic/nrf54l15dk/autogen_board_info.toml | 1 + .../nordic/nrf7002dk/autogen_board_info.toml | 1 + .../nxp/frdm_mcxn947/autogen_board_info.toml | 1 + .../nxp/frdm_rw612/autogen_board_info.toml | 1 + .../mimxrt1170_evk/autogen_board_info.toml | 1 + .../da14695_dk_usb/autogen_board_info.toml | 1 + .../renesas/ek_ra6m5/autogen_board_info.toml | 1 + .../renesas/ek_ra8d1/autogen_board_info.toml | 3 +- .../renesas/ek_ra8d1/circuitpython.toml | 1 + .../nucleo_n657x0_q/autogen_board_info.toml | 1 + .../nucleo_u575zi_q/autogen_board_info.toml | 1 + .../st/stm32h750b_dk/autogen_board_info.toml | 118 +++++ .../st/stm32h750b_dk/circuitpython.toml | 1 + .../st/stm32h7b3i_dk/autogen_board_info.toml | 5 +- .../stm32wba65i_dk1/autogen_board_info.toml | 1 + ...m32h750b_dk_stm32h750xx_ext_flash_app.conf | 2 + ...h750b_dk_stm32h750xx_ext_flash_app.overlay | 14 + ports/zephyr-cp/boards/stm32h7b3i_dk.conf | 2 + ports/zephyr-cp/boards/stm32h7b3i_dk.overlay | 22 + .../common-hal/zephyr_display/Display.c | 440 ++++++++++++++++++ .../common-hal/zephyr_display/Display.h | 31 ++ ports/zephyr-cp/cptools/board_tools.py | 27 ++ .../zephyr-cp/cptools/build_circuitpython.py | 3 + .../zephyr-cp/cptools/get_west_shield_args.py | 70 +++ .../cptools/pre_zephyr_build_prep.py | 8 +- ports/zephyr-cp/cptools/zephyr2cp.py | 48 ++ ports/zephyr-cp/debug.conf | 15 + ports/zephyr-cp/tests/__init__.py | 10 + .../tests/bsim/test_bsim_ble_scan.py | 2 +- ports/zephyr-cp/tests/conftest.py | 153 ++++-- ports/zephyr-cp/tests/perfetto_input_trace.py | 85 ++-- .../zephyr-cp/tests/zephyr_display/README.md | 45 ++ .../golden/color_gradient_320x240.png | Bin 0 -> 12102 bytes .../golden/color_gradient_320x240_AL_88.png | Bin 0 -> 17323 bytes .../color_gradient_320x240_ARGB_8888.png | Bin 0 -> 1024 bytes .../golden/color_gradient_320x240_BGR_565.png | Bin 0 -> 12102 bytes .../golden/color_gradient_320x240_L_8.png | Bin 0 -> 17323 bytes .../golden/color_gradient_320x240_MONO01.png | Bin 0 -> 1516 bytes .../golden/color_gradient_320x240_MONO10.png | Bin 0 -> 1516 bytes .../golden/color_gradient_320x240_RGB_565.png | Bin 0 -> 12102 bytes .../golden/color_gradient_320x240_RGB_888.png | Bin 0 -> 15287 bytes .../terminal_console_output_320x240.mask.png | Bin 0 -> 450 bytes .../terminal_console_output_320x240.png | Bin 0 -> 3930 bytes .../terminal_console_output_320x240_AL_88.png | Bin 0 -> 3900 bytes ...minal_console_output_320x240_ARGB_8888.png | Bin 0 -> 1024 bytes ...erminal_console_output_320x240_BGR_565.png | Bin 0 -> 3930 bytes .../terminal_console_output_320x240_L_8.png | Bin 0 -> 3900 bytes ...terminal_console_output_320x240_MONO01.png | Bin 0 -> 3655 bytes ...onsole_output_320x240_MONO01_no_vtiled.png | Bin 0 -> 3655 bytes ...terminal_console_output_320x240_MONO10.png | Bin 0 -> 3655 bytes ...onsole_output_320x240_MONO10_no_vtiled.png | Bin 0 -> 3655 bytes ...erminal_console_output_320x240_RGB_565.png | Bin 0 -> 3930 bytes ...erminal_console_output_320x240_RGB_888.png | Bin 0 -> 3930 bytes .../zephyr_display/test_zephyr_display.py | 365 +++++++++++++++ ports/zephyr-cp/zephyr-config/west.yml | 2 +- py/circuitpy_mpconfig.mk | 3 + shared-module/displayio/__init__.c | 19 + shared-module/displayio/__init__.h | 6 + 77 files changed, 1799 insertions(+), 97 deletions(-) create mode 100644 ports/zephyr-cp/CLAUDE.md create mode 100644 ports/zephyr-cp/bindings/zephyr_display/Display.c create mode 100644 ports/zephyr-cp/bindings/zephyr_display/Display.h create mode 100644 ports/zephyr-cp/bindings/zephyr_display/__init__.c create mode 100644 ports/zephyr-cp/bindings/zephyr_display/__init__.h create mode 100644 ports/zephyr-cp/boards/ek_ra8d1.conf create mode 100644 ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/st/stm32h750b_dk/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf create mode 100644 ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay create mode 100644 ports/zephyr-cp/boards/stm32h7b3i_dk.conf create mode 100644 ports/zephyr-cp/common-hal/zephyr_display/Display.c create mode 100644 ports/zephyr-cp/common-hal/zephyr_display/Display.h create mode 100644 ports/zephyr-cp/cptools/get_west_shield_args.py create mode 100644 ports/zephyr-cp/debug.conf create mode 100644 ports/zephyr-cp/tests/zephyr_display/README.md create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_AL_88.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_ARGB_8888.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_BGR_565.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_L_8.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_MONO01.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_MONO10.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_565.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_888.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.mask.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_AL_88.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_ARGB_8888.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_BGR_565.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_L_8.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01_no_vtiled.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10_no_vtiled.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_565.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_888.png create mode 100644 ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py diff --git a/.github/actions/deps/ports/zephyr-cp/action.yml b/.github/actions/deps/ports/zephyr-cp/action.yml index 5f52cc7f0c259..6c538e3fa573e 100644 --- a/.github/actions/deps/ports/zephyr-cp/action.yml +++ b/.github/actions/deps/ports/zephyr-cp/action.yml @@ -3,11 +3,18 @@ name: Fetch Zephyr port deps runs: using: composite steps: - - name: Get libusb and mtools + - name: Get Linux build dependencies if: runner.os == 'Linux' run: | + echo "--- cpu model ---" + grep "model name" /proc/cpuinfo | head -1 + sudo dpkg --add-architecture i386 sudo apt-get update - sudo apt-get install -y libusb-1.0-0-dev libudev-dev mtools + sudo apt-get install -y libusb-1.0-0-dev libudev-dev pkg-config mtools + # We have to hold python3 so the following install works. See https://github.com/actions/runner-images/issues/13803 + sudo apt-mark hold python3 + sudo apt-get install -y libsdl2-dev:i386 libsdl2-image-dev:i386 + echo "PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" >> $GITHUB_ENV shell: bash - name: Setup Zephyr project uses: zephyrproject-rtos/action-zephyr-setup@v1 diff --git a/AGENTS.md b/AGENTS.md index 145e31c127159..e7d2fdbbe8caf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,4 @@ - Capture CircuitPython output by finding the matching device in `/dev/serial/by-id` - You can mount the CIRCUITPY drive by doing `udisksctl mount -b /dev/disk/by-label/CIRCUITPY` and access it via `/run/media//CIRCUITPY`. - `circup` is a command line tool to install libraries and examples to CIRCUITPY. +- When connecting to serial devices on Linux use /dev/serial/by-id. These will be more stable than /dev/ttyACM*. diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index bf4c5d110f673..08196993c366a 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -113,6 +113,7 @@ msgstr "" #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -135,6 +136,7 @@ msgstr "" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "" @@ -166,8 +168,8 @@ msgstr "" msgid "%q must be %d" msgstr "" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -459,6 +461,7 @@ msgstr "" msgid ", in %q\n" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -647,6 +650,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -669,6 +673,7 @@ msgstr "" msgid "Both RX and TX required for flow control" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" @@ -856,8 +861,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1023,10 +1027,6 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c #: ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -1144,6 +1144,7 @@ msgstr "" msgid "Generic Failure" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -1242,8 +1243,8 @@ msgstr "" msgid "Internal define error" msgstr "" -#: ports/espressif/common-hal/qspibus/QSPIBus.c -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1518,8 +1519,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-bindings/qspibus/QSPIBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "" @@ -3237,10 +3237,6 @@ msgstr "" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3322,10 +3318,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -4047,10 +4039,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" diff --git a/ports/zephyr-cp/AGENTS.md b/ports/zephyr-cp/AGENTS.md index 47813886804e9..58a3912e2ba05 100644 --- a/ports/zephyr-cp/AGENTS.md +++ b/ports/zephyr-cp/AGENTS.md @@ -4,3 +4,4 @@ - To flash it on a board do `make BOARD=_ flash`. - Zephyr board docs are at `zephyr/boards//`. - Run zephyr-cp tests with `make test`. +- Do not add new translatable error strings (`MP_ERROR_TEXT`). Instead, use `raise_zephyr_error()` or `CHECK_ZEPHYR_RESULT()` from `bindings/zephyr_kernel/__init__.h` to convert Zephyr errno values into Python exceptions. diff --git a/ports/zephyr-cp/CLAUDE.md b/ports/zephyr-cp/CLAUDE.md new file mode 100644 index 0000000000000..43c994c2d3617 --- /dev/null +++ b/ports/zephyr-cp/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index 5e905701668ba..ae1260c0f4dc1 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -8,13 +8,24 @@ BUILD ?= build-$(BOARD) TRANSLATION ?= en_US -.DEFAULT_GOAL := $(BUILD)/zephyr-cp/zephyr/zephyr.elf +# Compute shield args once. Command-line SHIELD/SHIELDS values override board defaults from circuitpython.toml. +ifneq ($(strip $(BOARD)),) +WEST_SHIELD_ARGS := $(shell SHIELD_ORIGIN="$(origin SHIELD)" SHIELDS_ORIGIN="$(origin SHIELDS)" SHIELD="$(SHIELD)" SHIELDS="$(SHIELDS)" python cptools/get_west_shield_args.py $(BOARD)) +endif -.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug run run-sim clean menuconfig all clean-all test fetch-port-submodules +WEST_CMAKE_ARGS := -DZEPHYR_BOARD_ALIASES=$(CURDIR)/boards/board_aliases.cmake -Dzephyr-cp_TRANSLATION=$(TRANSLATION) + +# When DEBUG=1, apply additional Kconfig fragments for debug-friendly settings. +DEBUG_CONF_FILE ?= $(CURDIR)/debug.conf +ifeq ($(DEBUG),1) +WEST_CMAKE_ARGS += -Dzephyr-cp_EXTRA_CONF_FILE=$(DEBUG_CONF_FILE) +endif + +.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug debug-jlink debugserver attach run run-sim clean menuconfig all clean-all test fetch-port-submodules $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) - west build -b $(BOARD) -d $(BUILD) --sysbuild -- -DZEPHYR_BOARD_ALIASES=$(CURDIR)/boards/board_aliases.cmake -Dzephyr-cp_TRANSLATION=$(TRANSLATION) + west build -b $(BOARD) -d $(BUILD) $(WEST_SHIELD_ARGS) --sysbuild -- $(WEST_CMAKE_ARGS) $(BUILD)/firmware.elf: $(BUILD)/zephyr-cp/zephyr/zephyr.elf cp $^ $@ @@ -37,6 +48,15 @@ recover: $(BUILD)/zephyr-cp/zephyr/zephyr.elf debug: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west debug -d $(BUILD) +debug-jlink: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + west debug --runner jlink -d $(BUILD) + +debugserver: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + west debugserver -d $(BUILD) + +attach: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + west attach -d $(BUILD) + run: $(BUILD)/firmware.exe $^ @@ -51,7 +71,7 @@ run-sim: build-native_native_sim/firmware.exe --flash=build-native_native_sim/flash.bin --flash_rm -wait_uart -rt menuconfig: - west build --sysbuild -d $(BUILD) -t menuconfig + west build $(WEST_SHIELD_ARGS) --sysbuild -d $(BUILD) -t menuconfig -- $(WEST_CMAKE_ARGS) clean: rm -rf $(BUILD) diff --git a/ports/zephyr-cp/README.md b/ports/zephyr-cp/README.md index f4391fc4cb635..28bbfbf298441 100644 --- a/ports/zephyr-cp/README.md +++ b/ports/zephyr-cp/README.md @@ -42,6 +42,36 @@ If a local `./CIRCUITPY/` folder exists, its files are used as the simulator's C Edit files in `./CIRCUITPY` (for example `code.py`) and rerun `make run-sim` to test changes. +## Shields + +Board defaults can be set in `boards///circuitpython.toml`: + +```toml +SHIELDS = ["shield1", "shield2"] +``` + +For example, `boards/renesas/ek_ra8d1/circuitpython.toml` enables: + +```toml +SHIELDS = ["rtkmipilcdb00000be"] +``` + +You can override shield selection from the command line: + +```sh +# Single shield +make BOARD=renesas_ek_ra8d1 SHIELD=rtkmipilcdb00000be + +# Multiple shields (comma, semicolon, or space separated) +make BOARD=my_vendor_my_board SHIELDS="shield1,shield2" +``` + +Behavior and precedence: + +- If `SHIELD` or `SHIELDS` is explicitly provided, it overrides board defaults. +- If neither is provided, defaults from `circuitpython.toml` are used. +- Use `SHIELD=` (empty) to disable a board default shield for one build. + ## Testing other boards [Any Zephyr board](https://docs.zephyrproject.org/latest/boards/index.html#) can diff --git a/ports/zephyr-cp/bindings/zephyr_display/Display.c b/ports/zephyr-cp/bindings/zephyr_display/Display.c new file mode 100644 index 0000000000000..0923618c50a7f --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_display/Display.c @@ -0,0 +1,195 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/zephyr_display/Display.h" + +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-module/displayio/__init__.h" + +static mp_obj_t zephyr_display_display_make_new(const mp_obj_type_t *type, + size_t n_args, + size_t n_kw, + const mp_obj_t *all_args) { + (void)type; + (void)n_args; + (void)n_kw; + (void)all_args; + mp_raise_NotImplementedError(NULL); + return mp_const_none; +} + +static zephyr_display_display_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native = mp_obj_cast_to_native_base(display_obj, &zephyr_display_display_type); + mp_obj_assert_native_inited(native); + return MP_OBJ_TO_PTR(native); +} + +static mp_obj_t zephyr_display_display_obj_show(mp_obj_t self_in, mp_obj_t group_in) { + (void)self_in; + (void)group_in; + mp_raise_AttributeError(MP_ERROR_TEXT(".show(x) removed. Use .root_group = x")); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_show_obj, zephyr_display_display_obj_show); + +static mp_obj_t zephyr_display_display_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_target_frames_per_second, + ARG_minimum_frames_per_second, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_target_frames_per_second, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_minimum_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + zephyr_display_display_obj_t *self = native_display(pos_args[0]); + + uint32_t maximum_ms_per_real_frame = NO_FPS_LIMIT; + mp_int_t minimum_frames_per_second = args[ARG_minimum_frames_per_second].u_int; + if (minimum_frames_per_second > 0) { + maximum_ms_per_real_frame = 1000 / minimum_frames_per_second; + } + + uint32_t target_ms_per_frame; + if (args[ARG_target_frames_per_second].u_obj == mp_const_none) { + target_ms_per_frame = NO_FPS_LIMIT; + } else { + target_ms_per_frame = 1000 / mp_obj_get_int(args[ARG_target_frames_per_second].u_obj); + } + + return mp_obj_new_bool(common_hal_zephyr_display_display_refresh( + self, + target_ms_per_frame, + maximum_ms_per_real_frame)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_display_display_refresh_obj, 1, zephyr_display_display_obj_refresh); + +static mp_obj_t zephyr_display_display_obj_get_auto_refresh(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_zephyr_display_display_get_auto_refresh(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_auto_refresh_obj, zephyr_display_display_obj_get_auto_refresh); + +static mp_obj_t zephyr_display_display_obj_set_auto_refresh(mp_obj_t self_in, mp_obj_t auto_refresh) { + zephyr_display_display_obj_t *self = native_display(self_in); + common_hal_zephyr_display_display_set_auto_refresh(self, mp_obj_is_true(auto_refresh)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_auto_refresh_obj, zephyr_display_display_obj_set_auto_refresh); + +MP_PROPERTY_GETSET(zephyr_display_display_auto_refresh_obj, + (mp_obj_t)&zephyr_display_display_get_auto_refresh_obj, + (mp_obj_t)&zephyr_display_display_set_auto_refresh_obj); + +static mp_obj_t zephyr_display_display_obj_get_brightness(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + mp_float_t brightness = common_hal_zephyr_display_display_get_brightness(self); + if (brightness < 0) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Brightness not adjustable")); + } + return mp_obj_new_float(brightness); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_brightness_obj, zephyr_display_display_obj_get_brightness); + +static mp_obj_t zephyr_display_display_obj_set_brightness(mp_obj_t self_in, mp_obj_t brightness_obj) { + zephyr_display_display_obj_t *self = native_display(self_in); + mp_float_t brightness = mp_obj_get_float(brightness_obj); + if (brightness < 0.0f || brightness > 1.0f) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be %d-%d"), MP_QSTR_brightness, 0, 1); + } + bool ok = common_hal_zephyr_display_display_set_brightness(self, brightness); + if (!ok) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Brightness not adjustable")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_brightness_obj, zephyr_display_display_obj_set_brightness); + +MP_PROPERTY_GETSET(zephyr_display_display_brightness_obj, + (mp_obj_t)&zephyr_display_display_get_brightness_obj, + (mp_obj_t)&zephyr_display_display_set_brightness_obj); + +static mp_obj_t zephyr_display_display_obj_get_width(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_width_obj, zephyr_display_display_obj_get_width); +MP_PROPERTY_GETTER(zephyr_display_display_width_obj, (mp_obj_t)&zephyr_display_display_get_width_obj); + +static mp_obj_t zephyr_display_display_obj_get_height(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_height_obj, zephyr_display_display_obj_get_height); +MP_PROPERTY_GETTER(zephyr_display_display_height_obj, (mp_obj_t)&zephyr_display_display_get_height_obj); + +static mp_obj_t zephyr_display_display_obj_get_rotation(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_rotation(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_rotation_obj, zephyr_display_display_obj_get_rotation); + +static mp_obj_t zephyr_display_display_obj_set_rotation(mp_obj_t self_in, mp_obj_t value) { + zephyr_display_display_obj_t *self = native_display(self_in); + common_hal_zephyr_display_display_set_rotation(self, mp_obj_get_int(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_rotation_obj, zephyr_display_display_obj_set_rotation); + +MP_PROPERTY_GETSET(zephyr_display_display_rotation_obj, + (mp_obj_t)&zephyr_display_display_get_rotation_obj, + (mp_obj_t)&zephyr_display_display_set_rotation_obj); + +static mp_obj_t zephyr_display_display_obj_get_root_group(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return common_hal_zephyr_display_display_get_root_group(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_root_group_obj, zephyr_display_display_obj_get_root_group); + +static mp_obj_t zephyr_display_display_obj_set_root_group(mp_obj_t self_in, mp_obj_t group_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + displayio_group_t *group = NULL; + if (group_in != mp_const_none) { + group = MP_OBJ_TO_PTR(native_group(group_in)); + } + + bool ok = common_hal_zephyr_display_display_set_root_group(self, group); + if (!ok) { + mp_raise_ValueError(MP_ERROR_TEXT("Group already used")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_root_group_obj, zephyr_display_display_obj_set_root_group); + +MP_PROPERTY_GETSET(zephyr_display_display_root_group_obj, + (mp_obj_t)&zephyr_display_display_get_root_group_obj, + (mp_obj_t)&zephyr_display_display_set_root_group_obj); + +static const mp_rom_map_elem_t zephyr_display_display_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&zephyr_display_display_show_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&zephyr_display_display_refresh_obj) }, + + { MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&zephyr_display_display_auto_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&zephyr_display_display_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&zephyr_display_display_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&zephyr_display_display_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&zephyr_display_display_rotation_obj) }, + { MP_ROM_QSTR(MP_QSTR_root_group), MP_ROM_PTR(&zephyr_display_display_root_group_obj) }, +}; +static MP_DEFINE_CONST_DICT(zephyr_display_display_locals_dict, zephyr_display_display_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + zephyr_display_display_type, + MP_QSTR_Display, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, zephyr_display_display_make_new, + locals_dict, &zephyr_display_display_locals_dict); diff --git a/ports/zephyr-cp/bindings/zephyr_display/Display.h b/ports/zephyr-cp/bindings/zephyr_display/Display.h new file mode 100644 index 0000000000000..a50dda8fe8b69 --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_display/Display.h @@ -0,0 +1,37 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/displayio/Group.h" +#include "common-hal/zephyr_display/Display.h" + +extern const mp_obj_type_t zephyr_display_display_type; + +#define NO_FPS_LIMIT 0xffffffff + +void common_hal_zephyr_display_display_construct_from_device(zephyr_display_display_obj_t *self, + const struct device *device, + uint16_t rotation, + bool auto_refresh); + +bool common_hal_zephyr_display_display_refresh(zephyr_display_display_obj_t *self, + uint32_t target_ms_per_frame, + uint32_t maximum_ms_per_real_frame); + +bool common_hal_zephyr_display_display_get_auto_refresh(zephyr_display_display_obj_t *self); +void common_hal_zephyr_display_display_set_auto_refresh(zephyr_display_display_obj_t *self, bool auto_refresh); + +uint16_t common_hal_zephyr_display_display_get_width(zephyr_display_display_obj_t *self); +uint16_t common_hal_zephyr_display_display_get_height(zephyr_display_display_obj_t *self); +uint16_t common_hal_zephyr_display_display_get_rotation(zephyr_display_display_obj_t *self); +void common_hal_zephyr_display_display_set_rotation(zephyr_display_display_obj_t *self, int rotation); + +mp_float_t common_hal_zephyr_display_display_get_brightness(zephyr_display_display_obj_t *self); +bool common_hal_zephyr_display_display_set_brightness(zephyr_display_display_obj_t *self, mp_float_t brightness); + +mp_obj_t common_hal_zephyr_display_display_get_root_group(zephyr_display_display_obj_t *self); +bool common_hal_zephyr_display_display_set_root_group(zephyr_display_display_obj_t *self, displayio_group_t *root_group); diff --git a/ports/zephyr-cp/bindings/zephyr_display/__init__.c b/ports/zephyr-cp/bindings/zephyr_display/__init__.c new file mode 100644 index 0000000000000..eecfeeaec58ae --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_display/__init__.c @@ -0,0 +1,24 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/runtime.h" + +#include "bindings/zephyr_display/Display.h" + +static const mp_rom_map_elem_t zephyr_display_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr_display) }, + { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&zephyr_display_display_type) }, +}; + +static MP_DEFINE_CONST_DICT(zephyr_display_module_globals, zephyr_display_module_globals_table); + +const mp_obj_module_t zephyr_display_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&zephyr_display_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_zephyr_display, zephyr_display_module); diff --git a/ports/zephyr-cp/bindings/zephyr_display/__init__.h b/ports/zephyr-cp/bindings/zephyr_display/__init__.h new file mode 100644 index 0000000000000..4256bfac2fe16 --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_display/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 9712f467858eb..2c717fc83c169 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index dce5b100aa6df..5914ae61f28e6 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -41,6 +41,7 @@ cp_board_alias(nxp_frdm_mcxn947 frdm_mcxn947/mcxn947/cpu0) cp_board_alias(nxp_frdm_rw612 frdm_rw612) cp_board_alias(nxp_mimxrt1170_evk mimxrt1170_evk@A/mimxrt1176/cm7) cp_board_alias(st_stm32h7b3i_dk stm32h7b3i_dk) +cp_board_alias(st_stm32h750b_dk stm32h750b_dk/stm32h750xx/ext_flash_app) cp_board_alias(st_stm32wba65i_dk1 stm32wba65i_dk1) cp_board_alias(st_nucleo_u575zi_q nucleo_u575zi_q/stm32u575xx) cp_board_alias(st_nucleo_n657x0_q nucleo_n657x0_q/stm32n657xx) diff --git a/ports/zephyr-cp/boards/ek_ra8d1.conf b/ports/zephyr-cp/boards/ek_ra8d1.conf new file mode 100644 index 0000000000000..f979d31e751f1 --- /dev/null +++ b/ports/zephyr-cp/boards/ek_ra8d1.conf @@ -0,0 +1,3 @@ + +# Enable Zephyr display subsystem so DT chosen zephyr,display creates a device. +CONFIG_DISPLAY=y diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 80b1b4ebf7d6a..1b82d3ba00e02 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -37,7 +37,7 @@ canio = false codeop = false countio = false digitalio = true -displayio = true # Zephyr board has busio +displayio = true # Zephyr board has displayio dotclockframebuffer = false dualbank = false epaperdisplay = true # Zephyr board has busio @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 1bc1b96e6cf5c..f995c03c2a26f 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/native_sim.conf b/ports/zephyr-cp/boards/native_sim.conf index ddbfef11266d8..739a71eeeb61e 100644 --- a/ports/zephyr-cp/boards/native_sim.conf +++ b/ports/zephyr-cp/boards/native_sim.conf @@ -14,6 +14,10 @@ CONFIG_TRACING_GPIO=y # I2C emulation for testing CONFIG_I2C_EMUL=y +# Display emulation for display/terminal golden tests. +CONFIG_DISPLAY=y +CONFIG_SDL_DISPLAY=y + # EEPROM emulation for testing CONFIG_EEPROM=y CONFIG_EEPROM_AT24=y diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 7869cca4fafba..397d514a0dac5 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index c2233ddf8b544..69a657b8c98be 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index a1e8de8822b49..378cbc07e9a2a 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index b50b1966ed074..892b3dbbbd447 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = true # Zephyr board has wifi +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 21d55194a1c1c..718cd0bcfd353 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index 90f84ab1586c4..10b0056c81dcc 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = true # Zephyr board has wifi +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index eb5db066c893c..a3d6acdb79e71 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index d6efa285fe2a4..3d5fbb08c6543 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 7600b8bbd151a..ec96d53072699 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 8e49b95d33416..132899d424838 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -37,7 +37,7 @@ canio = false codeop = false countio = false digitalio = true -displayio = true # Zephyr board has busio +displayio = true # Zephyr board has displayio dotclockframebuffer = false dualbank = false epaperdisplay = true # Zephyr board has busio @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/circuitpython.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/circuitpython.toml index 3272dd4c5f319..0e19d8d71574e 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/circuitpython.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/circuitpython.toml @@ -1 +1,2 @@ CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +SHIELDS = ["rtkmipilcdb00000be"] diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index b28a9481c72d9..bc2a29aea088c 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 6b0ef8d8480f1..aacda400a305e 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml new file mode 100644 index 0000000000000..a8c03d5a4e8f5 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -0,0 +1,118 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "STMicroelectronics STM32H750B Discovery Kit" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has displayio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = true # Zephyr board has zephyr_display +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/circuitpython.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/circuitpython.toml new file mode 100644 index 0000000000000..83e6bcd39c4f9 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index b6f03f3d627c6..b66682c59238b 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -37,7 +37,7 @@ canio = false codeop = false countio = false digitalio = true -displayio = true # Zephyr board has busio +displayio = true # Zephyr board has displayio dotclockframebuffer = false dualbank = false epaperdisplay = true # Zephyr board has busio @@ -103,7 +103,7 @@ touchio = false traceback = true uheap = false usb = false -usb_cdc = false +usb_cdc = true usb_hid = false usb_host = false usb_midi = false @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index 8d1fd9253488b..cadd8b5ade3a2 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -113,5 +113,6 @@ vectorio = true # Zephyr board has busio warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false zlib = false diff --git a/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf new file mode 100644 index 0000000000000..24afffb8e88ee --- /dev/null +++ b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf @@ -0,0 +1,2 @@ +# Enable Zephyr display subsystem so the built-in LTDC panel is available. +CONFIG_DISPLAY=y diff --git a/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay new file mode 100644 index 0000000000000..fdb4960477b77 --- /dev/null +++ b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay @@ -0,0 +1,14 @@ +&ext_flash { + partitions { + /delete-node/ partition@7800000; + + circuitpy_partition: partition@7800000 { + label = "circuitpy"; + reg = <0x7800000 DT_SIZE_M(8)>; + }; + }; +}; + +&rng { + status = "okay"; +}; diff --git a/ports/zephyr-cp/boards/stm32h7b3i_dk.conf b/ports/zephyr-cp/boards/stm32h7b3i_dk.conf new file mode 100644 index 0000000000000..24afffb8e88ee --- /dev/null +++ b/ports/zephyr-cp/boards/stm32h7b3i_dk.conf @@ -0,0 +1,2 @@ +# Enable Zephyr display subsystem so the built-in LTDC panel is available. +CONFIG_DISPLAY=y diff --git a/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay b/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay index 88ad0415485b8..c2b5f3129c48a 100644 --- a/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay +++ b/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay @@ -2,6 +2,28 @@ /delete-node/ partitions; }; +&sram5 { + status = "disabled"; +}; + &rng { status = "okay"; }; + +&fdcan1 { + status = "disabled"; +}; + +/ { + chosen { + /delete-property/ zephyr,canbus; + }; +}; + +zephyr_udc0: &usbotg_hs { + pinctrl-0 = <&usb_otg_hs_dm_pa11 &usb_otg_hs_dp_pa12>; + pinctrl-names = "default"; + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/common-hal/zephyr_display/Display.c b/ports/zephyr-cp/common-hal/zephyr_display/Display.c new file mode 100644 index 0000000000000..d1585b5f291d5 --- /dev/null +++ b/ports/zephyr-cp/common-hal/zephyr_display/Display.c @@ -0,0 +1,440 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/zephyr_display/Display.h" + +#include + +#include "bindings/zephyr_kernel/__init__.h" +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/time/__init__.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/display.h" +#include "supervisor/shared/tick.h" + +static const displayio_area_t *zephyr_display_get_refresh_areas(zephyr_display_display_obj_t *self) { + if (self->core.full_refresh) { + self->core.area.next = NULL; + return &self->core.area; + } else if (self->core.current_group != NULL) { + return displayio_group_get_refresh_areas(self->core.current_group, NULL); + } + return NULL; +} + +static enum display_pixel_format zephyr_display_select_pixel_format(const struct display_capabilities *caps) { + uint32_t formats = caps->supported_pixel_formats; + + if (formats & PIXEL_FORMAT_RGB_565) { + return PIXEL_FORMAT_RGB_565; + } + if (formats & PIXEL_FORMAT_RGB_888) { + return PIXEL_FORMAT_RGB_888; + } + if (formats & PIXEL_FORMAT_ARGB_8888) { + return PIXEL_FORMAT_ARGB_8888; + } + if (formats & PIXEL_FORMAT_RGB_565X) { + return PIXEL_FORMAT_RGB_565X; + } + if (formats & PIXEL_FORMAT_L_8) { + return PIXEL_FORMAT_L_8; + } + if (formats & PIXEL_FORMAT_AL_88) { + return PIXEL_FORMAT_AL_88; + } + if (formats & PIXEL_FORMAT_MONO01) { + return PIXEL_FORMAT_MONO01; + } + if (formats & PIXEL_FORMAT_MONO10) { + return PIXEL_FORMAT_MONO10; + } + return caps->current_pixel_format; +} + +static void zephyr_display_select_colorspace(zephyr_display_display_obj_t *self, + uint16_t *color_depth, + uint8_t *bytes_per_cell, + bool *grayscale, + bool *pixels_in_byte_share_row, + bool *reverse_pixels_in_byte, + bool *reverse_bytes_in_word) { + *color_depth = 16; + *bytes_per_cell = 2; + *grayscale = false; + *pixels_in_byte_share_row = false; + *reverse_pixels_in_byte = false; + *reverse_bytes_in_word = false; + + if (self->pixel_format == PIXEL_FORMAT_RGB_565X) { + // RGB_565X is big-endian RGB_565, so byte-swap from native LE. + *reverse_bytes_in_word = true; + } else if (self->pixel_format == PIXEL_FORMAT_RGB_888) { + *color_depth = 24; + *bytes_per_cell = 3; + *reverse_bytes_in_word = false; + } else if (self->pixel_format == PIXEL_FORMAT_ARGB_8888) { + *color_depth = 32; + *bytes_per_cell = 4; + *reverse_bytes_in_word = false; + } else if (self->pixel_format == PIXEL_FORMAT_L_8 || + self->pixel_format == PIXEL_FORMAT_AL_88) { + *color_depth = 8; + *bytes_per_cell = 1; + *grayscale = true; + *reverse_bytes_in_word = false; + } else if (self->pixel_format == PIXEL_FORMAT_MONO01 || + self->pixel_format == PIXEL_FORMAT_MONO10) { + bool vtiled = self->capabilities.screen_info & SCREEN_INFO_MONO_VTILED; + bool msb_first = self->capabilities.screen_info & SCREEN_INFO_MONO_MSB_FIRST; + *color_depth = 1; + *bytes_per_cell = 1; + *grayscale = true; + *pixels_in_byte_share_row = !vtiled; + *reverse_pixels_in_byte = msb_first; + *reverse_bytes_in_word = false; + } +} + +void common_hal_zephyr_display_display_construct_from_device(zephyr_display_display_obj_t *self, + const struct device *device, + uint16_t rotation, + bool auto_refresh) { + self->auto_refresh = false; + + if (device == NULL || !device_is_ready(device)) { + raise_zephyr_error(-ENODEV); + } + + self->device = device; + display_get_capabilities(self->device, &self->capabilities); + + self->pixel_format = zephyr_display_select_pixel_format(&self->capabilities); + if (self->pixel_format != self->capabilities.current_pixel_format) { + (void)display_set_pixel_format(self->device, self->pixel_format); + display_get_capabilities(self->device, &self->capabilities); + self->pixel_format = self->capabilities.current_pixel_format; + } + + uint16_t color_depth; + uint8_t bytes_per_cell; + bool grayscale; + bool pixels_in_byte_share_row; + bool reverse_pixels_in_byte; + bool reverse_bytes_in_word; + zephyr_display_select_colorspace(self, &color_depth, &bytes_per_cell, + &grayscale, &pixels_in_byte_share_row, &reverse_pixels_in_byte, + &reverse_bytes_in_word); + + displayio_display_core_construct( + &self->core, + self->capabilities.x_resolution, + self->capabilities.y_resolution, + 0, + color_depth, + grayscale, + pixels_in_byte_share_row, + bytes_per_cell, + reverse_pixels_in_byte, + reverse_bytes_in_word); + + self->native_frames_per_second = 60; + self->native_ms_per_frame = 1000 / self->native_frames_per_second; + self->first_manual_refresh = !auto_refresh; + + if (rotation != 0) { + common_hal_zephyr_display_display_set_rotation(self, rotation); + } + + (void)display_blanking_off(self->device); + + displayio_display_core_set_root_group(&self->core, &circuitpython_splash); + common_hal_zephyr_display_display_set_auto_refresh(self, auto_refresh); +} + +uint16_t common_hal_zephyr_display_display_get_width(zephyr_display_display_obj_t *self) { + return displayio_display_core_get_width(&self->core); +} + +uint16_t common_hal_zephyr_display_display_get_height(zephyr_display_display_obj_t *self) { + return displayio_display_core_get_height(&self->core); +} + +mp_float_t common_hal_zephyr_display_display_get_brightness(zephyr_display_display_obj_t *self) { + (void)self; + return -1; +} + +bool common_hal_zephyr_display_display_set_brightness(zephyr_display_display_obj_t *self, mp_float_t brightness) { + (void)self; + (void)brightness; + return false; +} + +static bool zephyr_display_refresh_area(zephyr_display_display_obj_t *self, const displayio_area_t *area) { + uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); + + displayio_area_t clipped; + if (!displayio_display_core_clip_area(&self->core, area, &clipped)) { + return true; + } + + uint16_t rows_per_buffer = displayio_area_height(&clipped); + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; + // For AL_88, displayio fills at 1 byte/pixel (L_8) but output needs 2 bytes/pixel, + // so halve the effective pixels_per_word for buffer sizing. + uint8_t effective_pixels_per_word = pixels_per_word; + if (self->pixel_format == PIXEL_FORMAT_AL_88) { + effective_pixels_per_word = sizeof(uint32_t) / 2; + } + uint16_t pixels_per_buffer = displayio_area_size(&clipped); + uint16_t subrectangles = 1; + + // When pixels_in_byte_share_row is false (mono vtiled), 8 vertical pixels + // pack into one column byte. The byte layout needs width * ceil(height/8) + // bytes, which can exceed the pixel-count-based buffer size. + bool vtiled = self->core.colorspace.depth < 8 && + !self->core.colorspace.pixels_in_byte_share_row; + + bool needs_subdivision = displayio_area_size(&clipped) > buffer_size * effective_pixels_per_word; + if (vtiled && !needs_subdivision) { + uint16_t width = displayio_area_width(&clipped); + uint16_t height = displayio_area_height(&clipped); + uint32_t vtiled_bytes = (uint32_t)width * ((height + 7) / 8); + needs_subdivision = vtiled_bytes > buffer_size * sizeof(uint32_t); + } + + if (needs_subdivision) { + rows_per_buffer = buffer_size * effective_pixels_per_word / displayio_area_width(&clipped); + if (vtiled) { + rows_per_buffer = (rows_per_buffer / 8) * 8; + if (rows_per_buffer == 0) { + rows_per_buffer = 8; + } + } + if (rows_per_buffer == 0) { + rows_per_buffer = 1; + } + subrectangles = displayio_area_height(&clipped) / rows_per_buffer; + if (displayio_area_height(&clipped) % rows_per_buffer != 0) { + subrectangles++; + } + pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); + buffer_size = pixels_per_buffer / pixels_per_word; + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + // Ensure buffer is large enough for vtiled packing. + if (vtiled) { + uint16_t width = displayio_area_width(&clipped); + uint16_t vtiled_words = (width * ((rows_per_buffer + 7) / 8) + sizeof(uint32_t) - 1) / sizeof(uint32_t); + if (vtiled_words > buffer_size) { + buffer_size = vtiled_words; + } + } + // Ensure buffer is large enough for AL_88 expansion. + if (self->pixel_format == PIXEL_FORMAT_AL_88) { + uint16_t al88_words = (pixels_per_buffer * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t); + if (al88_words > buffer_size) { + buffer_size = al88_words; + } + } + } + + uint32_t buffer[buffer_size]; + uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t mask[mask_length]; + + uint16_t remaining_rows = displayio_area_height(&clipped); + + for (uint16_t j = 0; j < subrectangles; j++) { + displayio_area_t subrectangle = { + .x1 = clipped.x1, + .y1 = clipped.y1 + rows_per_buffer * j, + .x2 = clipped.x2, + .y2 = clipped.y1 + rows_per_buffer * (j + 1), + }; + + if (remaining_rows < rows_per_buffer) { + subrectangle.y2 = subrectangle.y1 + remaining_rows; + } + remaining_rows -= rows_per_buffer; + + memset(mask, 0, mask_length * sizeof(mask[0])); + memset(buffer, 0, buffer_size * sizeof(buffer[0])); + + displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); + + uint16_t width = displayio_area_width(&subrectangle); + uint16_t height = displayio_area_height(&subrectangle); + size_t pixel_count = (size_t)width * (size_t)height; + + if (self->pixel_format == PIXEL_FORMAT_MONO10) { + uint8_t *bytes = (uint8_t *)buffer; + size_t byte_count = (pixel_count + 7) / 8; + for (size_t i = 0; i < byte_count; i++) { + bytes[i] = ~bytes[i]; + } + } + + if (self->pixel_format == PIXEL_FORMAT_AL_88) { + uint8_t *bytes = (uint8_t *)buffer; + for (size_t i = pixel_count; i > 0; i--) { + bytes[(i - 1) * 2 + 1] = 0xFF; + bytes[(i - 1) * 2] = bytes[i - 1]; + } + } + + // Compute buf_size based on the Zephyr pixel format. + uint32_t buf_size_bytes; + if (self->pixel_format == PIXEL_FORMAT_MONO01 || + self->pixel_format == PIXEL_FORMAT_MONO10) { + buf_size_bytes = (pixel_count + 7) / 8; + } else if (self->pixel_format == PIXEL_FORMAT_AL_88) { + buf_size_bytes = pixel_count * 2; + } else { + buf_size_bytes = pixel_count * (self->core.colorspace.depth / 8); + } + + struct display_buffer_descriptor desc = { + .buf_size = buf_size_bytes, + .width = width, + .height = height, + .pitch = width, + .frame_incomplete = false, + }; + + int err = display_write(self->device, subrectangle.x1, subrectangle.y1, &desc, buffer); + if (err < 0) { + return false; + } + + RUN_BACKGROUND_TASKS; + } + + return true; +} + +static void zephyr_display_refresh(zephyr_display_display_obj_t *self) { + if (!displayio_display_core_start_refresh(&self->core)) { + return; + } + + const displayio_area_t *current_area = zephyr_display_get_refresh_areas(self); + while (current_area != NULL) { + if (!zephyr_display_refresh_area(self, current_area)) { + break; + } + current_area = current_area->next; + } + + displayio_display_core_finish_refresh(&self->core); +} + +void common_hal_zephyr_display_display_set_rotation(zephyr_display_display_obj_t *self, int rotation) { + bool transposed = (self->core.rotation == 90 || self->core.rotation == 270); + bool will_transposed = (rotation == 90 || rotation == 270); + if (transposed != will_transposed) { + int tmp = self->core.width; + self->core.width = self->core.height; + self->core.height = tmp; + } + + displayio_display_core_set_rotation(&self->core, rotation); + + if (self == &displays[0].zephyr_display) { + supervisor_stop_terminal(); + supervisor_start_terminal(self->core.width, self->core.height); + } + + if (self->core.current_group != NULL) { + displayio_group_update_transform(self->core.current_group, &self->core.transform); + } +} + +uint16_t common_hal_zephyr_display_display_get_rotation(zephyr_display_display_obj_t *self) { + return self->core.rotation; +} + +bool common_hal_zephyr_display_display_refresh(zephyr_display_display_obj_t *self, + uint32_t target_ms_per_frame, + uint32_t maximum_ms_per_real_frame) { + if (!self->auto_refresh && !self->first_manual_refresh && (target_ms_per_frame != NO_FPS_LIMIT)) { + uint64_t current_time = supervisor_ticks_ms64(); + uint32_t current_ms_since_real_refresh = current_time - self->core.last_refresh; + if (current_ms_since_real_refresh > maximum_ms_per_real_frame) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Below minimum frame rate")); + } + uint32_t current_ms_since_last_call = current_time - self->last_refresh_call; + self->last_refresh_call = current_time; + if (current_ms_since_last_call > target_ms_per_frame) { + return false; + } + uint32_t remaining_time = target_ms_per_frame - (current_ms_since_real_refresh % target_ms_per_frame); + while (supervisor_ticks_ms64() - self->last_refresh_call < remaining_time) { + RUN_BACKGROUND_TASKS; + } + } + self->first_manual_refresh = false; + zephyr_display_refresh(self); + return true; +} + +bool common_hal_zephyr_display_display_get_auto_refresh(zephyr_display_display_obj_t *self) { + return self->auto_refresh; +} + +void common_hal_zephyr_display_display_set_auto_refresh(zephyr_display_display_obj_t *self, bool auto_refresh) { + self->first_manual_refresh = !auto_refresh; + if (auto_refresh != self->auto_refresh) { + if (auto_refresh) { + supervisor_enable_tick(); + } else { + supervisor_disable_tick(); + } + } + self->auto_refresh = auto_refresh; +} + +void zephyr_display_display_background(zephyr_display_display_obj_t *self) { + if (self->auto_refresh && (supervisor_ticks_ms64() - self->core.last_refresh) > self->native_ms_per_frame) { + zephyr_display_refresh(self); + } +} + +void release_zephyr_display(zephyr_display_display_obj_t *self) { + common_hal_zephyr_display_display_set_auto_refresh(self, false); + release_display_core(&self->core); + self->device = NULL; + self->base.type = &mp_type_NoneType; +} + +void zephyr_display_display_collect_ptrs(zephyr_display_display_obj_t *self) { + (void)self; + displayio_display_core_collect_ptrs(&self->core); +} + +void zephyr_display_display_reset(zephyr_display_display_obj_t *self) { + if (self->device != NULL && device_is_ready(self->device)) { + common_hal_zephyr_display_display_set_auto_refresh(self, true); + displayio_display_core_set_root_group(&self->core, &circuitpython_splash); + self->core.full_refresh = true; + } else { + release_zephyr_display(self); + } +} + +mp_obj_t common_hal_zephyr_display_display_get_root_group(zephyr_display_display_obj_t *self) { + if (self->core.current_group == NULL) { + return mp_const_none; + } + return self->core.current_group; +} + +bool common_hal_zephyr_display_display_set_root_group(zephyr_display_display_obj_t *self, displayio_group_t *root_group) { + return displayio_display_core_set_root_group(&self->core, root_group); +} diff --git a/ports/zephyr-cp/common-hal/zephyr_display/Display.h b/ports/zephyr-cp/common-hal/zephyr_display/Display.h new file mode 100644 index 0000000000000..bdec1e96ba5f3 --- /dev/null +++ b/ports/zephyr-cp/common-hal/zephyr_display/Display.h @@ -0,0 +1,31 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" +#include "shared-module/displayio/display_core.h" + +typedef struct { + mp_obj_base_t base; + displayio_display_core_t core; + const struct device *device; + struct display_capabilities capabilities; + enum display_pixel_format pixel_format; + uint64_t last_refresh_call; + uint16_t native_frames_per_second; + uint16_t native_ms_per_frame; + bool auto_refresh; + bool first_manual_refresh; +} zephyr_display_display_obj_t; + +void zephyr_display_display_background(zephyr_display_display_obj_t *self); +void zephyr_display_display_collect_ptrs(zephyr_display_display_obj_t *self); +void zephyr_display_display_reset(zephyr_display_display_obj_t *self); +void release_zephyr_display(zephyr_display_display_obj_t *self); diff --git a/ports/zephyr-cp/cptools/board_tools.py b/ports/zephyr-cp/cptools/board_tools.py index 088b4bb54914b..305abab3f19b5 100644 --- a/ports/zephyr-cp/cptools/board_tools.py +++ b/ports/zephyr-cp/cptools/board_tools.py @@ -1,3 +1,6 @@ +import tomllib + + def find_mpconfigboard(portdir, board_id): next_underscore = board_id.find("_") while next_underscore != -1: @@ -8,3 +11,27 @@ def find_mpconfigboard(portdir, board_id): return p next_underscore = board_id.find("_", next_underscore + 1) return None + + +def load_mpconfigboard(portdir, board_id): + mpconfigboard_path = find_mpconfigboard(portdir, board_id) + if mpconfigboard_path is None or not mpconfigboard_path.exists(): + return None, {} + + with mpconfigboard_path.open("rb") as f: + return mpconfigboard_path, tomllib.load(f) + + +def get_shields(mpconfigboard): + shields = mpconfigboard.get("SHIELDS") + if shields is None: + shields = mpconfigboard.get("SHIELD") + + if shields is None: + return [] + if isinstance(shields, str): + return [shields] + if isinstance(shields, (list, tuple)): + return [str(shield) for shield in shields] + + return [str(shields)] diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 3da878e0f5b7c..2022d82f1b73f 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -68,6 +68,9 @@ "busio": ["fourwire", "i2cdisplaybus", "sdcardio", "sharpdisplay"], "fourwire": ["displayio", "busdisplay", "epaperdisplay"], "i2cdisplaybus": ["displayio", "busdisplay", "epaperdisplay"], + # Zephyr display backends need displayio and, by extension, terminalio so + # the REPL console appears on the display by default. + "zephyr_display": ["displayio"], "displayio": [ "vectorio", "bitmapfilter", diff --git a/ports/zephyr-cp/cptools/get_west_shield_args.py b/ports/zephyr-cp/cptools/get_west_shield_args.py new file mode 100644 index 0000000000000..deda6bf5f26f8 --- /dev/null +++ b/ports/zephyr-cp/cptools/get_west_shield_args.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""Resolve shield arguments for west build. + +Priority: +1. SHIELD / SHIELDS make variables (if explicitly provided) +2. SHIELD / SHIELDS from boards///circuitpython.toml +""" + +import argparse +import os +import pathlib +import re +import shlex + +import board_tools + + +def split_shields(raw): + if not raw: + return [] + + return [shield for shield in re.split(r"[,;\s]+", raw.strip()) if shield] + + +def dedupe(values): + deduped = [] + seen = set() + + for value in values: + if value in seen: + continue + seen.add(value) + deduped.append(value) + + return deduped + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("board") + args = parser.parse_args() + + portdir = pathlib.Path(__file__).resolve().parent.parent + + _, mpconfigboard = board_tools.load_mpconfigboard(portdir, args.board) + + shield_origin = os.environ.get("SHIELD_ORIGIN", "undefined") + shields_origin = os.environ.get("SHIELDS_ORIGIN", "undefined") + + shield_override = os.environ.get("SHIELD", "") + shields_override = os.environ.get("SHIELDS", "") + + override_requested = shield_origin != "undefined" or shields_origin != "undefined" + + if override_requested: + shields = split_shields(shield_override) + split_shields(shields_override) + else: + shields = board_tools.get_shields(mpconfigboard) + + shields = dedupe(shields) + + west_shield_args = [] + for shield in shields: + west_shield_args.extend(("--shield", shield)) + + print(shlex.join(west_shield_args)) + + +if __name__ == "__main__": + main() diff --git a/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py b/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py index acc3ae786196d..f42fc1a3a8547 100644 --- a/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py +++ b/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py @@ -2,7 +2,6 @@ import pathlib import subprocess import sys -import tomllib import board_tools @@ -10,14 +9,11 @@ board = sys.argv[-1] -mpconfigboard = board_tools.find_mpconfigboard(portdir, board) -if mpconfigboard is None: +_, mpconfigboard = board_tools.load_mpconfigboard(portdir, board) +if not mpconfigboard: # Assume it doesn't need any prep. sys.exit(0) -with mpconfigboard.open("rb") as f: - mpconfigboard = tomllib.load(f) - blobs = mpconfigboard.get("BLOBS", []) blob_fetch_args = mpconfigboard.get("blob_fetch_args", {}) for blob in blobs: diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index f7d79517195e0..c123d90ce7816 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -488,6 +488,14 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa value = device_tree.root.nodes["chosen"].props[k] path2chosen[value.to_path()] = k chosen2path[k] = value.to_path() + + chosen_display = chosen2path.get("zephyr,display") + if chosen_display is not None: + status = chosen_display.props.get("status", None) + if status is None or status.to_string() == "okay": + board_info["zephyr_display"] = True + board_info["displayio"] = True + remaining_nodes = set([device_tree.root]) while remaining_nodes: node = remaining_nodes.pop() @@ -724,6 +732,43 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa zephyr_binding_objects = "\n".join(zephyr_binding_objects) zephyr_binding_labels = "\n".join(zephyr_binding_labels) + zephyr_display_header = "" + zephyr_display_object = "" + zephyr_display_board_entry = "" + if board_info.get("zephyr_display", False): + zephyr_display_header = """ +#include +#include +#include "shared-module/displayio/__init__.h" +#include "bindings/zephyr_display/Display.h" + """.strip() + zephyr_display_object = """ +void board_init(void) { +#if CIRCUITPY_ZEPHYR_DISPLAY && DT_HAS_CHOSEN(zephyr_display) + // Always allocate a display slot so board.DISPLAY is at least a valid + // NoneType object even if the underlying Zephyr display is unavailable. + primary_display_t *display_obj = allocate_display(); + if (display_obj == NULL) { + return; + } + + zephyr_display_display_obj_t *display = &display_obj->zephyr_display; + display->base.type = &mp_type_NoneType; + + const struct device *display_dev = device_get_binding(DEVICE_DT_NAME(DT_CHOSEN(zephyr_display))); + if (display_dev == NULL || !device_is_ready(display_dev)) { + return; + } + + display->base.type = &zephyr_display_display_type; + common_hal_zephyr_display_display_construct_from_device(display, display_dev, 0, true); +#endif +} + """.strip() + zephyr_display_board_entry = ( + "{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].zephyr_display) }," + ) + board_dir.mkdir(exist_ok=True, parents=True) header = board_dir / "mpconfigboard.h" if status_led: @@ -797,6 +842,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa #include "py/mphal.h" {zephyr_binding_headers} +{zephyr_display_header} const struct device* const flashes[] = {{ {", ".join(flashes)} }}; const int circuitpy_flash_device_count = {len(flashes)}; @@ -810,6 +856,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa {pin_defs} {zephyr_binding_objects} +{zephyr_display_object} static const mp_rom_map_elem_t mcu_pin_globals_table[] = {{ {mcu_pin_mapping} @@ -820,6 +867,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS {hostnetwork_entry} +{zephyr_display_board_entry} {board_pin_mapping} {zephyr_binding_labels} diff --git a/ports/zephyr-cp/debug.conf b/ports/zephyr-cp/debug.conf new file mode 100644 index 0000000000000..90c1f52d4db6b --- /dev/null +++ b/ports/zephyr-cp/debug.conf @@ -0,0 +1,15 @@ +CONFIG_DEBUG=y +CONFIG_DEBUG_OPTIMIZATIONS=y +CONFIG_LOG_MAX_LEVEL=4 + +CONFIG_STACK_SENTINEL=y +CONFIG_DEBUG_THREAD_INFO=y +CONFIG_DEBUG_INFO=y +CONFIG_EXCEPTION_STACK_TRACE=y + +CONFIG_ASSERT=y +CONFIG_LOG_BLOCK_IN_THREAD=y +CONFIG_FRAME_POINTER=y + +CONFIG_FLASH_LOG_LEVEL_DBG=y +CONFIG_LOG_MODE_IMMEDIATE=y diff --git a/ports/zephyr-cp/tests/__init__.py b/ports/zephyr-cp/tests/__init__.py index 18e596e8e7046..8ab7610ce0f53 100644 --- a/ports/zephyr-cp/tests/__init__.py +++ b/ports/zephyr-cp/tests/__init__.py @@ -1,3 +1,5 @@ +from pathlib import Path + import serial import subprocess import threading @@ -144,6 +146,14 @@ def shutdown(self): self.serial.close() self.debug_serial.close() + def display_capture_paths(self) -> list[Path]: + """Return paths to numbered PNG capture files produced by trace-driven capture.""" + pattern = getattr(self, "_capture_png_pattern", None) + count = getattr(self, "_capture_count", 0) + if not pattern or count == 0: + return [] + return [Path(pattern % i) for i in range(count)] + def wait_until_done(self): start_time = time.monotonic() while self._proc.poll() is None and time.monotonic() - start_time < self._timeout: diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py index ffc4cb6eabe4f..19455b7bfa3fc 100644 --- a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py @@ -91,7 +91,7 @@ def test_bsim_scan_zephyr_beacon_reload(bsim_phy, circuitpython, zephyr_sample): assert output.count("scan run done True") >= 2 -@pytest.mark.flaky(reruns=3) +@pytest.mark.xfail(strict=False, reason="scan without stop_scan may fail on reload") @pytest.mark.zephyr_sample("bluetooth/beacon") @pytest.mark.code_py_runs(2) @pytest.mark.duration(8) diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py index 1a364ba2995eb..b0047f0c94763 100644 --- a/ports/zephyr-cp/tests/conftest.py +++ b/ports/zephyr-cp/tests/conftest.py @@ -4,23 +4,31 @@ """Pytest fixtures for CircuitPython native_sim testing.""" import logging -import re -import select +import os import subprocess -import time -from dataclasses import dataclass from pathlib import Path import pytest import serial -from . import NativeSimProcess + from .perfetto_input_trace import write_input_trace from perfetto.trace_processor import TraceProcessor +from . import NativeSimProcess + logger = logging.getLogger(__name__) +def pytest_addoption(parser): + parser.addoption( + "--update-goldens", + action="store_true", + default=False, + help="Overwrite golden images with captured output instead of comparing.", + ) + + def pytest_configure(config): config.addinivalue_line( "markers", "circuitpy_drive(files): run CircuitPython with files in the flash image" @@ -51,6 +59,20 @@ def pytest_configure(config): "markers", "native_sim_rt: run native_sim in realtime mode (-rt instead of -no-rt)", ) + config.addinivalue_line( + "markers", + "display(capture_times_ns=None): run test with SDL display; " + "capture_times_ns is a list of nanosecond timestamps for trace-triggered captures", + ) + config.addinivalue_line( + "markers", + "display_pixel_format(format): override the display pixel format " + "(e.g. 'RGB_565', 'ARGB_8888')", + ) + config.addinivalue_line( + "markers", + "display_mono_vtiled(value): override the mono vtiled screen_info flag (True or False)", + ) ZEPHYR_CP = Path(__file__).parent.parent @@ -136,7 +158,6 @@ def board(request): @pytest.fixture def native_sim_binary(request, board): """Return path to native_sim binary, skip if not built.""" - ZEPHYR_CP = Path(__file__).parent.parent build_dir = ZEPHYR_CP / f"build-{board}" binary = build_dir / "zephyr-cp/zephyr/zephyr.exe" @@ -150,6 +171,26 @@ def native_sim_env() -> dict[str, str]: return {} +PIXEL_FORMAT_BITMASK = { + "RGB_888": 1 << 0, + "MONO01": 1 << 1, + "MONO10": 1 << 2, + "ARGB_8888": 1 << 3, + "RGB_565": 1 << 4, + "BGR_565": 1 << 5, + "L_8": 1 << 6, + "AL_88": 1 << 7, +} + + +@pytest.fixture +def pixel_format(request) -> str: + """Indirect-parametrize fixture: adds display_pixel_format marker.""" + fmt = request.param + request.node.add_marker(pytest.mark.display_pixel_format(fmt)) + return fmt + + @pytest.fixture def sim_id(request) -> str: return request.node.nodeid.replace("/", "_") @@ -175,6 +216,54 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp if input_trace_markers and len(input_trace_markers[0][1].args) == 1: input_trace = input_trace_markers[0][1].args[0] + input_trace_file = None + if input_trace is not None: + input_trace_file = tmp_path / "input.perfetto" + write_input_trace(input_trace_file, input_trace) + + marker = request.node.get_closest_marker("duration") + if marker is None: + timeout = 10 + else: + timeout = marker.args[0] + + runs_marker = request.node.get_closest_marker("code_py_runs") + if runs_marker is None: + code_py_runs = 1 + else: + code_py_runs = int(runs_marker.args[0]) + + display_marker = request.node.get_closest_marker("display") + if display_marker is None: + display_marker = request.node.get_closest_marker("display_capture") + + capture_times_ns = None + if display_marker is not None: + capture_times_ns = display_marker.kwargs.get("capture_times_ns", None) + + pixel_format_marker = request.node.get_closest_marker("display_pixel_format") + pixel_format = None + if pixel_format_marker is not None and pixel_format_marker.args: + pixel_format = pixel_format_marker.args[0] + + mono_vtiled_marker = request.node.get_closest_marker("display_mono_vtiled") + mono_vtiled = None + if mono_vtiled_marker is not None and mono_vtiled_marker.args: + mono_vtiled = mono_vtiled_marker.args[0] + + # If capture_times_ns is set, merge display_capture track into input trace. + if capture_times_ns is not None: + if input_trace is None: + input_trace = {} + else: + input_trace = dict(input_trace) + input_trace["display_capture"] = list(capture_times_ns) + if input_trace_file is None: + input_trace_file = tmp_path / "input.perfetto" + write_input_trace(input_trace_file, input_trace) + + use_realtime = request.node.get_closest_marker("native_sim_rt") is not None + procs = [] for i in range(instance_count): flash = tmp_path / f"flash-{i}.bin" @@ -189,30 +278,14 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp for name, content in files.items(): src = tmp_drive / name - src.write_text(content) + if isinstance(content, bytes): + src.write_bytes(content) + else: + src.write_text(content) subprocess.run(["mcopy", "-i", str(flash), str(src), f"::{name}"], check=True) trace_file = tmp_path / f"trace-{i}.perfetto" - input_trace_file = None - if input_trace is not None: - input_trace_file = tmp_path / f"input-{i}.perfetto" - write_input_trace(input_trace_file, input_trace) - - marker = request.node.get_closest_marker("duration") - if marker is None: - timeout = 10 - else: - timeout = marker.args[0] - - runs_marker = request.node.get_closest_marker("code_py_runs") - if runs_marker is None: - code_py_runs = 1 - else: - code_py_runs = int(runs_marker.args[0]) - - use_realtime = request.node.get_closest_marker("native_sim_rt") is not None - if "bsim" in board: cmd = [str(native_sim_binary), f"--flash_app={flash}"] if instance_count > 1: @@ -231,7 +304,9 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp cmd = [str(native_sim_binary), f"--flash={flash}"] # native_sim vm-runs includes the boot VM setup run. realtime_flag = "-rt" if use_realtime else "-no-rt" - cmd.extend((realtime_flag, "-wait_uart", f"--vm-runs={code_py_runs + 1}")) + cmd.extend( + (realtime_flag, "-display_headless", "-wait_uart", f"--vm-runs={code_py_runs + 1}") + ) if input_trace_file is not None: cmd.append(f"--input-trace={input_trace_file}") @@ -240,9 +315,31 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp if marker and len(marker.args) > 0: for device in marker.args: cmd.append(f"--disable-i2c={device}") + + if pixel_format is not None: + cmd.append(f"--display_pixel_format={PIXEL_FORMAT_BITMASK[pixel_format]}") + + if mono_vtiled is not None: + cmd.append(f"--display_mono_vtiled={'true' if mono_vtiled else 'false'}") + + env = os.environ.copy() + env.update(native_sim_env) + + capture_png_pattern = None + if capture_times_ns is not None: + if instance_count == 1: + capture_png_pattern = str(tmp_path / "frame_%d.png") + else: + capture_png_pattern = str(tmp_path / f"frame-{i}_%d.png") + cmd.append(f"--display_capture_png={capture_png_pattern}") + logger.info("Running: %s", " ".join(cmd)) + proc = NativeSimProcess(cmd, timeout, trace_file, env) + proc.display_dump = None + proc._capture_png_pattern = capture_png_pattern + proc._capture_count = len(capture_times_ns) if capture_times_ns is not None else 0 + procs.append(proc) - procs.append(NativeSimProcess(cmd, timeout, trace_file, native_sim_env)) if instance_count == 1: yield procs[0] else: diff --git a/ports/zephyr-cp/tests/perfetto_input_trace.py b/ports/zephyr-cp/tests/perfetto_input_trace.py index d0cde49be087a..494c9cdcadf69 100644 --- a/ports/zephyr-cp/tests/perfetto_input_trace.py +++ b/ports/zephyr-cp/tests/perfetto_input_trace.py @@ -7,11 +7,12 @@ python -m tests.perfetto_input_trace input_trace.json output.perfetto -Input JSON format: +Input JSON format — counter tracks use [timestamp, value] pairs, instant +tracks use bare timestamps: { "gpio_emul.01": [[8000000000, 0], [9000000000, 1], [10000000000, 0]], - "gpio_emul.02": [[8000000000, 0], [9200000000, 1]] + "display_capture": [500000000, 1000000000] } """ @@ -22,7 +23,9 @@ from pathlib import Path from typing import Mapping, Sequence -InputTraceData = Mapping[str, Sequence[tuple[int, int]]] +# Counter tracks: list of (timestamp, value) pairs. +# Instant tracks: list of timestamps (bare ints). +InputTraceData = Mapping[str, Sequence[tuple[int, int] | int]] def _load_perfetto_pb2(): @@ -31,8 +34,15 @@ def _load_perfetto_pb2(): return perfetto_pb2 +def _is_instant_track(events: Sequence) -> bool: + """Return True if *events* is a list of bare timestamps (instant track).""" + if not events: + return False + return isinstance(events[0], int) + + def build_input_trace(trace_data: InputTraceData, *, sequence_id: int = 1): - """Build a Perfetto Trace protobuf for input replay counter tracks.""" + """Build a Perfetto Trace protobuf for input replay counter and instant tracks.""" perfetto_pb2 = _load_perfetto_pb2() trace = perfetto_pb2.Trace() @@ -41,6 +51,7 @@ def build_input_trace(trace_data: InputTraceData, *, sequence_id: int = 1): for idx, (track_name, events) in enumerate(trace_data.items()): track_uuid = 1001 + idx + instant = _is_instant_track(events) desc_packet = trace.packet.add() desc_packet.timestamp = 0 @@ -49,16 +60,28 @@ def build_input_trace(trace_data: InputTraceData, *, sequence_id: int = 1): desc_packet.sequence_flags = seq_incremental_state_cleared desc_packet.track_descriptor.uuid = track_uuid desc_packet.track_descriptor.name = track_name - desc_packet.track_descriptor.counter.unit = perfetto_pb2.CounterDescriptor.Unit.UNIT_COUNT + if not instant: + desc_packet.track_descriptor.counter.unit = ( + perfetto_pb2.CounterDescriptor.Unit.UNIT_COUNT + ) - for ts, value in events: - event_packet = trace.packet.add() - event_packet.timestamp = ts - event_packet.trusted_packet_sequence_id = sequence_id - event_packet.sequence_flags = seq_needs_incremental_state - event_packet.track_event.type = perfetto_pb2.TrackEvent.Type.TYPE_COUNTER - event_packet.track_event.track_uuid = track_uuid - event_packet.track_event.counter_value = value + if instant: + for ts in events: + event_packet = trace.packet.add() + event_packet.timestamp = ts + event_packet.trusted_packet_sequence_id = sequence_id + event_packet.sequence_flags = seq_needs_incremental_state + event_packet.track_event.type = perfetto_pb2.TrackEvent.Type.TYPE_INSTANT + event_packet.track_event.track_uuid = track_uuid + else: + for ts, value in events: + event_packet = trace.packet.add() + event_packet.timestamp = ts + event_packet.trusted_packet_sequence_id = sequence_id + event_packet.sequence_flags = seq_needs_incremental_state + event_packet.track_event.type = perfetto_pb2.TrackEvent.Type.TYPE_COUNTER + event_packet.track_event.track_uuid = track_uuid + event_packet.track_event.counter_value = value return trace @@ -72,27 +95,35 @@ def write_input_trace( trace_file.write_bytes(trace.SerializeToString()) -def _parse_trace_json(data: object) -> dict[str, list[tuple[int, int]]]: +def _parse_trace_json(data: object) -> dict[str, list[tuple[int, int]] | list[int]]: if not isinstance(data, dict): raise ValueError("top-level JSON value must be an object") - parsed: dict[str, list[tuple[int, int]]] = {} + parsed: dict[str, list[tuple[int, int]] | list[int]] = {} for track_name, events in data.items(): if not isinstance(track_name, str): raise ValueError("track names must be strings") if not isinstance(events, list): - raise ValueError( - f"track {track_name!r} must map to a list of [timestamp, value] events" - ) - - parsed_events: list[tuple[int, int]] = [] - for event in events: - if not isinstance(event, (list, tuple)) or len(event) != 2: - raise ValueError(f"track {track_name!r} events must be [timestamp, value] pairs") - timestamp_ns, value = event - parsed_events.append((int(timestamp_ns), int(value))) - - parsed[track_name] = parsed_events + raise ValueError(f"track {track_name!r} must map to a list of events") + + if not events: + parsed[track_name] = [] + continue + + # Distinguish instant (bare ints) vs counter ([ts, value] pairs). + if isinstance(events[0], (int, float)): + parsed[track_name] = [int(ts) for ts in events] + else: + parsed_events: list[tuple[int, int]] = [] + for event in events: + if not isinstance(event, (list, tuple)) or len(event) != 2: + raise ValueError( + f"track {track_name!r} events must be [timestamp, value] pairs " + "or bare timestamps" + ) + timestamp_ns, value = event + parsed_events.append((int(timestamp_ns), int(value))) + parsed[track_name] = parsed_events return parsed diff --git a/ports/zephyr-cp/tests/zephyr_display/README.md b/ports/zephyr-cp/tests/zephyr_display/README.md new file mode 100644 index 0000000000000..6b50202154352 --- /dev/null +++ b/ports/zephyr-cp/tests/zephyr_display/README.md @@ -0,0 +1,45 @@ +# Zephyr Display Golden Tests + +This directory contains native_sim golden-image tests for the Zephyr-specific `zephyr_display` path. + +## What is tested + +- `board.DISPLAY` is present and usable. +- CircuitPython terminal/console tilegrids are attached to the default display root group. +- Deterministic console terminal output matches a checked-in golden image. +- `zephyr_display` pixel format constants are exposed. +- `displayio` rendering produces expected stripe colors at sampled pixel locations. + +## Files + +- `test_zephyr_display.py` – pytest tests. +- `golden/terminal_console_output_320x240.png` – console terminal output golden reference image. + +## How capture works + +These tests use trace-driven SDL display capture triggered by Perfetto instant events: + +- `--input-trace=` provides a Perfetto trace containing a `"display_capture"` track + with instant events at the desired capture timestamps. +- `--display_capture_png=` specifies the output PNG pattern (may contain `%d` for + a sequence number). +- `--display_headless` runs SDL in headless/hidden-window mode (always enabled for native_sim tests). + +The test harness sets these flags automatically when tests use +`@pytest.mark.display(capture_times_ns=[...])`. + +## Regenerating the console golden image + +```bash +rm -rf /tmp/zephyr-display-golden +pytest -q ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py::test_console_output_golden \ + --basetemp=/tmp/zephyr-display-golden +cp /tmp/zephyr-display-golden/test_console_output_golden0/frame_0.png \ + ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.png +``` + +## Running the tests + +```bash +pytest -q ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py +``` diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240.png new file mode 100644 index 0000000000000000000000000000000000000000..5739b8ee156eb72f2fd419d5de58eb81d6750650 GIT binary patch literal 12102 zcmV-MFS*c(P)Pu@3^;LcfrASiT;SmL0sDY;AkFti6v?jYt|mEeRls5; zvdNh@-CZp9NTau3zkdBHFTC)=3%?=$wE6^Ic;ST?R^eOu30nR+^*mm9;Tk~m<%LV& z8*CYI{EIwa*bOftd!dx_t$h3#^=dE*wih`*Vc?Mg{vtk$i(>}CeegqGURs5AR=xfD z=RadNK~yAp;0qI?kJyy>!s(|bJO{Zc@r6B=0Doq^{rbNS0V;?sI>$xHa6QqnxM;d5 zNH0oslZo=8o)RD@tNH{uf|uC5<2EP4>k%G;jZ@7_d=au*tO$G9*h@jo$*Q+s|Ms;M57B{Rej`sgF+a1~1lR*ZT3NOWNifSj zIN71dOtnQR#*iOFVlxPGfGkg4hX~hZ)!VQC4Jg1>c9Tg^?IJzlAyB8<#1uox4<)Bn zb^tEyB@%mNRhs}?K*(vP%>)uuxo8dHji5@i3F)OIznL~7T*y-(_R6ZaUw{8QuK*P! zrZ`3t2`XHqa1S;|G({9CmY7}^0a|3FAT+@-6jyn3PXUN#mOiWAe*Mqie}%{>ljy9m z2?V&(MRE78B*TQ%$|S!`(n}`9sF&A}U?j8hvg$^FHN2!WyRQ@}39fKa?)KQ5D&BG8 z)EW_>mw+<*kp2N?d#RM!BcEp(v$E=-mfD$T!uja5pZm~uq>pJ|h#4+w+n-y}(D#fv#%aeY+X6G{i&bjb?Lifo=_U>7Az;Hl(_TP;Jq6ez zp=^Rv5@pG~^6cDsOI0@cX|nK>0MCe$-fY_d0WxmdllWT5N|{L{IGg;kYz^!}ggGR* zLuT2wpO3?8ZF{XYk1f*1+D<@*idu4qNyd`3P-=)kwJV$8|tM}^jF_g zywD+o{4z4|$B9l*B8!sSM#P4?N$n!sJywsJ2-Rtr3=vUfwVnN^@>GULldHA^tk0~s zU;p`E{Td+B<{hV=%drhc2vB+Hahx9o8~qNRi844zC?9p@jX^Z#tvqM| z_3<9`kOgH7T6W_r{;hnxAFpm`ee9*T6-KtpqxYyyjZHolqa;3Vj?tQqY>$!6k;*0k zip6!(nBLVvy)>EwwV8)m)UKlRcTxeO>W_Kv9g_@&3u0^2XnjLhz7dc)6e=&MEj+Q{ ziAhgf0}9Z(%BXI)FvF8^%=k;VozS-@<>pax(f5-P4LF61BIBPeqdLRV7?J*DL>J)f z_jg1+g{r|w|GJwY2j*v1Ujb&kFNyCbA=5=@g^nMIXiaPWNPiMCV=5Z&9Uz{v#z*?| zka^~c_->Ey*8rLKsM^a^hChqsd%=+a$_i|}28ig4k;ceUdQNQ-S@c-4%djnDPes(A z*Ap9ki0K3_`X3Qn*uVWjIuW8{ZBf$OZ0qcGlh;N4K8xtA^vWQ?$*DDi&;-XAc`9qU z+K=SCnUgOmZd>59d>?IHnQg7APYgX7%_hIvs%JUsi>#W9Fp3MN&l87iPIPXD^e{Vq zBDA7K)>XlC;}d?8h9GZ*M9U)V;_M|t z<8{wd9uIny;M#hilA6_$;)byT^eRKe>x&|v{Gzj}0xWjsZp?_mgcw#`#kn=QEU5l! zHqM(hz#kelm+@MR>=e(O|8wqI11L4dZ-_)fZFx$e*;&1=nqPyRvI&HN-zztSbD=ns>{$N+)Bw*lzxuUD&8byjl44}s$C4jv z%RdiLp#c>2K%v6)E_@7Gc}b2S!HB06N-anT7>A$s~-i1sh| zIvnbzDEWQbMHE|9Uqp2Ts9B*R@_xLb`^Z#|5)nB)j zPMc@U2H=Tm+~%LNrSN)R21m~(LkEcGjFMifEfTA(ocMUOjl@i^Jv!$)ME;tLId684 zkHhNK{SZ~>QGe>T3n3oJs2+$Y!N`0Q;32(M8@sp@3wq?LzHlJ2kdLzpSAc9=-KT1E zLamRK^R@I_uaYyjqLB=Y4&(Zt=H#6xri#suRZSbV=%`Jgb{Ih z`_Az8O@LPu%_G0_yYE$-hXk*cT1|LK&r5KlzMqeM-m4qEeWe(e3$NzXl3yw3kHM7k z=|D69dNycZZ8Ji2ya*~cMQ@-y_hbiEmdzcbC&8%VBO1F1`BmFseaen8_p5T5NAt!P zcO^d`hviX%s3NSPvI$^jihFF@ZnM^MrM{@KfeK`ZP(_F=`Q7ooL4ua2YV2m?gNn92 z3%%*pGvr3532js1UPMCr#vez*6G^0V|sh7M5ZlM`kvf-GTL z{|hwwZeyu88gv=@L;!CrM*jzML6OQY)KJIr zs0N5CLDl~gu^VOZw>4Nja1DL19fCq2>ZPa(yq1bwlU0}kB=F~@OrGxa*_vOW@wHOj z^>#kS{AW)^Jfl>Bd(DZg*BLz#QGpY` z8RA(?8QakUkCc5tl4U%@D7n3Ac`aluyb;@5hGGh^)y8~Aumw-RCV(L^?>r+n`Nck? zFMLPP=>_LJ(b$OWWDuJVdpuJ@iFk2@86Nf{>n|By+0S|G&jA*ruXJqCK93FCmjEwu zda3ENucr$}Iv}Dw6nzOmh;P(~5Z%u~AD{q_tHJ1OMz5pea^Q{fELq_@e*cNk$a4Wl zACguP;zVRzZnO_0zvG>np!Q0wFuAqb(7w?Qha|!Cl^c=Z?B!^`E%E`BxV$?0S&z4W z74H1M)rQ<1>$TN=C<0qzh2&SLKd-BwTbY|8Jv*lFjES2oFY&e7*toPvaF9DAPo3e3 zjnN!l%-s^BF{s#6we#e23tS4Wdlp555hy=^S2;JhlZH{W8g#D}(x zw!UE7_tyMnT(metp%8n=oePvw6?S~ajTIp3d}O;&zgDybp>~a}+@AY5J5=5)j@}!i zup^K~a_85PSnlMG-^aF{VsZA^#o;;g%stilt8k@^m7Udp{(!Wlu46$T%D4=D6{bc_}r3XP8OEEmsX`Up6*>Wc zDZt)q#}sxD%j~(>Gm4%m3-TCV|9gqArT%)afqco*+gWj!52dW=fzN@* zufi4bbxXk^Dv(isEJhD&Scpgv_iKPFRT-i>MdznjTqEU=70d>&wR1{hB*)z+yM`-{h zI&=YL6(4HX8KYKP*V6fG2o1;0_VzKD+p8Oxt?;PtOjao2dF%`*bc~nW*wwy;M_c7a zMonsy)6lcKsNeV0-)IhtAn)(LH%PDr_4ZV~JaJxCke>H$u}@|G>5o^SW2wHDE!x+! zn*;^XdB?Tpcz^1#sNGAsXDGJYB)oCJ^r_>#(bxCk__YPaxIHlA3fL0l@o`wC_*Gxe zw)gcok-}4Mjd=l$!J;ir(6`ogubT7PZUQgqmBOh%h7>=Z?HFa}Em$y0O{>+jXqX{7 z|DI~B02%d$+9K~q5UW9@`z)9F-4wIpZKM5O+Xk3eiNswr-Wy{TdcD=Zk$1#ku2M7` zZ*AimAo_l!>_#_3JnttXn<_9j9k0<6xKi0MRjdVwF1AskGird0UT-10KOYRKdHSt| zeY~`A3UKtkOn5&D%q91X=iSt*0Wwvl(ahGFG5wQ1Iunhf`XRn8`ydf|b^zH|e;>7% zAuD|QFRTW5et)#p?>%E!r6*E{ofP+dO^nLy(z$BTi|uPWm}`C`X(#HtQAy2qI?bTKvpeK zRR;vY_+1fW>ew$m%`vuP+Z5PGNh)LocQqd$iC@a2BfTo{Xm6>po^p%MF##+EuvCPV zck3G9xi_i-6q+IVJHb(6W7}EVyO=;Kw2E4ws&<$Q1)b~FUn%DrV6QmLtc1mAAN`0r z+U|tna*z7v+mBW}ma3IkM`3>l8ylW>mI%T2Jz@mwQ`q~Vlok9w>+xqXOX(}Xb6>BW z8FTcp3NJzZDL)dx1|g#U-k(}~nu1kuthN|oa@~+6yVe4K9^+ApbzfCVLcGG>wN+l_ z9HlsmhBYL4dm{81_3vp!$j#d~k^C$uT@as>Nfj@-dFM>RXeYoDnzs+x@l?B;zjTg~ zaph+bW-h#05$pTYQ~P-p^9I8xz*^hjbH5jYEpsNJY1AJ-!tSeWlzkIm5oCl2tr!Qk z!-!YdO8{ zZiAiUM~h-9_TpysBir9df8$dWKR)_f+o)o@hT=mCOHrEkX45BGJ}bbEL!WG-fm49~ zvz-Xh%Zk{H)+xrOy`~{SlS$KzjSvV4Ms1jw29%}W$9|g;HuNfRRP_~lssUC9A1S<% zO>H+)Yd-;-^L*?>5=2l3m3O`Rw5h*RttO>G;saR6N`Q6fai5LJICyYHPbj0%jZ zLB_@hHKoqXxEoov6(FJl&$gZ171$fgC^*lUV_XoC$6#f-a;zS*D3#F` zz2i+rB+k4sP1G3~6?nko1>XYtc)S>99XXPD?F__ffHitSz^XiUTs?{tO>G9N{Bd~3cWD6|YiYCW_Cy<+8?P4yF@h_fE4kxFx{J1{8Xs5fU0GO|Z8N8by0 z4R8c2I220FQ%?*aBh`CV3eMHFW{9Xj^cWLsdrI_FninHc;5bI_Dl6}yE7YRzsOq_t z%faUy&kC>l}=}0kFZhJ`h->rQd*#G=rkTlm+4(c3AX!#E7WBk9cj)^5`067e)RO zkDtTs|1r&E2jN9XE(0i0?zN&L5T@wN9fuewaqYB3tp}S8G4)qUs|NVk`@(kcrj}P* zuyzxGY+*sAM*U;vd|^X86Xfl2STCZ3;9ZBYf=;JRR>zAq*Y}}}C){3<$ z(Pq}|v_Mnhg*0490bZLZ=g5l3EpRqLA%StxkGOcxfmM=AA8#Sz*5f;b+BwP69_ z32SbsSj060_2aNSbr3ZYSJD}m79}=6X52wrd6!Ov8R{(I82RyopF_^98M4EFOY!kz zFzOFMU+@FaC^ELmwi`$5^9*&jgI?*NUZmCYktFy1V~!{G{WwepWh=9;^*AhbZ9~2K zI_JnBC&e86ny3f>eGn~Eh>Ez1#(7|T?spvG@Xeu6cnaN%U0uEg*mLfMAA~T%Y>M&S zh_Nb6=#f`G9D#tB^t?pp5CrcZy2n*SRNj8du!RWGMV62Ht8fwJ7g<5c0`ni^5)pZ_ z3p0i%Nlz`sbA}r}9xJ*FVeQfFv?E`XizI^l8-BCmgt=n&o(q2*R?Df@j~Jo0^_YbZ zL(K{*CTRO5+}2v|sW8>r0>NS};oX)pww6614!(c*6r4vXUdW55vtVsKnPc19kHZ=T zg(r9rB&)j?JT4g_vQG7FnChYW3~E~hBHh8n4XfBjKtJ)&$Qj$0St*TSP1RQYkbXRK zdRG#A92TnPka{9ob41nPX7xZ-P;*-&L^pU8V$?8?{LULsniJDrSv(31;dRukOP8qFPdO z7IRFRBgO`kx;5ZyqaFhu>~HjWTW;Pz+JKF z?F86O+(>An_ME#|s#VxWZ=_6sSG}p)2MVwhh2K9hS|CwqyvEMLZn70|I=UHn>-+Yk zwiv}`sYO)v^%UO*N|2=lbPnd`$8+0ATcEmw)d&hr0nWiJa*BnLAwEzn9AAYy1GyPu zwT+l5Yiy2og^j7nvGu#&9IdnQ z)E)2bKN<;PmqhKh5flaLxlr6&6yKjz*5jDDd*`wkXhoNuBUVLbE7-f-x_O=hSAZjo z0*DPnb|vp6zzMIraHZ^e7p{~7-&lBRuhDBS3)=}O;wbl12yFB#ZgS*)3gH{QY3eDv zgSc2V*ct<>yy2J+{&ISQ2esc9Ud1-j7L4vVfhEII#@`}RlI21ubol}HvxvuBKmK@k z{=c@g`g%uYpv%1ehfI2;cbCHno2>RpWH$l)13Z9`Vr>_(DWJ_^Hwn9W`f!Nc~CBM`QheJ?OO z3b1yQ+7$=Xw2}{|I)&X2e6&SAsteWt4=}{ovPZz!9`<4|o?$)^9r5CMY}>l^zjcIC zcE}vA2FM1;c?=tb$6YyAQG&&c^vX!+iWl$jg&4xpD1#*c4LB8;xVnU=(bf@$dIB^<|tBB+ql~4MrbWV;$UfW1xjA|ZT(+#}&4D9!7dRS7^&Ej0I(E@>)(Q2_kbGh|hu704*@JJkNYB81=#of;*seV-6y! z&YIJsEoz+Rh^jFKSt9dq1f?hHIY%mRJi3%QS*Oc3&BG8{$PUb;V_0H-2GQjl!&AFq z=SHty=PyJQV7>iSBP&+R+}O#B-x(P(FYDGka6S@P;2Hn8AA?QJ65+btjAaELbwv`bHIKUQ;kWh^Gwp((dBc_CP4+VH$O7486nPyFYV^Cg&l69 zJpMJ08t>z&C5B$$c#o0vxvWe#tWk=}4_H{*!h=B#(1RIU-N|HFD>nr+p3JGmjgOI+ zJ=%}K)GKy$4sReg?f`p8EyE4DM3@nLia7``AnK2Wnh+C7a2NV$(;72V#{Nq$Q9f&c z5%uS-8MXomnZqL2Z=Jb|rb?l7<$6REU8KU)sR1GoERYYx>OsP8B&w$)kHR0mR0Ff;1Uvom1gHxV9f zgv_Uk;dcIH#RY<#4B;M!h13ZVb%?0KIsjXCErK-^AmSdi3-id0S>I>QN2)-JoXEuw zB12|zM(h$W(;Hk3FrxmvHnnZk<_25=616wCjUo|FeJb_G*o?52EWJlnUm@c!MARP> z2S~hG5E9&sI8jkf+pAF@@0BSxy<{mnMStnoSZdJfuUGQB&5uUV&T!8@^*^R=*Fsc( zj5I}6TO5%Ux#*?z7odLhc!}n zy*@$Q7Gl4-a4oKSwFu1*aXs?dJx?+X>2=kO9y9BUXTp(dOK4eNAPkl^#3u2;E z`hSnuMmE=xBaWc6=YJKBQGQ-!*HU$eZG&hP>W^Kg_Iq;fXnoI`W26G>wi7JUtHBam zYcXC-jwsM8%Gx6;@OWLnDv~y@eG6Rn^%WrFmt~vPKf^1N)=ch zZ+x5)jR6I*C6e9DcyemfPok(AV>N*17zzz?LR9@_kYl8cMC7W^hyZ=`&y*U^h(8QL)g}(+ujQtz zprs#LADyV|S~osgN^hk8YP8yV+uw_0O{{+nS%KF8d$m8yrf%!0IfXJbw!sIoL^?C~ z9#Crw=iDulD?l4-gSJiH3isf->Mf{LV7msmtE|B0X)8d)*`A<5R$!0fYrQWzH$}&^ zF#kr0=PW%}&AH@QK4ol1V_G&XOeVa@W3j9@<W2cm-MUJO--)BI?f?W2rokG90}> z1#r|&sz9@#Q;J5NtqG5!i>ko;YiluMNv<`f^%0xV`xZQ78t{0>8e?5)j|#+VfSzpe z$P+z$H9#aFqwkrdXuMxRYS!Ae&sI44mqt}!qd)5NGU5FsWXGB1y}n3bNXw)CvNP}* z{O15`AWEd|-;9~cHFG7L`qTXTp5xri& zviDSb%L-coG64|@E+()YH9+PvX1mmDn-AtHF;@Z5+YzF(Y!%yIk1YUYtOPZ?g{urfbslZy{A!-o7j9UY1DpMJ3&#Ush#5e)o4)sRDjVy@T{=W0Bg(B{eI6UOF9BW;GhS;y38p?$=~#>; z#y+xQyah&_$^;b>oQ#d75Qt#3D#R* z?*eO$*#ykh?i3rs^M1}gI?K`f(LAcf7zg(-1STMmfVRf;0vy~PF>6$on`-Pqj>^FE zYLC-gR;;vQ#)=IO(Rm0@Op?mjiqKTQe$G_WBho-Qwh!Oqun-03oumH0RS!hwIDvJG z(oOR|{=%p{b}mD`QDa+k6`TY;B^L$J^^TDYOUWs%$TqxXBr3hm>t*Z|B7 z@;jpBr*spG*s*3(bMPv&ae-`oStOF?%9vql;K+NAM_9@Ao5wi3{qQ2jEbsPbW!iMVWSh`B;l6gvzGHHJdZlG zRNTlGQG!K7ek~}}Uk)L96dUu2v7QYwhqDGadcuo`pp?WYM26OGL7@aS`=M1?^f<;> z15^hb6VQkf{Lqn~$4z_x7FJepBAS=10W#!QoVL9&2qtfpRg1G<3BCi?o+Gv}hY-2% z0wW3!QG<-yYuQ>#@UXdqllJ@C_~6k6$ULoiy-Kje$xlso$=hC!&Yo#=#AB=mSc-39 zY#~Qhd@4XB7)J00lV>GAziuf(2l?&orXDgYi(cAxTuBf4jmD#Wwap08@gjIsV5<$; z?eyB(JxDAd^Nf$Gw#j`YKH7(Uzgnm0N6_H>+B9k-0P&Z-J3*EZP>aZ;@cB+UnmFX&{^Y_Ne`N%IVBp5fUtAxT!*) z>&KE9Vvk}GIiBGou67$UR~ugPLlM`iBk(st&>)Hgr?>5AX?|p)Rl5CA^2skUpH?NV z`YVV;h>#ykaFI4A)UlxSR4*6o7@QG9xrl9nSyk6-LsTErb|S&-_38J3QVI6#%9z}& zezdK1JXPC;5cE{pYXS9VujV&Fdi_=#+TUwy)q#u~APo5>=$wVf*vJd75bJAeOlxlM zm<^CY5576k{Sd*c!?Gp?da5VPu451F%{CoAk4U{85@$9dGle)I`XTUJ^k@RbX!pRtc8o+={H~wX<`_Y~zo^ioRCQXOQ1$ zeNvF$dPR3YaVFV;etH_TSLLe-?F{vN0x05<&4KYF@vSqgRDY+pqYS(~OfsDTJ&VNS+;msmT0IDmt!c%>o@I>)GG9mUkz^vB^0h~>K*TM|S zCp5-L0k&S339m1T?8P}g>nd5%M!%xxt^HZ!5yclBe+p#f`ZOZHHEPa+=Weh{UyS-X z?H-cKBDqb7(NnecD8bU`>$Ur`XEbI;L8(1g{#{_p&KuB%G8nBjv&e6R^_u|C8H@ro zpa#53FG_e(ViZChLmo=Obel)r%{o&aZ~sxsuZNaz!0Xw4-Z?6GFR7ROGWz-$DZO5w zXG|&MhbDOEEb&D$qt81>2i~i*nWrkuloZ-N%{M`qK5q~*eU0<^+T`a&)>R=@0;2$D z6QdZjVExqQqv{w%&IT zx!M^@ke@da;SuuFXWiSsj|k9uA0f!>xea(-WR11@BV)&iq>j}eAwgvP2~pC?Bs7t7 z%=AR&a%Ce+QbhaveX*M$*#Q-K-@!#BLT#dk#gjl8dqr+hyNQ7;F* z2;UdIFF^3IBS|B#a93&AcNNJ}r24;Cd;VHfbSjrqpf!K`u z-~P=*fUUOOYz)jIzKprXm_>r94MKz`AY{{O+s(#+>gp=zB0bf?%*=}0JQH9ke!@|b zmva6S(66#xll)4VpEjtSkzSd^w_+{;JCa~29wJm`6>6uM3nRd*wwhhtX?sEjNzJwevCEZ@e+KZ3 z9Wn*8efw*R085!qf-~Ch*oF8!_BgUrK%9=5M6{|M#NsO-vj0SOPJ!xh2A^iiaAh5Y#nVXqjk#TpZ2`sA*=_e4mYKuH& zV0LEl%l8F-3CggV@N=U1`e8bAj@Qoc4U@5soHSrh< z(%EGWKUSs}F^7V9GKrC~abVI_7R|AP90)uN;a{Qz$k@%a5kW;*Yl-g+)utzR^YjE2 zAZa@pNZgpEB#V9(6L2(T26WU_2M z_6D*DYG2~3k(CY!^D@M$%nVYx4)L`xLmLQcfQm6Q zj*)~-ymZI4v+C_TLx2aoDlDSjCf5BJT$}Jl*spT*2fRcyd)y>!?5P0eWR{Nr4=6|} zI>%&U{JMl^VZ)mq*>h}8d_7!`5Z7f@i~!G26VYPD_xlo^2Mbe9O;Q&nJp}8Calfq6 z5nzoO1UBIbFfXX!NWU3m^o0q}3m;KlGqc`G76Dq=w5IpM7sQL?Ug%&Q0ebNw&fgO+ w5_=&5*CW6eUU=aJ5n?}#{=y3{yl`RsKRYrR%_cZ=NB{r;07*qoM6N<$g3B>&DF6Tf literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_AL_88.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_AL_88.png new file mode 100644 index 0000000000000000000000000000000000000000..6bdb72b802c97f5981b6e59a0505d9f390d3de74 GIT binary patch literal 17323 zcmXtAS6EY7+YKdv0R=__L8Ll10!SG{2_>kY;~+&yLX6ZDETJe6dM_5LLlLA3qaX<_ zfq?WPp*JN!2u)B!2?A22`}1A=H|IIexi}a5dEd3yde_?fxs}C@n65-SbnPYn=;0hW zW&Nl{A^wdIN#k3bxi!DI&~J`A=)=_CEkD;bH+_5+T7PbBk$t6)zEu3Ye0h8N;sbyK zpAsvk8saFK;5%(lrh6Xx?Zsm%V;o@LxI*RoY#uvbGxg*nhZbp@jNc_fLyH8hfPQ z^v20GS|baBPRaHUY411*{T3)17b%%N(3;2X%7;O=X*zdO9uT{sYF4OHz}-#Z>Gu%_ zMebNMqqWYB$U%PV7F+AC3dt`?xN%NFTSSO{@(UqS-f{;I3yUkIR4>>CkGBM7aS#V9j_V%tur7aZgWymDqcNTk(<%&?bNdM3-caf52PpR zKiqxL^e8Yj+Lrlav8L{uH_VT<>+pc9qWDQoqjiA)=Hx|@#)LQ*hBP0G?*H~ltw(B^ zr0F_cl%Zl(P=W^s)-w*Jxfx39F+C@(F|ivsh0U2O}r4wvZl zR#_K%UDXnl&-kG-x$IgN3VcGrVfkF&9`NWo!0Ncrs`wYbRehTt8}2+=#c#c;n11lP z;}<{%z}1UP9?34Dae43bUeRnnJt31PY6 zlc4$HBo6SZltJ%wtXFY-0@Vmx+6hC-%1PAnd*Av!r(RkXZYc2zUt8;F;$V3wiqjJ*!3(I-(>H%iZ{ z6o&aL|5)@{X(q8ofrVj?}sa%Y|kr)7uzaev~yfkB%H_6iq4?}30mi(7rtj#^r5UEyOJv9%JKw4wu((a4y zgD^ATMwPvGNZn7+s)|n9uWOQfcpr?0e}FjIemD83QYIdLmu}bPquT459Zm!A2-&$E zjFXU`i1~;gIEG<)YsJ1?M}+m{!WlZ}kf+@&f=?B@ysMP{;HiD=Cv8RlT{%0IRnv+F z8t6lESZt7Xm_A>LoKrxfF+kVMozoMh>r<@sE>qJ$@va<)$RRhx5#HWA)=5niZsd_` z{z+TN<;wQ$83YVo%@w(Yky~m5U1a;rT`B&w%-um%w@+$dQVq_`(7 zxmPz&@ z9m2+xaza>gbc%4<&cT?RME{-1m$vjq=&~h(2eOI*RAPo9dh>54LJF-~Jk?K#lWzTB zm&f*|B$qES?D-s}p4|81PmxI~j()AD9F=0g@ANdwUH~fR$phH?aDq;|pu;y8&OPg! zPnl>{i%{yuTp${#%FI;6S*60iRJr6v2XFW(dKnQ4m0o=qLMf(4D-g-JRX0y5KUEtl z;QOETDi!p{XEn0JhTnThhvDK@mQ`9U2mxX-HQ!7%`gLJKn+oWpevJoyWdyrDusc+sfFHYV~=gEGwfWF?`W4(%Bj$BE1TE zPP8N2yu=0j{P`9-e8m=SdiqHhAU>hst!WRLWbvJjXuK!u730Ah_#PYLy!4giZyqX5oAQ!QZjpyObqeGzl1vJ^EHC&G-sMx=e!Ec_alt~#W;e4z=(hY3PQr;kEqwF_Iw5mAOkaFFzyq3!1(s(DR|3! zo|XibZ(609ysIr_AW(@NyM*(2ULr=nx@GE$t#?zeb9RgA)>1fUBVBe2?|Y7?eDH7t z&F6Tft!H<}CxESw{Wx@d@R*_Cy-i%lYx`uiq1T2Pe5mjme#*lGmEVaSbCMAQk!Og3 zNL{mXoXi8}G}Ku8I1RFNoYNzUR~f>O~<&CPywI z?D0eLYBss^Xsd;Wb-@s%Z zs|ATw*lO22m#u;-2r`2W>qQBxe}Tz%`7LWW&auZ|iw?n7v!)VymrRHg(L1 zx9%P@fAG~T?2^5po*)VsWX?Mge@yaaGD4MjG9K8sOv0r!m=b~Tzs71hlciE-F) z>sk)qcx18_78{$tQmcF&VF^elmkOUQTeOE{Fe{* zo;;$HPHBV4<&z(E9sCJ1m|!6kMWe0OE!1>?r2Q6TWZ+uf=C%2}`6(vv6}bOSU$0fD zzBYB<*O_&!@dPBK_isEqQ}{ZgZ_==XBp#k_c;hDRXECC8m-t1R5C&yuFZZZlXXk%h zSKZF{>fJ&OsO=lr1uS`j^^w!XIS0)y2P^y|4@As%#WPFTnPtIQxPiFbG*jdb5beImhhDn(uRJDx9;;qlT z{Z(olAx&Q=snt-A_iP#X3q^SKvNk9AJ|^%6x>p46-pCRcJts=n42c+2KP{>wswN%; z@&=J~HP>UDlkiq?=YN2`bEc95J?Wb#Zo1S_#b0KBLYHCH=0!rfErKQft@*Y|GueCe zm=|CHsDQS`j$hAozi7{AbnIxI3?vfPPLe@EQ&$n0=35=^A*q`LS~F5Ndyueqr;DdQ z^DZo{3g?y=R}wEKBD0-tAgXVvK#02k=pW$?2fqK{iap~>e_UX6=s}xPS5FcPu zfBsB2$*sdStxz#f>zZmAe)NIL(x7-(|78Q@WZQkcf8EvSiCk zzQF{#3bgiz-^>0qr0yi&b<1Bzc(y)I2t#HI>+r@(Ehfv0>p^2GZY9l$3+34_N(GV} zo-@Aa^l>Cq)2yUp+2i*_GCqzgf{ zGu3cLu_JT_D_3YUL(i2HDSl*SoNgIW0-ZW`(f;_r1pv*}9~W0x!aEanQQ#im^u50% z4JbOAC-|Z0Fj|i*FhsJPX4SLpf7pFq`-GSs7%LuRE!E+ISG0>B0{v$Z@o8NTSThOJ zI`lgX*T|G;f{Mqkqe5ST-YFUidxI~>Am77d1ztJA)|S^IrEF10p;Jz*uFE>L}KIQzl6Afo=h2b8o7`B=Y9=U13gwcg@R z)-&!MFHUx&wwBFCy7>@8S+gcDP2zTDYHeYvdz7n54T1|1Q|_>8j!;f+*fesjiLZ!J zx7EpLttpzCI^1JQ&uNZToKR9#n}2s(C_?ulqh5J}_ET<~(1PN-2lSInH*9{RDheN9 z3go8V{>{@iseihvPu4O#H|>V=fd+xcEOtIsHyn!{lfullr}Xpsr`07vLLz1%->-|Q!H`%65kPsF!5hM{}o;TM%` z{jyez1#OJO*A6CAw(P}${ZGv;CEuUAOU1WSjQNrXzVw8qP3I-4`*t17h5$jhxF00) z_USZqIDUjIYzqov{0J{T>(z_U7Pu;o&#)A7av(!k6)UN#D+ywjZa%5?N4s9@OF853 zAKB9^f(G6vlMl6sfspwmdnkyq(}9cnv@7Rs1fxRo{S-GAQ#X-(=X(H1Ileo5$q`@w zh7d{CBf(jll>J7&`_&AbL7Tfp>F$fT-Bewq>)H`b#Y_Y9U)S73rB3tpPnxfS6L|W| zS#$nU#FuJ~K>4|y(7n5>N+Q}mu+VHpZ2Zfv4O=maF|Y-iI1Tt1mtbFGh-T+S->tN= zemWpvs2DJl`M5!>TxGgynbcEPTBn3kgQ3W0s1}pYSc$+{>e)eqF@KqvEb01plOzvt zKwTIfSNhdHskQO?x}%=a!I9WIHD*hEcG*B9W?91_EZR=+qe#%yZNlXs(=8CACQN3# zEQ<5MNvmA*L-7hgv-{9MBEah%9yOK0N9O-D0yh~3)5rhUN+spphl4`eh$!~GGf2e% ziR(@NIl&1pU*?7_JTwQUo?NZtPv6=#=&V>P+VW9-;NR+2*;~&O zPK8Zv`HGz|Ogoof!arMit){{YF8SG!@YL={6xJ0kqEJSSSh}Tv9199uRZP2($GTp z&J$tUve#VWp=-9}^h+;QCrCB6$J!Ft{L zN?u-&m8MhB5z?<$cZ>LKlBP;34V$u%RnWz(M_icTwc}z6!(uL9*EpLMPoXC9CE6#^ zTKg2DR*VEDx_G7c1>7vcA4osT zxeW>%8oft4->G$7$RO&!=yOJGv55QC%{6(2=7jJjdsJ7w#XKojSq>^AF?+B7Y^LkI z!TXdj9p@`T17C-C+j#>5xGV738Jo>z_qOiRsOc`Uk?Mr4vquOwiZ14vGtfdmUR?56 zDq@S-mif1uJ~<~C@5OV^(7|L|w zBxL6%)1Zt7zbPOUzHBU?*pdDl@<2-HVX$DyZBrEckKCBiV-m!$SE>H1 z*l2vagFQXs9x4q36^l@X3hn-y}g4iqFO(ObE;AvV~vNOYe5buF+CLA-bGzoCv3yK#i3Ll+-jOr^BPaz$3u zy~61fS3bg$T++sF-p0kAZQ3KEr;Yyk8}~JTcY7gH4=Th4w;t*2^^AG%R_L*Tw$l6h zvj)MEcTM8Kz$?HW6~RJr3!yqIvcT9Aov*bnfr@k^nVC0kX3X*T+kWlX`kd@)`IH1g zu;i+WjMCO7C~TQo>Kus0@9Jc4>X)`k$JB7mo%Bk$pp`?eri1`3s% zVb6m(eSe@4ud2`zS&we=U4HR&3-T!8a;7n~j^7EKS(I2fQ}w8B_GBZ_0f?*acL-Cv z<$G?pv@(Ns60ua--l)=0%j`|JMZ6liRHKRc4|g!lH=PR0nNCG4mB(*P|Au6zszPKc zYnAR_h~X3W8PXQ}UiZigIdO!ZtU9-XJJHiAD`D!CYhy^KvEx*zHlng+{e-7ao5vNQ z1Lch~f?E+$;V;_L55a?96rnP)Ew_Y8rTos!j_&%Bm6Y3iC-ZFIo{ed+0A7L_+2^hA zLp5yku;Fvr2;^M--HykEpA?rK$7mcoJHUCC5QY(P>3}BL#L6y{`^$blcd-SeRV+}U z&6qmm(sH&zW1SJFk4Cr&^+-^jx|q+A5GJT$cf$vGANU-bg0KYwt@XI2K9Ghb+d?0 zTIw4IlfhjLLyml4HhSjc^NN!p!tvduE{-&TjK!byPBAiMo~o|aCsvi<1I4^W zOqVi&lgCy~-AX@Nc_qDHK@_(+vYMv(Z*cBz?GHpM!aGbqDi-O-N%!&G7GnB;_~Vl+ zs%a`b8rdO+Az*CKvGkl76OY`Av0ehY*)g8_Y&v6S;3m>0Vs2V8%9)A1W4_}))$1FH zL#`%(as8gtbga|JG4YtO$5|3pgAHAKx49Q7c+W{PxQM{BUHkreQp{XBcz6_L00)|` ze3-S*i|sF@(tJ>Jy!hXja@CuLu}+NTzN;DnH+Xqfn|b#y0DC59^n-KQyIPZRRs2_1 zzU%T`6OUT7J5ZdVJ^FlgF!0zDoq3C_X}(e$x6h%mU9={#eUw(qymEP4jRaCw^=-$l zhe0(>#NAJR=e@3j`2b_~)dvUcz?lZ0xvqNE%j2NA03uw{V;{Qi)v_szbg^-opA*{`*3+|VA;-E(lBiY=YwE3!nWE2gSWP5}6PcnQ>R?Lb;9mUjp0|B}n(k4i%GYlMjRm6sI;Xjv4u%ST4n= z`1(@oxCVwid6C`ykNAF8{~{{Lm+xM*xzu&=o_elkr(;x32S^Mok>N(PHp}=gTLejM zQzpr_z)@@Ma&&{CxyKH8)rzXA|id{(K5V zAr}vR015v7Y<{=wG2S(Me-9Le#7Vtr<7JN3L9+E*ok8~eYqB^h8?W;@6) z(($G~*%yuL5>!9hW>-lX3K$%#c3&loS_=E=D>3GN9T*~j2vO)6F(EU-HI8kr7wPDIAm(x__dsz%d6nS!!W>?2QSX~l z>JsQ9Tj)dm*jb zHB5J2Ap+r<8Lvj6@pVZEjDf?G;UWz;%mX{?HBJ>INt- z#tgqtCuKD}UWqzdXk2<(OL>XyUaiigE!0@F`Q%xfYJjk;*#-y8W!Vyy} zXitFI)rJ*#qdU+)vwPKLaf@0>alF)J1G7=Nl9Pm!3&R0@q+s?xNpPo8$!iPxCa5Yy z=ih?ODuNiP^zhu#wO&~la-^LcF^7FX{0()!<}Dqz&*HCh*&m+Wl|X2BlMD zDg#1bNLlBU{w#X$CD4i>kK)0{^8i$Ltgknzf)Z^%myNXsuPM|g<_A2rhcO@4$qag5TmJLn zIRFbbx_C5>xs*Pff42f1Iq(&kHvz0b!lP;tq)SK($EO#)Dz}g1|Ql2a$0mD5Z z9ULX>K4+6=aHhZ>4lkwVY_9QY6)P#z&*47m0Lh4xPE*=ftDGcW4~a_f3=XeX1)N{k zLKrv#Kd~y<%Gq!E4Tf)IV8AcNM&snmlKa$lU8N1X>#OTO8?Dc_F>xMGp`L8MPsbGA z!jD&h>h(920BZk@p@}3Ex8)D%IALSf1J2`K2{!Pq9L06np(|7>>gg*vnl7{%uao=` zXPW5cwp_3owqEKkv=?L%3|T$(6{5)XK(imUFQIBXU8VnnhuO}-m?9llPOrxqY5O9B zL1FR`)_TqSPWEV5?^Cr9_}{8Fcfk@Y=p)(?mo|1@W&|b$>+%xJPh7-1lC+k)A^pU(>acxVS^J7 z&p(TnHEKHgV|Z82I^j*H)J{DBc5IPZ{_WJWA90WVks^Oi13$`)#*Dx+*VsE9jIoJ; zIol5=5~R;J!o`E@b+>lw5Z-@yvhCQG%#gU#t273CYvH$GhL4br;MvH`>Vr+E`_&y} zrGD4oDd22hIA-Yi?)w^tz%dwSF~NGT`){GR9J%!_d|>g)lwHw>4*l^!g}v`O>z{DD z*9Z7lI(JcXL=G0%_u?Qc(Pr%kRlMwSb8}Dmk^V7YCSi%9rI;IvL}v)wT-T+(w{O_G zDgQXhLn`gy66Zl8&||pM=<|hvjx9`;oAjE&tRHp4@8NNFW*^--Z@vGqK`Ax&1qYZj zsYl(tv?o3>9x!-qM(L}8VI+3gnOA9eqrvbuc1iv(Vi$ksxa9}*piW&?)#%FyJ^k9< zR*8%$7gsGxEQ6lo5S~Q9%dzjhGver4H}TtLALgt)om`~3U5+f^&wm(2h-4-7n|3nV zhT>-z8UoHI(7HAX#|uA5EJu8_lRT4JwrIn8wOMe)B%a(m`%;x&s#>4oK0@afU3)f@ zu92CrOTx7?yDLIKcd{R=94a_EIvO(?MLvi;~^(x^T3F0(Qt8GZlvGg&d$y?vPxagFna>;M$KaR8hfe2 zXP24zp%Sc|8%gvKux@{Xc2R|B@7Nc;slV(ufB3O$(Vj|ygmalT{DkAd|DOd2#zbe2 zx}I@`4N#496hekaMj~@*IH$^k`IviGJ}>pEl!d(>HS_IVscr`0p`IV3TWKvkFT~1u zgjJF3Z1w0`tsRC>vaZW^0<&_lc(T_gU`e_5f$d}|k}PkyME41FFRM6tQ&+*f5$l@N z7w5aF&Gh#SST-VrZG&Iwy;og2>V0gPx5_7f(bw77rqTf4deUU~@NC&BayYsAH3-wp~xFSdG{0+eog;dqHfwT=%j{MF3F4|POll+Z~@!JZbVB{|L7 zaf}QaSb*mHe#Ce|VJg?Mvdc)1D3Mh*)ydykW&C#N2D%?sg_^61=_D+|b3q~vwacRx z-$x|R+6+{FzQQcqcnPq@WOW13+IpMsRRhQ;D>A$4*u|T0mwo6Xto^%c%+qSHS+H%j z#F+x=Cl2+~0^him*z^zWtuP){|O#bC?aH1UPQh&9@ zt>Ra2C%ii2oI_dd7ppQ}#%1Oj!$s_c^NGC53uRL2TZs#8|vUuLVY~j-XAO+(;Fp+La*wf$R zmiSVk2L>y>JlY!SARpov>>E6$8)$xqi;MfsuYpB1cWk6`BQmO)A|K6TJ{_YcuaSZs zIMW-}9@Z>b^jnu6+H>%TV~*z5ZBkd;f05?zA0VNH-C>nQbXWhArWId~U-x zlbw3T$SFrL0bI~`zrI}xz$zFO&iI*us|*nLZYBW}*kc*B@lQv6UEZa(HeC`Kid_FI%iq?yz0MQ%lws;|sh;gqG%;G8^9Sf>&|Nb9eE$J8~Xzqt?Sg z8W%%s;rUY6h}Q3G`SXFf!A|nwmJK2c#dEs$KunopC+f@B7P@R4*#g{2!#V@&NsL#~ z4uVE!$}}DJ&{IcrFmW!Ase(0-^20S?N%e(l<%Yk`H!T)A73QHUa8~$}zjTJ7m^2l5 zIWLZ|XCIyQijTO@oM}?%?x&M$n4w-mor^=SU2x^1JtB>%cB)&#p#Oo6H0c|?lu@S| zBWxm6>z~jx%CrBxpf5|I-u_x}3wSjtzdA=Ag<0=BIPy-p?w`I#CD z=poZDCtyaZ@6>%6xa`%bl|Gr#lR34)(U(!I*=~VlT)Yp@T zack4YR}w?-Ohvw-IVa&TF6STNIS!Q>(e3c#1vZZxwv_$+aosOEB+GdGe)7iY$-X$P z?TUcEAo7B0GX}Z-Cp9%_$7)Y{+E)yV3Zv4qX=ujQ&eYt?&@YlziEbA=O=s3;+P@CH zZmlX{G>|6e?)5E|4R_da4#~{TahuVM=*bgbwaz_)yXEBYsd(DD6B!6CiI2Vg@ah@- z<6)rh$jY;C_Kt$JA~!!XBhT<_Mqq_rOU|!$(%@BUr3UMnqftx2%<{SU5`WltviwTe zU9{>IBCi&mH=hdT6m?cNt9GhPs3_EBS8;8K1Dbm_I)BwQLIKrFD{803$@1P>k5!U- z#>VtE0nZRPEpd0sWnENDr@K~E>)*}GyJ(!VTv;{96jiGv-G1vpQ*gy4pPAoYDR8S- zhnF3yPQ$l~`$i7E!>DzwjrwDnO!gu4U27$db=} zQ~}3+^fOF4UOcwomLQ9C=G*TSzTz&tbl>S4ftA*m^_69NH&%Va-A1-|b{4Km3|O`- zMrEf`sPPNhc|T`ewe-ZFPLy*SIn;+}z$qpTV7uieV)NhL0f_I?XR0DwtfA?pPj^ZEqZ1N9% zE%kc>-TjXqwVx0zy$xi5B(#4b6)3LJje=bgIdN~`6yp3lq7v99@abCWym zNGltLj7Vv7VqfT$$lk`|44fp}X(24F5Dvb!Fl?uxM|Yh|j(6Tmd%nybP!2u$@zI}m zPeCkOKyJ3M`S5<6Qu|-xy=9d#LOTwL5<$B=DIY|&%U(k zUke&6t_97gC{`|)Fzv!;j#xm(i5Wzmy`V`|RbR72FjueVP$Sk_;Exs}@a_edyKI#W zD~IP9U0+d3_rsY8NO&Jds0=YEpd9J^*HemTCbulV?M`>m)UX-9f&VPgo~$lHU(D zG3P)_>H6hgRh!PfZikjoP$lC2EC8+i7n_A7P8eAV!jY=}fUD@eJgoloW&OP=O zSG+D6-AOY&G_f^%1pT$aPVAmP1u2M2B!n&o)brJy_5@?eePDL6qpPaH0Ae6yIJeJ5 zqQ~G`7LM1F6#laBCCwzyPk%|4T{=&m)fj|gnD0(Qp*_1@Wi5U_SJ+c7E+HZ?A!DJd zvI1{(nU*&vj#FQ_opSBYrw-(3W0>bN*#wgE(Rj;lGBRa+#0)#9cOt`Q+2kc(3G{3h zmr|RmOOhj--0Uibbq~-VA4QA=jd>8Pl?>#rW7@ZhUC7+u%<(RJ<14a>Ui6oYy?TM^9b9)Ds~dY!?U4`{K_uGZ}J!pgp**bro3I+an>=E_Mdf#s9FK zoRiBSaKVEF=jU^bCyCUF8#rAVCqW&Ou0W%h2>7eY;|7x{;Go=KB^SJk48wCUz`HD0 z2TO1O$Pc;Nw-i6(OOy+I44Gf8=P#0*P9^g`$i3oyh;QpJ2!wexE?zroQ#H^4jNUoM z?=onUAupfW*FTfRKkd%DgatZ#CnV_~HxX9`1x&@p&&%Xio2{i|_KFqr$cVyzco3z+ z!1KLVC7P>hv`9wu%!ZctyIu&9POw)HPru+i?4MI*55xj*soGFJG7fsPpyhV_O>GkzLvaUl_2zIVg{b1&b)I021b9$IMJ z^#?~Gr$K+74Ko8!Th-~{A4aG*50>Qea9-82@WB3=YJLVUm?hH#eeW7}iy^ABm33JF zCQdfr!hY%KmsDgp7!EM?Cd~vJdtDoEt~nIp-w-<{vA?N#eO**uf|q#IkRH>jmbG_T z`?8-yfGBB+GDuId(GqG(eWlC}KbprvGqOwQJk>dF`>K`^79B#tPMO2(c6yfvO23KZ z>w1K?>B$zTmMa^J|Eu!OHFFIbDn2*@9qegNYKd=0vfq@<38I9)rS=J6+U}LR!HZ2q}Oa8n-WG@x@Te@e{M0qVkyk2y>vfQQF5PTDB38- zBL>HD-O9T`P=Uw7oirWEZspFb%2=nguKhvxU*9@zeb*_wBw9Dmw0*RKY;OM}LhrJS zVVc-k8u_I-Er#v?grLLR-jTA?dw;@gwax1t9VRp8kI06Nz)n9y3pjiKAdnf2AWdX7 z7kWrv5la|aHyS2<;kIZ5Na=ROP6~I+b4d=A?6x|S!XRbCgm84n7L-hNA2@4adT2Lc z{ItEFz8Whf8Yv&48ZlW(_mRB}hxmp)?zj-~I<>%3C{5;B31W~Eh;@uQ6N$pNA)dp$ z%!lI%1Bt3%El@H-*oT1uskKO!kVmo@jL=a3y_R*unocOzuy)k{lA)Y6A_ezO4^p2vK zHL)y%MS*2+s%}v0_d6tPFQk|JypbItvySFCNwqB%)=tS~LT3Y#C=n_Vb1FQEa_cx! zT4D3o9oLzTk*W)n=AnL>ep&iL)ZO;;bGVLhmUYZB9*19|+TutDIB%?Dv?DMM_i+#x zi?$!|IP$E-#+0#0$11rw88YhKB1_HloB;0=yW-b~^yf)nmAb5u_&IF!97qtY^x&WE zy5_0d{(po>d1B_B$&gKBuXx`vzYusc{J06ncmz$z1ZKaIBsX7PcV15kmrtH_p4%ai zsH*(({>}`51o$3j0kq_jVgJ>?ICS^60D2XSfaQKK}k538avp&R4R_ z?GOAIDHrIj=0h3#bN|HU>@9~?@EZfo&xBUJGhFovzt1W=3rh%RxrRO6jWb?<&s+l? z;6%s>UbKcrfM|21LeCE#ha)C&6!Emb86fcZkEraBi#>qzXR+ZLg;= zkPqCz997xh>+9>soG^;TBuPFgx2N1)lbvEQZiRMT&-ii#m;jj;urYl%i^5a3rai)! zTeGyTh{(@Kyr}8a=()Ci+poe4ipD4sqaKEZP)New+0D2u8x%{k#(x#3$mtW}+7XRL zP<G zlUc3sDI1~dgkGHP`e;FT@v@|LbjWf|@#p&f*-Wat*xIO*M8MbsI3!&ze`=Q#tyA4f z%7kyKYU44ApDE9elKI7QFyp$sb&?jtrh{bl6lOhw9t!k4%DaZ-^!D@BeOSJN-2rPs zr(a_HH`7xIi0VHEpozl%qP;Vzz^O{>*p~65p7c%!x>OiFoI&C14(&DB_wWhe=Ds;~ zUF|g{M18FIGVIh?N>Bm;95NO3ejVPzVkM-yCdg>`m|Rimy|8cZ1c!)dq-bXt{_>Xc z+|sSSi8E+gYwsNxr-=@9mhsOPH}S`Qnp{qPo&j{)xHnh|1X>cXm=4H4?Aa_+8ki%; zzSRr*s)A^Hk~;7U>(t9z&0o6!IQ%PdKVPSwmqm!+1;woiq&s{7-7O@s$6U1y3=$;U z96YM{CgxDS{`0t@T)RfzGCK0tpd-@ur;7)d-$Is0DZW1bB zH0p{xw4yoT5tL@PxZI2>4u_NYN`5GF1|5Ylb?-SN8`$W@d-maUn}Cy`zIZyj_i@}8 zey{%L!p7&?^eEj>r`k^;!XacT*@f`qBuQYvrU}F~c@mc!854P0?bPKS`f3|BiSU9f zN>!hb)%8F)0%LSXNIW$CFL*UYCP(_(PH}a8Ms-7X>CYC|uo@Kl%d4&_ttVN&4Ea*p z3P8G!DL1yf{b_}7)|B$x#F&E986BpdP?2bCXfQk&&x*+X71&&8;zN*yOPs+ekYzgG zpIwh{ey4RU3IhKXq_leU z34d4WL)k*5^s1ISe6x^Q%US4{shkbgnfR0~H?A~HTkQ#v)c<79*T&ic4>F%Q5;Za} z^^DC7J!%^-f)Rf=h$YB@S83#8d!dgHWcfaifbSXDBO7IV7HLjZTuS-fa0Mvbt?%cC zVY4|Pl~7^&5~$QFMn?c|i2pm4NBA~r1Yffi>cC4TsYoD0Bq7MU`^n)B8wP`s_JTDe zG5C$tOqf79xZJ*24;&%7aie@J%b#N?0LyakJhlCwE1LXZWbyqNx$ACCI^3t)ILVMrW4yxd&&li~^g zUw+hmkyQ|rEYZ`6@?lpuLTlA;603)GpF&rEdQaw2E8Bxm&fnW#c(b;1EDjBu{Yyjg zHE*JvT$f zx4PXls_zUeZSTl<+$*V zPIGD4fB(Kc)T}+8=Z>hX5RGYxx9^-=+vfYQu;dopF_X8U9(3eQyAQvs>S`|G|0vds z!h@n1?tEyzTwzViejaZ-;7EQMo^Lw&@MV8&&xi~8hV%kdqmQ}8O9_c{5+%uvS%QED z^>3)z-Mjg{{oQK3si~=)8Q0&MwZ3bZNy<)X^Krt6>qrKbf8=7qr$9h&n5t-3r+PAQ z+`nrg#rImtEM?^MV%-dbzgEtPrrabHncQ|B%Ad3Nm%7GYuVpqCK{GE~I__!KG*>4Z zIPPuo;9O*!3u=SDEH@g;5cG$1Pl4$#EvzVCc5E(X}ah#k@}kh zBl(|l0S-*qQf|fZCLub1>m?j_?87D8zrx;P#$d2DwG+GFy*^huKXm?6!R^0Jpoa}J(C-@dq~8>9%|4qgP>&~T z=UtK&y}HpCwgL_tlRPW%pG77nm*5MhhIE>-?ML46SAfnmz=jPfG@Sy7Hqk?=npTZ> z(E$7R=(*lIhq;QV?>;VK4RR>h(FpyRH+@F*M3e(jG;3iU%JEB7tnbzMnMkr2aea?a z-)rZpY^NNLe3LExeSk#7@VPLFK)?GoL%Dzvq+9RL@Yp*hCpPK$16$|IS5^rz5z4z~ z&oqb*oUlTLnqo0UgvAlZque5iP4(n=i}UG39zcfcaZCm07o++;prYEm51&v{Rypp{ z_&TFoKMwU&>s#u@Gz9936nj^1U+&peRV;~96RP(^)*{HtY1P41up&yU7S7d;zFKKS zPgs5J1En{=zpB1h0T({X+gYSIpCK=Imx|(#TvoR-*JQpS&@hdhdT94yAE_(&x*JgP z<9dZ7$xNo_kmeZ0cq@v}UcLl}T}HVdXkn-POY@}I&ZNsZpDMV1a4o^RDB?GHu~adi z3v@=m-9tm7Q-f$}DFnL46v^Q$o_Q#G<(~@@3gS6m-ctv90A+yT1z6!SXxqRqfJ^_u zCWIZ;UCZCW%vJg|j&WSP)(87(h9Yf-XITTYag;6*TnUlFAv)UKX?$|!D={nQCwDEmMz3xF~ zMT^}90NF=&BT-31Q7aQuU-ASTBNgOj9q?Y47`%S6>g6ThHj6Gay~)4v;Zh&Lhtng4 z2Wr?e?9fKtBNY02FXIz^vV!ycLqG#MEGgU7EjL7Z?6l@06idM|km;J7BO6GtPBVlU z3^QZwqd2L{JarNDz*!DB>2~JL+-RlPr#^6X6`?5w2A&U9(Qq_T@cJVvHRSqXk!X{! zF4IfDM9S~o+&uivcau;2;*L))Dh7-uUn(R)w}xDng}xxUMK*}L(K#0e_$|WY+pimT zuhmxEJFaoL4*Q$~Y=!n)|JqtxTPt5J52dU`P2}XJxb|G~%dV6wIE;&rf9`5iHLX{6 zRrTM*7A5wj#NC%_sa1EYj0te2jE29W;n4S^&jLg94|#2BV1@^ zoIHC(cTW7N|u zI~Y1=mtGW*n1juWgC)!(=;>(uQ_kL(*bmJZe_^<5JM!gkr|h3?>m{LaMzO+8$ZR)9 zf**zL=xtaSq(Ao|6kU^zFcD|~rUyMy% zYB4J@@W=K#z`C7F2zz zs@e)7D-8pyCJ+BN*^HI0Znk#S;^2f}-!j8p-rYOSJTWacc$5P7Cy-@i7{~{0<3w7q z*s!wUNNU5+_D8GD+%C_1wrf8M!NvnRbahrkdieJV=a&ZO*597o zl^g7B@h438uEWkzI-!S@YIfwc^k(LMwXc%guEjztzsa`#JsKEZfDR zt~VZw`EyK~bAh8U{63RZ?M?KcR8SQ%6`<(#l=zeFE~7JDn5;_~&(FsI00MXacP{`o zIw>9l%%xSh`5oB8ioJOjTArNvW9;@PW%OQWvmjf8dPi zTc{|O<@51wcxKhL41H91?dR&rPYpSi6AJXK{tb)v0zlR#c-4>WAZwpzDvNZGAz9bY z@0JR>pabC&^c$7W5%0cY{x9wV5&gTU4OKc}bVsZ!A$7979{?O9m!UCR9Jmv;_T7EKasohC z!g2>%k!uYB>qZ&Ne!L71^9_!v&y{XCQ>~6`yKbR(A!&X1NlW zSHLQ3*V)z>K4j)G0-BOn{8oI}@d}X5auqapVbx{V)z;hF+uP&+D}x=4VM?y_JHAyv zC5y_H(CljSO5jy&D{bz;%G2-oM)IsprCbTju68@N#1*^lHYEUO$pCEEzuHE{f0jGo zc2)bG;99ZoYJUf0Rhw>q&*m<$UD1GqI}OPG369-SD)1IFZe* z`gHdt3%&{e+AAj@wHwp=M;Y15o4=~h%G=zoH^8&})*t3xIdM;QWr!{S zm5i0FZ+E<`?{6uE)ctTzu4`lGMMBoMJ6_iJ)k}4bueio5+xP*%F>=q@G9|n9-HEl* z#&2AtUFFUn44JRLWaZU3ov}LG_{~44v*phFEz|ZB0LSRv+N|3AFD#DVQ0|_GjvHuY z``q1!f2F6if5m7J0Ddc9aGQ1a4OuN$+&sT+OjgRPZoOU+j(4wzR~!>nTmar#?w-Vd vpQBuz?7x zqxufUulFZa1#h3Q8zdSiVKmY%=d#o^BC`L^C&hYl_ z+s)PDn5Ki&-DBReef#-$I83kPzi{uK-R^QmBsU>Ue`@PcUH$vbsKwCO8Vzc+WHKZZ b*H`APu@3^;LcfrASiT;SmL0sDY;AkFti6v?jYt|mEeRls5; zvdNh@-CZp9NTau3zkdBHFTC)=3%?=$wE6^Ic;ST?R^eOu30nR+^*mm9;Tk~m<%LV& z8*CYI{EIwa*bOftd!dx_t$h3#^=dE*wih`*Vc?Mg{vtk$i(>}CeegqGURs5AR=xfD z=RadNK~yAp;0qI?kJyy>!s(|bJO{Zc@r6B=0Doq^{rbNS0V;?sI>$xHa6QqnxM;d5 zNH0oslZo=8o)RD@tNH{uf|uC5<2EP4>k%G;jZ@7_d=au*tO$G9*h@jo$*Q+s|Ms;M57B{Rej`sgF+a1~1lR*ZT3NOWNifSj zIN71dOtnQR#*iOFVlxPGfGkg4hX~hZ)!VQC4Jg1>c9Tg^?IJzlAyB8<#1uox4<)Bn zb^tEyB@%mNRhs}?K*(vP%>)uuxo8dHji5@i3F)OIznL~7T*y-(_R6ZaUw{8QuK*P! zrZ`3t2`XHqa1S;|G({9CmY7}^0a|3FAT+@-6jyn3PXUN#mOiWAe*Mqie}%{>ljy9m z2?V&(MRE78B*TQ%$|S!`(n}`9sF&A}U?j8hvg$^FHN2!WyRQ@}39fKa?)KQ5D&BG8 z)EW_>mw+<*kp2N?d#RM!BcEp(v$E=-mfD$T!uja5pZm~uq>pJ|h#4+w+n-y}(D#fv#%aeY+X6G{i&bjb?Lifo=_U>7Az;Hl(_TP;Jq6ez zp=^Rv5@pG~^6cDsOI0@cX|nK>0MCe$-fY_d0WxmdllWT5N|{L{IGg;kYz^!}ggGR* zLuT2wpO3?8ZF{XYk1f*1+D<@*idu4qNyd`3P-=)kwJV$8|tM}^jF_g zywD+o{4z4|$B9l*B8!sSM#P4?N$n!sJywsJ2-Rtr3=vUfwVnN^@>GULldHA^tk0~s zU;p`E{Td+B<{hV=%drhc2vB+Hahx9o8~qNRi844zC?9p@jX^Z#tvqM| z_3<9`kOgH7T6W_r{;hnxAFpm`ee9*T6-KtpqxYyyjZHolqa;3Vj?tQqY>$!6k;*0k zip6!(nBLVvy)>EwwV8)m)UKlRcTxeO>W_Kv9g_@&3u0^2XnjLhz7dc)6e=&MEj+Q{ ziAhgf0}9Z(%BXI)FvF8^%=k;VozS-@<>pax(f5-P4LF61BIBPeqdLRV7?J*DL>J)f z_jg1+g{r|w|GJwY2j*v1Ujb&kFNyCbA=5=@g^nMIXiaPWNPiMCV=5Z&9Uz{v#z*?| zka^~c_->Ey*8rLKsM^a^hChqsd%=+a$_i|}28ig4k;ceUdQNQ-S@c-4%djnDPes(A z*Ap9ki0K3_`X3Qn*uVWjIuW8{ZBf$OZ0qcGlh;N4K8xtA^vWQ?$*DDi&;-XAc`9qU z+K=SCnUgOmZd>59d>?IHnQg7APYgX7%_hIvs%JUsi>#W9Fp3MN&l87iPIPXD^e{Vq zBDA7K)>XlC;}d?8h9GZ*M9U)V;_M|t z<8{wd9uIny;M#hilA6_$;)byT^eRKe>x&|v{Gzj}0xWjsZp?_mgcw#`#kn=QEU5l! zHqM(hz#kelm+@MR>=e(O|8wqI11L4dZ-_)fZFx$e*;&1=nqPyRvI&HN-zztSbD=ns>{$N+)Bw*lzxuUD&8byjl44}s$C4jv z%RdiLp#c>2K%v6)E_@7Gc}b2S!HB06N-anT7>A$s~-i1sh| zIvnbzDEWQbMHE|9Uqp2Ts9B*R@_xLb`^Z#|5)nB)j zPMc@U2H=Tm+~%LNrSN)R21m~(LkEcGjFMifEfTA(ocMUOjl@i^Jv!$)ME;tLId684 zkHhNK{SZ~>QGe>T3n3oJs2+$Y!N`0Q;32(M8@sp@3wq?LzHlJ2kdLzpSAc9=-KT1E zLamRK^R@I_uaYyjqLB=Y4&(Zt=H#6xri#suRZSbV=%`Jgb{Ih z`_Az8O@LPu%_G0_yYE$-hXk*cT1|LK&r5KlzMqeM-m4qEeWe(e3$NzXl3yw3kHM7k z=|D69dNycZZ8Ji2ya*~cMQ@-y_hbiEmdzcbC&8%VBO1F1`BmFseaen8_p5T5NAt!P zcO^d`hviX%s3NSPvI$^jihFF@ZnM^MrM{@KfeK`ZP(_F=`Q7ooL4ua2YV2m?gNn92 z3%%*pGvr3532js1UPMCr#vez*6G^0V|sh7M5ZlM`kvf-GTL z{|hwwZeyu88gv=@L;!CrM*jzML6OQY)KJIr zs0N5CLDl~gu^VOZw>4Nja1DL19fCq2>ZPa(yq1bwlU0}kB=F~@OrGxa*_vOW@wHOj z^>#kS{AW)^Jfl>Bd(DZg*BLz#QGpY` z8RA(?8QakUkCc5tl4U%@D7n3Ac`aluyb;@5hGGh^)y8~Aumw-RCV(L^?>r+n`Nck? zFMLPP=>_LJ(b$OWWDuJVdpuJ@iFk2@86Nf{>n|By+0S|G&jA*ruXJqCK93FCmjEwu zda3ENucr$}Iv}Dw6nzOmh;P(~5Z%u~AD{q_tHJ1OMz5pea^Q{fELq_@e*cNk$a4Wl zACguP;zVRzZnO_0zvG>np!Q0wFuAqb(7w?Qha|!Cl^c=Z?B!^`E%E`BxV$?0S&z4W z74H1M)rQ<1>$TN=C<0qzh2&SLKd-BwTbY|8Jv*lFjES2oFY&e7*toPvaF9DAPo3e3 zjnN!l%-s^BF{s#6we#e23tS4Wdlp555hy=^S2;JhlZH{W8g#D}(x zw!UE7_tyMnT(metp%8n=oePvw6?S~ajTIp3d}O;&zgDybp>~a}+@AY5J5=5)j@}!i zup^K~a_85PSnlMG-^aF{VsZA^#o;;g%stilt8k@^m7Udp{(!Wlu46$T%D4=D6{bc_}r3XP8OEEmsX`Up6*>Wc zDZt)q#}sxD%j~(>Gm4%m3-TCV|9gqArT%)afqco*+gWj!52dW=fzN@* zufi4bbxXk^Dv(isEJhD&Scpgv_iKPFRT-i>MdznjTqEU=70d>&wR1{hB*)z+yM`-{h zI&=YL6(4HX8KYKP*V6fG2o1;0_VzKD+p8Oxt?;PtOjao2dF%`*bc~nW*wwy;M_c7a zMonsy)6lcKsNeV0-)IhtAn)(LH%PDr_4ZV~JaJxCke>H$u}@|G>5o^SW2wHDE!x+! zn*;^XdB?Tpcz^1#sNGAsXDGJYB)oCJ^r_>#(bxCk__YPaxIHlA3fL0l@o`wC_*Gxe zw)gcok-}4Mjd=l$!J;ir(6`ogubT7PZUQgqmBOh%h7>=Z?HFa}Em$y0O{>+jXqX{7 z|DI~B02%d$+9K~q5UW9@`z)9F-4wIpZKM5O+Xk3eiNswr-Wy{TdcD=Zk$1#ku2M7` zZ*AimAo_l!>_#_3JnttXn<_9j9k0<6xKi0MRjdVwF1AskGird0UT-10KOYRKdHSt| zeY~`A3UKtkOn5&D%q91X=iSt*0Wwvl(ahGFG5wQ1Iunhf`XRn8`ydf|b^zH|e;>7% zAuD|QFRTW5et)#p?>%E!r6*E{ofP+dO^nLy(z$BTi|uPWm}`C`X(#HtQAy2qI?bTKvpeK zRR;vY_+1fW>ew$m%`vuP+Z5PGNh)LocQqd$iC@a2BfTo{Xm6>po^p%MF##+EuvCPV zck3G9xi_i-6q+IVJHb(6W7}EVyO=;Kw2E4ws&<$Q1)b~FUn%DrV6QmLtc1mAAN`0r z+U|tna*z7v+mBW}ma3IkM`3>l8ylW>mI%T2Jz@mwQ`q~Vlok9w>+xqXOX(}Xb6>BW z8FTcp3NJzZDL)dx1|g#U-k(}~nu1kuthN|oa@~+6yVe4K9^+ApbzfCVLcGG>wN+l_ z9HlsmhBYL4dm{81_3vp!$j#d~k^C$uT@as>Nfj@-dFM>RXeYoDnzs+x@l?B;zjTg~ zaph+bW-h#05$pTYQ~P-p^9I8xz*^hjbH5jYEpsNJY1AJ-!tSeWlzkIm5oCl2tr!Qk z!-!YdO8{ zZiAiUM~h-9_TpysBir9df8$dWKR)_f+o)o@hT=mCOHrEkX45BGJ}bbEL!WG-fm49~ zvz-Xh%Zk{H)+xrOy`~{SlS$KzjSvV4Ms1jw29%}W$9|g;HuNfRRP_~lssUC9A1S<% zO>H+)Yd-;-^L*?>5=2l3m3O`Rw5h*RttO>G;saR6N`Q6fai5LJICyYHPbj0%jZ zLB_@hHKoqXxEoov6(FJl&$gZ171$fgC^*lUV_XoC$6#f-a;zS*D3#F` zz2i+rB+k4sP1G3~6?nko1>XYtc)S>99XXPD?F__ffHitSz^XiUTs?{tO>G9N{Bd~3cWD6|YiYCW_Cy<+8?P4yF@h_fE4kxFx{J1{8Xs5fU0GO|Z8N8by0 z4R8c2I220FQ%?*aBh`CV3eMHFW{9Xj^cWLsdrI_FninHc;5bI_Dl6}yE7YRzsOq_t z%faUy&kC>l}=}0kFZhJ`h->rQd*#G=rkTlm+4(c3AX!#E7WBk9cj)^5`067e)RO zkDtTs|1r&E2jN9XE(0i0?zN&L5T@wN9fuewaqYB3tp}S8G4)qUs|NVk`@(kcrj}P* zuyzxGY+*sAM*U;vd|^X86Xfl2STCZ3;9ZBYf=;JRR>zAq*Y}}}C){3<$ z(Pq}|v_Mnhg*0490bZLZ=g5l3EpRqLA%StxkGOcxfmM=AA8#Sz*5f;b+BwP69_ z32SbsSj060_2aNSbr3ZYSJD}m79}=6X52wrd6!Ov8R{(I82RyopF_^98M4EFOY!kz zFzOFMU+@FaC^ELmwi`$5^9*&jgI?*NUZmCYktFy1V~!{G{WwepWh=9;^*AhbZ9~2K zI_JnBC&e86ny3f>eGn~Eh>Ez1#(7|T?spvG@Xeu6cnaN%U0uEg*mLfMAA~T%Y>M&S zh_Nb6=#f`G9D#tB^t?pp5CrcZy2n*SRNj8du!RWGMV62Ht8fwJ7g<5c0`ni^5)pZ_ z3p0i%Nlz`sbA}r}9xJ*FVeQfFv?E`XizI^l8-BCmgt=n&o(q2*R?Df@j~Jo0^_YbZ zL(K{*CTRO5+}2v|sW8>r0>NS};oX)pww6614!(c*6r4vXUdW55vtVsKnPc19kHZ=T zg(r9rB&)j?JT4g_vQG7FnChYW3~E~hBHh8n4XfBjKtJ)&$Qj$0St*TSP1RQYkbXRK zdRG#A92TnPka{9ob41nPX7xZ-P;*-&L^pU8V$?8?{LULsniJDrSv(31;dRukOP8qFPdO z7IRFRBgO`kx;5ZyqaFhu>~HjWTW;Pz+JKF z?F86O+(>An_ME#|s#VxWZ=_6sSG}p)2MVwhh2K9hS|CwqyvEMLZn70|I=UHn>-+Yk zwiv}`sYO)v^%UO*N|2=lbPnd`$8+0ATcEmw)d&hr0nWiJa*BnLAwEzn9AAYy1GyPu zwT+l5Yiy2og^j7nvGu#&9IdnQ z)E)2bKN<;PmqhKh5flaLxlr6&6yKjz*5jDDd*`wkXhoNuBUVLbE7-f-x_O=hSAZjo z0*DPnb|vp6zzMIraHZ^e7p{~7-&lBRuhDBS3)=}O;wbl12yFB#ZgS*)3gH{QY3eDv zgSc2V*ct<>yy2J+{&ISQ2esc9Ud1-j7L4vVfhEII#@`}RlI21ubol}HvxvuBKmK@k z{=c@g`g%uYpv%1ehfI2;cbCHno2>RpWH$l)13Z9`Vr>_(DWJ_^Hwn9W`f!Nc~CBM`QheJ?OO z3b1yQ+7$=Xw2}{|I)&X2e6&SAsteWt4=}{ovPZz!9`<4|o?$)^9r5CMY}>l^zjcIC zcE}vA2FM1;c?=tb$6YyAQG&&c^vX!+iWl$jg&4xpD1#*c4LB8;xVnU=(bf@$dIB^<|tBB+ql~4MrbWV;$UfW1xjA|ZT(+#}&4D9!7dRS7^&Ej0I(E@>)(Q2_kbGh|hu704*@JJkNYB81=#of;*seV-6y! z&YIJsEoz+Rh^jFKSt9dq1f?hHIY%mRJi3%QS*Oc3&BG8{$PUb;V_0H-2GQjl!&AFq z=SHty=PyJQV7>iSBP&+R+}O#B-x(P(FYDGka6S@P;2Hn8AA?QJ65+btjAaELbwv`bHIKUQ;kWh^Gwp((dBc_CP4+VH$O7486nPyFYV^Cg&l69 zJpMJ08t>z&C5B$$c#o0vxvWe#tWk=}4_H{*!h=B#(1RIU-N|HFD>nr+p3JGmjgOI+ zJ=%}K)GKy$4sReg?f`p8EyE4DM3@nLia7``AnK2Wnh+C7a2NV$(;72V#{Nq$Q9f&c z5%uS-8MXomnZqL2Z=Jb|rb?l7<$6REU8KU)sR1GoERYYx>OsP8B&w$)kHR0mR0Ff;1Uvom1gHxV9f zgv_Uk;dcIH#RY<#4B;M!h13ZVb%?0KIsjXCErK-^AmSdi3-id0S>I>QN2)-JoXEuw zB12|zM(h$W(;Hk3FrxmvHnnZk<_25=616wCjUo|FeJb_G*o?52EWJlnUm@c!MARP> z2S~hG5E9&sI8jkf+pAF@@0BSxy<{mnMStnoSZdJfuUGQB&5uUV&T!8@^*^R=*Fsc( zj5I}6TO5%Ux#*?z7odLhc!}n zy*@$Q7Gl4-a4oKSwFu1*aXs?dJx?+X>2=kO9y9BUXTp(dOK4eNAPkl^#3u2;E z`hSnuMmE=xBaWc6=YJKBQGQ-!*HU$eZG&hP>W^Kg_Iq;fXnoI`W26G>wi7JUtHBam zYcXC-jwsM8%Gx6;@OWLnDv~y@eG6Rn^%WrFmt~vPKf^1N)=ch zZ+x5)jR6I*C6e9DcyemfPok(AV>N*17zzz?LR9@_kYl8cMC7W^hyZ=`&y*U^h(8QL)g}(+ujQtz zprs#LADyV|S~osgN^hk8YP8yV+uw_0O{{+nS%KF8d$m8yrf%!0IfXJbw!sIoL^?C~ z9#Crw=iDulD?l4-gSJiH3isf->Mf{LV7msmtE|B0X)8d)*`A<5R$!0fYrQWzH$}&^ zF#kr0=PW%}&AH@QK4ol1V_G&XOeVa@W3j9@<W2cm-MUJO--)BI?f?W2rokG90}> z1#r|&sz9@#Q;J5NtqG5!i>ko;YiluMNv<`f^%0xV`xZQ78t{0>8e?5)j|#+VfSzpe z$P+z$H9#aFqwkrdXuMxRYS!Ae&sI44mqt}!qd)5NGU5FsWXGB1y}n3bNXw)CvNP}* z{O15`AWEd|-;9~cHFG7L`qTXTp5xri& zviDSb%L-coG64|@E+()YH9+PvX1mmDn-AtHF;@Z5+YzF(Y!%yIk1YUYtOPZ?g{urfbslZy{A!-o7j9UY1DpMJ3&#Ush#5e)o4)sRDjVy@T{=W0Bg(B{eI6UOF9BW;GhS;y38p?$=~#>; z#y+xQyah&_$^;b>oQ#d75Qt#3D#R* z?*eO$*#ykh?i3rs^M1}gI?K`f(LAcf7zg(-1STMmfVRf;0vy~PF>6$on`-Pqj>^FE zYLC-gR;;vQ#)=IO(Rm0@Op?mjiqKTQe$G_WBho-Qwh!Oqun-03oumH0RS!hwIDvJG z(oOR|{=%p{b}mD`QDa+k6`TY;B^L$J^^TDYOUWs%$TqxXBr3hm>t*Z|B7 z@;jpBr*spG*s*3(bMPv&ae-`oStOF?%9vql;K+NAM_9@Ao5wi3{qQ2jEbsPbW!iMVWSh`B;l6gvzGHHJdZlG zRNTlGQG!K7ek~}}Uk)L96dUu2v7QYwhqDGadcuo`pp?WYM26OGL7@aS`=M1?^f<;> z15^hb6VQkf{Lqn~$4z_x7FJepBAS=10W#!QoVL9&2qtfpRg1G<3BCi?o+Gv}hY-2% z0wW3!QG<-yYuQ>#@UXdqllJ@C_~6k6$ULoiy-Kje$xlso$=hC!&Yo#=#AB=mSc-39 zY#~Qhd@4XB7)J00lV>GAziuf(2l?&orXDgYi(cAxTuBf4jmD#Wwap08@gjIsV5<$; z?eyB(JxDAd^Nf$Gw#j`YKH7(Uzgnm0N6_H>+B9k-0P&Z-J3*EZP>aZ;@cB+UnmFX&{^Y_Ne`N%IVBp5fUtAxT!*) z>&KE9Vvk}GIiBGou67$UR~ugPLlM`iBk(st&>)Hgr?>5AX?|p)Rl5CA^2skUpH?NV z`YVV;h>#ykaFI4A)UlxSR4*6o7@QG9xrl9nSyk6-LsTErb|S&-_38J3QVI6#%9z}& zezdK1JXPC;5cE{pYXS9VujV&Fdi_=#+TUwy)q#u~APo5>=$wVf*vJd75bJAeOlxlM zm<^CY5576k{Sd*c!?Gp?da5VPu451F%{CoAk4U{85@$9dGle)I`XTUJ^k@RbX!pRtc8o+={H~wX<`_Y~zo^ioRCQXOQ1$ zeNvF$dPR3YaVFV;etH_TSLLe-?F{vN0x05<&4KYF@vSqgRDY+pqYS(~OfsDTJ&VNS+;msmT0IDmt!c%>o@I>)GG9mUkz^vB^0h~>K*TM|S zCp5-L0k&S339m1T?8P}g>nd5%M!%xxt^HZ!5yclBe+p#f`ZOZHHEPa+=Weh{UyS-X z?H-cKBDqb7(NnecD8bU`>$Ur`XEbI;L8(1g{#{_p&KuB%G8nBjv&e6R^_u|C8H@ro zpa#53FG_e(ViZChLmo=Obel)r%{o&aZ~sxsuZNaz!0Xw4-Z?6GFR7ROGWz-$DZO5w zXG|&MhbDOEEb&D$qt81>2i~i*nWrkuloZ-N%{M`qK5q~*eU0<^+T`a&)>R=@0;2$D z6QdZjVExqQqv{w%&IT zx!M^@ke@da;SuuFXWiSsj|k9uA0f!>xea(-WR11@BV)&iq>j}eAwgvP2~pC?Bs7t7 z%=AR&a%Ce+QbhaveX*M$*#Q-K-@!#BLT#dk#gjl8dqr+hyNQ7;F* z2;UdIFF^3IBS|B#a93&AcNNJ}r24;Cd;VHfbSjrqpf!K`u z-~P=*fUUOOYz)jIzKprXm_>r94MKz`AY{{O+s(#+>gp=zB0bf?%*=}0JQH9ke!@|b zmva6S(66#xll)4VpEjtSkzSd^w_+{;JCa~29wJm`6>6uM3nRd*wwhhtX?sEjNzJwevCEZ@e+KZ3 z9Wn*8efw*R085!qf-~Ch*oF8!_BgUrK%9=5M6{|M#NsO-vj0SOPJ!xh2A^iiaAh5Y#nVXqjk#TpZ2`sA*=_e4mYKuH& zV0LEl%l8F-3CggV@N=U1`e8bAj@Qoc4U@5soHSrh< z(%EGWKUSs}F^7V9GKrC~abVI_7R|AP90)uN;a{Qz$k@%a5kW;*Yl-g+)utzR^YjE2 zAZa@pNZgpEB#V9(6L2(T26WU_2M z_6D*DYG2~3k(CY!^D@M$%nVYx4)L`xLmLQcfQm6Q zj*)~-ymZI4v+C_TLx2aoDlDSjCf5BJT$}Jl*spT*2fRcyd)y>!?5P0eWR{Nr4=6|} zI>%&U{JMl^VZ)mq*>h}8d_7!`5Z7f@i~!G26VYPD_xlo^2Mbe9O;Q&nJp}8Calfq6 z5nzoO1UBIbFfXX!NWU3m^o0q}3m;KlGqc`G76Dq=w5IpM7sQL?Ug%&Q0ebNw&fgO+ w5_=&5*CW6eUU=aJ5n?}#{=y3{yl`RsKRYrR%_cZ=NB{r;07*qoM6N<$g3B>&DF6Tf literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_L_8.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_L_8.png new file mode 100644 index 0000000000000000000000000000000000000000..6bdb72b802c97f5981b6e59a0505d9f390d3de74 GIT binary patch literal 17323 zcmXtAS6EY7+YKdv0R=__L8Ll10!SG{2_>kY;~+&yLX6ZDETJe6dM_5LLlLA3qaX<_ zfq?WPp*JN!2u)B!2?A22`}1A=H|IIexi}a5dEd3yde_?fxs}C@n65-SbnPYn=;0hW zW&Nl{A^wdIN#k3bxi!DI&~J`A=)=_CEkD;bH+_5+T7PbBk$t6)zEu3Ye0h8N;sbyK zpAsvk8saFK;5%(lrh6Xx?Zsm%V;o@LxI*RoY#uvbGxg*nhZbp@jNc_fLyH8hfPQ z^v20GS|baBPRaHUY411*{T3)17b%%N(3;2X%7;O=X*zdO9uT{sYF4OHz}-#Z>Gu%_ zMebNMqqWYB$U%PV7F+AC3dt`?xN%NFTSSO{@(UqS-f{;I3yUkIR4>>CkGBM7aS#V9j_V%tur7aZgWymDqcNTk(<%&?bNdM3-caf52PpR zKiqxL^e8Yj+Lrlav8L{uH_VT<>+pc9qWDQoqjiA)=Hx|@#)LQ*hBP0G?*H~ltw(B^ zr0F_cl%Zl(P=W^s)-w*Jxfx39F+C@(F|ivsh0U2O}r4wvZl zR#_K%UDXnl&-kG-x$IgN3VcGrVfkF&9`NWo!0Ncrs`wYbRehTt8}2+=#c#c;n11lP z;}<{%z}1UP9?34Dae43bUeRnnJt31PY6 zlc4$HBo6SZltJ%wtXFY-0@Vmx+6hC-%1PAnd*Av!r(RkXZYc2zUt8;F;$V3wiqjJ*!3(I-(>H%iZ{ z6o&aL|5)@{X(q8ofrVj?}sa%Y|kr)7uzaev~yfkB%H_6iq4?}30mi(7rtj#^r5UEyOJv9%JKw4wu((a4y zgD^ATMwPvGNZn7+s)|n9uWOQfcpr?0e}FjIemD83QYIdLmu}bPquT459Zm!A2-&$E zjFXU`i1~;gIEG<)YsJ1?M}+m{!WlZ}kf+@&f=?B@ysMP{;HiD=Cv8RlT{%0IRnv+F z8t6lESZt7Xm_A>LoKrxfF+kVMozoMh>r<@sE>qJ$@va<)$RRhx5#HWA)=5niZsd_` z{z+TN<;wQ$83YVo%@w(Yky~m5U1a;rT`B&w%-um%w@+$dQVq_`(7 zxmPz&@ z9m2+xaza>gbc%4<&cT?RME{-1m$vjq=&~h(2eOI*RAPo9dh>54LJF-~Jk?K#lWzTB zm&f*|B$qES?D-s}p4|81PmxI~j()AD9F=0g@ANdwUH~fR$phH?aDq;|pu;y8&OPg! zPnl>{i%{yuTp${#%FI;6S*60iRJr6v2XFW(dKnQ4m0o=qLMf(4D-g-JRX0y5KUEtl z;QOETDi!p{XEn0JhTnThhvDK@mQ`9U2mxX-HQ!7%`gLJKn+oWpevJoyWdyrDusc+sfFHYV~=gEGwfWF?`W4(%Bj$BE1TE zPP8N2yu=0j{P`9-e8m=SdiqHhAU>hst!WRLWbvJjXuK!u730Ah_#PYLy!4giZyqX5oAQ!QZjpyObqeGzl1vJ^EHC&G-sMx=e!Ec_alt~#W;e4z=(hY3PQr;kEqwF_Iw5mAOkaFFzyq3!1(s(DR|3! zo|XibZ(609ysIr_AW(@NyM*(2ULr=nx@GE$t#?zeb9RgA)>1fUBVBe2?|Y7?eDH7t z&F6Tft!H<}CxESw{Wx@d@R*_Cy-i%lYx`uiq1T2Pe5mjme#*lGmEVaSbCMAQk!Og3 zNL{mXoXi8}G}Ku8I1RFNoYNzUR~f>O~<&CPywI z?D0eLYBss^Xsd;Wb-@s%Z zs|ATw*lO22m#u;-2r`2W>qQBxe}Tz%`7LWW&auZ|iw?n7v!)VymrRHg(L1 zx9%P@fAG~T?2^5po*)VsWX?Mge@yaaGD4MjG9K8sOv0r!m=b~Tzs71hlciE-F) z>sk)qcx18_78{$tQmcF&VF^elmkOUQTeOE{Fe{* zo;;$HPHBV4<&z(E9sCJ1m|!6kMWe0OE!1>?r2Q6TWZ+uf=C%2}`6(vv6}bOSU$0fD zzBYB<*O_&!@dPBK_isEqQ}{ZgZ_==XBp#k_c;hDRXECC8m-t1R5C&yuFZZZlXXk%h zSKZF{>fJ&OsO=lr1uS`j^^w!XIS0)y2P^y|4@As%#WPFTnPtIQxPiFbG*jdb5beImhhDn(uRJDx9;;qlT z{Z(olAx&Q=snt-A_iP#X3q^SKvNk9AJ|^%6x>p46-pCRcJts=n42c+2KP{>wswN%; z@&=J~HP>UDlkiq?=YN2`bEc95J?Wb#Zo1S_#b0KBLYHCH=0!rfErKQft@*Y|GueCe zm=|CHsDQS`j$hAozi7{AbnIxI3?vfPPLe@EQ&$n0=35=^A*q`LS~F5Ndyueqr;DdQ z^DZo{3g?y=R}wEKBD0-tAgXVvK#02k=pW$?2fqK{iap~>e_UX6=s}xPS5FcPu zfBsB2$*sdStxz#f>zZmAe)NIL(x7-(|78Q@WZQkcf8EvSiCk zzQF{#3bgiz-^>0qr0yi&b<1Bzc(y)I2t#HI>+r@(Ehfv0>p^2GZY9l$3+34_N(GV} zo-@Aa^l>Cq)2yUp+2i*_GCqzgf{ zGu3cLu_JT_D_3YUL(i2HDSl*SoNgIW0-ZW`(f;_r1pv*}9~W0x!aEanQQ#im^u50% z4JbOAC-|Z0Fj|i*FhsJPX4SLpf7pFq`-GSs7%LuRE!E+ISG0>B0{v$Z@o8NTSThOJ zI`lgX*T|G;f{Mqkqe5ST-YFUidxI~>Am77d1ztJA)|S^IrEF10p;Jz*uFE>L}KIQzl6Afo=h2b8o7`B=Y9=U13gwcg@R z)-&!MFHUx&wwBFCy7>@8S+gcDP2zTDYHeYvdz7n54T1|1Q|_>8j!;f+*fesjiLZ!J zx7EpLttpzCI^1JQ&uNZToKR9#n}2s(C_?ulqh5J}_ET<~(1PN-2lSInH*9{RDheN9 z3go8V{>{@iseihvPu4O#H|>V=fd+xcEOtIsHyn!{lfullr}Xpsr`07vLLz1%->-|Q!H`%65kPsF!5hM{}o;TM%` z{jyez1#OJO*A6CAw(P}${ZGv;CEuUAOU1WSjQNrXzVw8qP3I-4`*t17h5$jhxF00) z_USZqIDUjIYzqov{0J{T>(z_U7Pu;o&#)A7av(!k6)UN#D+ywjZa%5?N4s9@OF853 zAKB9^f(G6vlMl6sfspwmdnkyq(}9cnv@7Rs1fxRo{S-GAQ#X-(=X(H1Ileo5$q`@w zh7d{CBf(jll>J7&`_&AbL7Tfp>F$fT-Bewq>)H`b#Y_Y9U)S73rB3tpPnxfS6L|W| zS#$nU#FuJ~K>4|y(7n5>N+Q}mu+VHpZ2Zfv4O=maF|Y-iI1Tt1mtbFGh-T+S->tN= zemWpvs2DJl`M5!>TxGgynbcEPTBn3kgQ3W0s1}pYSc$+{>e)eqF@KqvEb01plOzvt zKwTIfSNhdHskQO?x}%=a!I9WIHD*hEcG*B9W?91_EZR=+qe#%yZNlXs(=8CACQN3# zEQ<5MNvmA*L-7hgv-{9MBEah%9yOK0N9O-D0yh~3)5rhUN+spphl4`eh$!~GGf2e% ziR(@NIl&1pU*?7_JTwQUo?NZtPv6=#=&V>P+VW9-;NR+2*;~&O zPK8Zv`HGz|Ogoof!arMit){{YF8SG!@YL={6xJ0kqEJSSSh}Tv9199uRZP2($GTp z&J$tUve#VWp=-9}^h+;QCrCB6$J!Ft{L zN?u-&m8MhB5z?<$cZ>LKlBP;34V$u%RnWz(M_icTwc}z6!(uL9*EpLMPoXC9CE6#^ zTKg2DR*VEDx_G7c1>7vcA4osT zxeW>%8oft4->G$7$RO&!=yOJGv55QC%{6(2=7jJjdsJ7w#XKojSq>^AF?+B7Y^LkI z!TXdj9p@`T17C-C+j#>5xGV738Jo>z_qOiRsOc`Uk?Mr4vquOwiZ14vGtfdmUR?56 zDq@S-mif1uJ~<~C@5OV^(7|L|w zBxL6%)1Zt7zbPOUzHBU?*pdDl@<2-HVX$DyZBrEckKCBiV-m!$SE>H1 z*l2vagFQXs9x4q36^l@X3hn-y}g4iqFO(ObE;AvV~vNOYe5buF+CLA-bGzoCv3yK#i3Ll+-jOr^BPaz$3u zy~61fS3bg$T++sF-p0kAZQ3KEr;Yyk8}~JTcY7gH4=Th4w;t*2^^AG%R_L*Tw$l6h zvj)MEcTM8Kz$?HW6~RJr3!yqIvcT9Aov*bnfr@k^nVC0kX3X*T+kWlX`kd@)`IH1g zu;i+WjMCO7C~TQo>Kus0@9Jc4>X)`k$JB7mo%Bk$pp`?eri1`3s% zVb6m(eSe@4ud2`zS&we=U4HR&3-T!8a;7n~j^7EKS(I2fQ}w8B_GBZ_0f?*acL-Cv z<$G?pv@(Ns60ua--l)=0%j`|JMZ6liRHKRc4|g!lH=PR0nNCG4mB(*P|Au6zszPKc zYnAR_h~X3W8PXQ}UiZigIdO!ZtU9-XJJHiAD`D!CYhy^KvEx*zHlng+{e-7ao5vNQ z1Lch~f?E+$;V;_L55a?96rnP)Ew_Y8rTos!j_&%Bm6Y3iC-ZFIo{ed+0A7L_+2^hA zLp5yku;Fvr2;^M--HykEpA?rK$7mcoJHUCC5QY(P>3}BL#L6y{`^$blcd-SeRV+}U z&6qmm(sH&zW1SJFk4Cr&^+-^jx|q+A5GJT$cf$vGANU-bg0KYwt@XI2K9Ghb+d?0 zTIw4IlfhjLLyml4HhSjc^NN!p!tvduE{-&TjK!byPBAiMo~o|aCsvi<1I4^W zOqVi&lgCy~-AX@Nc_qDHK@_(+vYMv(Z*cBz?GHpM!aGbqDi-O-N%!&G7GnB;_~Vl+ zs%a`b8rdO+Az*CKvGkl76OY`Av0ehY*)g8_Y&v6S;3m>0Vs2V8%9)A1W4_}))$1FH zL#`%(as8gtbga|JG4YtO$5|3pgAHAKx49Q7c+W{PxQM{BUHkreQp{XBcz6_L00)|` ze3-S*i|sF@(tJ>Jy!hXja@CuLu}+NTzN;DnH+Xqfn|b#y0DC59^n-KQyIPZRRs2_1 zzU%T`6OUT7J5ZdVJ^FlgF!0zDoq3C_X}(e$x6h%mU9={#eUw(qymEP4jRaCw^=-$l zhe0(>#NAJR=e@3j`2b_~)dvUcz?lZ0xvqNE%j2NA03uw{V;{Qi)v_szbg^-opA*{`*3+|VA;-E(lBiY=YwE3!nWE2gSWP5}6PcnQ>R?Lb;9mUjp0|B}n(k4i%GYlMjRm6sI;Xjv4u%ST4n= z`1(@oxCVwid6C`ykNAF8{~{{Lm+xM*xzu&=o_elkr(;x32S^Mok>N(PHp}=gTLejM zQzpr_z)@@Ma&&{CxyKH8)rzXA|id{(K5V zAr}vR015v7Y<{=wG2S(Me-9Le#7Vtr<7JN3L9+E*ok8~eYqB^h8?W;@6) z(($G~*%yuL5>!9hW>-lX3K$%#c3&loS_=E=D>3GN9T*~j2vO)6F(EU-HI8kr7wPDIAm(x__dsz%d6nS!!W>?2QSX~l z>JsQ9Tj)dm*jb zHB5J2Ap+r<8Lvj6@pVZEjDf?G;UWz;%mX{?HBJ>INt- z#tgqtCuKD}UWqzdXk2<(OL>XyUaiigE!0@F`Q%xfYJjk;*#-y8W!Vyy} zXitFI)rJ*#qdU+)vwPKLaf@0>alF)J1G7=Nl9Pm!3&R0@q+s?xNpPo8$!iPxCa5Yy z=ih?ODuNiP^zhu#wO&~la-^LcF^7FX{0()!<}Dqz&*HCh*&m+Wl|X2BlMD zDg#1bNLlBU{w#X$CD4i>kK)0{^8i$Ltgknzf)Z^%myNXsuPM|g<_A2rhcO@4$qag5TmJLn zIRFbbx_C5>xs*Pff42f1Iq(&kHvz0b!lP;tq)SK($EO#)Dz}g1|Ql2a$0mD5Z z9ULX>K4+6=aHhZ>4lkwVY_9QY6)P#z&*47m0Lh4xPE*=ftDGcW4~a_f3=XeX1)N{k zLKrv#Kd~y<%Gq!E4Tf)IV8AcNM&snmlKa$lU8N1X>#OTO8?Dc_F>xMGp`L8MPsbGA z!jD&h>h(920BZk@p@}3Ex8)D%IALSf1J2`K2{!Pq9L06np(|7>>gg*vnl7{%uao=` zXPW5cwp_3owqEKkv=?L%3|T$(6{5)XK(imUFQIBXU8VnnhuO}-m?9llPOrxqY5O9B zL1FR`)_TqSPWEV5?^Cr9_}{8Fcfk@Y=p)(?mo|1@W&|b$>+%xJPh7-1lC+k)A^pU(>acxVS^J7 z&p(TnHEKHgV|Z82I^j*H)J{DBc5IPZ{_WJWA90WVks^Oi13$`)#*Dx+*VsE9jIoJ; zIol5=5~R;J!o`E@b+>lw5Z-@yvhCQG%#gU#t273CYvH$GhL4br;MvH`>Vr+E`_&y} zrGD4oDd22hIA-Yi?)w^tz%dwSF~NGT`){GR9J%!_d|>g)lwHw>4*l^!g}v`O>z{DD z*9Z7lI(JcXL=G0%_u?Qc(Pr%kRlMwSb8}Dmk^V7YCSi%9rI;IvL}v)wT-T+(w{O_G zDgQXhLn`gy66Zl8&||pM=<|hvjx9`;oAjE&tRHp4@8NNFW*^--Z@vGqK`Ax&1qYZj zsYl(tv?o3>9x!-qM(L}8VI+3gnOA9eqrvbuc1iv(Vi$ksxa9}*piW&?)#%FyJ^k9< zR*8%$7gsGxEQ6lo5S~Q9%dzjhGver4H}TtLALgt)om`~3U5+f^&wm(2h-4-7n|3nV zhT>-z8UoHI(7HAX#|uA5EJu8_lRT4JwrIn8wOMe)B%a(m`%;x&s#>4oK0@afU3)f@ zu92CrOTx7?yDLIKcd{R=94a_EIvO(?MLvi;~^(x^T3F0(Qt8GZlvGg&d$y?vPxagFna>;M$KaR8hfe2 zXP24zp%Sc|8%gvKux@{Xc2R|B@7Nc;slV(ufB3O$(Vj|ygmalT{DkAd|DOd2#zbe2 zx}I@`4N#496hekaMj~@*IH$^k`IviGJ}>pEl!d(>HS_IVscr`0p`IV3TWKvkFT~1u zgjJF3Z1w0`tsRC>vaZW^0<&_lc(T_gU`e_5f$d}|k}PkyME41FFRM6tQ&+*f5$l@N z7w5aF&Gh#SST-VrZG&Iwy;og2>V0gPx5_7f(bw77rqTf4deUU~@NC&BayYsAH3-wp~xFSdG{0+eog;dqHfwT=%j{MF3F4|POll+Z~@!JZbVB{|L7 zaf}QaSb*mHe#Ce|VJg?Mvdc)1D3Mh*)ydykW&C#N2D%?sg_^61=_D+|b3q~vwacRx z-$x|R+6+{FzQQcqcnPq@WOW13+IpMsRRhQ;D>A$4*u|T0mwo6Xto^%c%+qSHS+H%j z#F+x=Cl2+~0^him*z^zWtuP){|O#bC?aH1UPQh&9@ zt>Ra2C%ii2oI_dd7ppQ}#%1Oj!$s_c^NGC53uRL2TZs#8|vUuLVY~j-XAO+(;Fp+La*wf$R zmiSVk2L>y>JlY!SARpov>>E6$8)$xqi;MfsuYpB1cWk6`BQmO)A|K6TJ{_YcuaSZs zIMW-}9@Z>b^jnu6+H>%TV~*z5ZBkd;f05?zA0VNH-C>nQbXWhArWId~U-x zlbw3T$SFrL0bI~`zrI}xz$zFO&iI*us|*nLZYBW}*kc*B@lQv6UEZa(HeC`Kid_FI%iq?yz0MQ%lws;|sh;gqG%;G8^9Sf>&|Nb9eE$J8~Xzqt?Sg z8W%%s;rUY6h}Q3G`SXFf!A|nwmJK2c#dEs$KunopC+f@B7P@R4*#g{2!#V@&NsL#~ z4uVE!$}}DJ&{IcrFmW!Ase(0-^20S?N%e(l<%Yk`H!T)A73QHUa8~$}zjTJ7m^2l5 zIWLZ|XCIyQijTO@oM}?%?x&M$n4w-mor^=SU2x^1JtB>%cB)&#p#Oo6H0c|?lu@S| zBWxm6>z~jx%CrBxpf5|I-u_x}3wSjtzdA=Ag<0=BIPy-p?w`I#CD z=poZDCtyaZ@6>%6xa`%bl|Gr#lR34)(U(!I*=~VlT)Yp@T zack4YR}w?-Ohvw-IVa&TF6STNIS!Q>(e3c#1vZZxwv_$+aosOEB+GdGe)7iY$-X$P z?TUcEAo7B0GX}Z-Cp9%_$7)Y{+E)yV3Zv4qX=ujQ&eYt?&@YlziEbA=O=s3;+P@CH zZmlX{G>|6e?)5E|4R_da4#~{TahuVM=*bgbwaz_)yXEBYsd(DD6B!6CiI2Vg@ah@- z<6)rh$jY;C_Kt$JA~!!XBhT<_Mqq_rOU|!$(%@BUr3UMnqftx2%<{SU5`WltviwTe zU9{>IBCi&mH=hdT6m?cNt9GhPs3_EBS8;8K1Dbm_I)BwQLIKrFD{803$@1P>k5!U- z#>VtE0nZRPEpd0sWnENDr@K~E>)*}GyJ(!VTv;{96jiGv-G1vpQ*gy4pPAoYDR8S- zhnF3yPQ$l~`$i7E!>DzwjrwDnO!gu4U27$db=} zQ~}3+^fOF4UOcwomLQ9C=G*TSzTz&tbl>S4ftA*m^_69NH&%Va-A1-|b{4Km3|O`- zMrEf`sPPNhc|T`ewe-ZFPLy*SIn;+}z$qpTV7uieV)NhL0f_I?XR0DwtfA?pPj^ZEqZ1N9% zE%kc>-TjXqwVx0zy$xi5B(#4b6)3LJje=bgIdN~`6yp3lq7v99@abCWym zNGltLj7Vv7VqfT$$lk`|44fp}X(24F5Dvb!Fl?uxM|Yh|j(6Tmd%nybP!2u$@zI}m zPeCkOKyJ3M`S5<6Qu|-xy=9d#LOTwL5<$B=DIY|&%U(k zUke&6t_97gC{`|)Fzv!;j#xm(i5Wzmy`V`|RbR72FjueVP$Sk_;Exs}@a_edyKI#W zD~IP9U0+d3_rsY8NO&Jds0=YEpd9J^*HemTCbulV?M`>m)UX-9f&VPgo~$lHU(D zG3P)_>H6hgRh!PfZikjoP$lC2EC8+i7n_A7P8eAV!jY=}fUD@eJgoloW&OP=O zSG+D6-AOY&G_f^%1pT$aPVAmP1u2M2B!n&o)brJy_5@?eePDL6qpPaH0Ae6yIJeJ5 zqQ~G`7LM1F6#laBCCwzyPk%|4T{=&m)fj|gnD0(Qp*_1@Wi5U_SJ+c7E+HZ?A!DJd zvI1{(nU*&vj#FQ_opSBYrw-(3W0>bN*#wgE(Rj;lGBRa+#0)#9cOt`Q+2kc(3G{3h zmr|RmOOhj--0Uibbq~-VA4QA=jd>8Pl?>#rW7@ZhUC7+u%<(RJ<14a>Ui6oYy?TM^9b9)Ds~dY!?U4`{K_uGZ}J!pgp**bro3I+an>=E_Mdf#s9FK zoRiBSaKVEF=jU^bCyCUF8#rAVCqW&Ou0W%h2>7eY;|7x{;Go=KB^SJk48wCUz`HD0 z2TO1O$Pc;Nw-i6(OOy+I44Gf8=P#0*P9^g`$i3oyh;QpJ2!wexE?zroQ#H^4jNUoM z?=onUAupfW*FTfRKkd%DgatZ#CnV_~HxX9`1x&@p&&%Xio2{i|_KFqr$cVyzco3z+ z!1KLVC7P>hv`9wu%!ZctyIu&9POw)HPru+i?4MI*55xj*soGFJG7fsPpyhV_O>GkzLvaUl_2zIVg{b1&b)I021b9$IMJ z^#?~Gr$K+74Ko8!Th-~{A4aG*50>Qea9-82@WB3=YJLVUm?hH#eeW7}iy^ABm33JF zCQdfr!hY%KmsDgp7!EM?Cd~vJdtDoEt~nIp-w-<{vA?N#eO**uf|q#IkRH>jmbG_T z`?8-yfGBB+GDuId(GqG(eWlC}KbprvGqOwQJk>dF`>K`^79B#tPMO2(c6yfvO23KZ z>w1K?>B$zTmMa^J|Eu!OHFFIbDn2*@9qegNYKd=0vfq@<38I9)rS=J6+U}LR!HZ2q}Oa8n-WG@x@Te@e{M0qVkyk2y>vfQQF5PTDB38- zBL>HD-O9T`P=Uw7oirWEZspFb%2=nguKhvxU*9@zeb*_wBw9Dmw0*RKY;OM}LhrJS zVVc-k8u_I-Er#v?grLLR-jTA?dw;@gwax1t9VRp8kI06Nz)n9y3pjiKAdnf2AWdX7 z7kWrv5la|aHyS2<;kIZ5Na=ROP6~I+b4d=A?6x|S!XRbCgm84n7L-hNA2@4adT2Lc z{ItEFz8Whf8Yv&48ZlW(_mRB}hxmp)?zj-~I<>%3C{5;B31W~Eh;@uQ6N$pNA)dp$ z%!lI%1Bt3%El@H-*oT1uskKO!kVmo@jL=a3y_R*unocOzuy)k{lA)Y6A_ezO4^p2vK zHL)y%MS*2+s%}v0_d6tPFQk|JypbItvySFCNwqB%)=tS~LT3Y#C=n_Vb1FQEa_cx! zT4D3o9oLzTk*W)n=AnL>ep&iL)ZO;;bGVLhmUYZB9*19|+TutDIB%?Dv?DMM_i+#x zi?$!|IP$E-#+0#0$11rw88YhKB1_HloB;0=yW-b~^yf)nmAb5u_&IF!97qtY^x&WE zy5_0d{(po>d1B_B$&gKBuXx`vzYusc{J06ncmz$z1ZKaIBsX7PcV15kmrtH_p4%ai zsH*(({>}`51o$3j0kq_jVgJ>?ICS^60D2XSfaQKK}k538avp&R4R_ z?GOAIDHrIj=0h3#bN|HU>@9~?@EZfo&xBUJGhFovzt1W=3rh%RxrRO6jWb?<&s+l? z;6%s>UbKcrfM|21LeCE#ha)C&6!Emb86fcZkEraBi#>qzXR+ZLg;= zkPqCz997xh>+9>soG^;TBuPFgx2N1)lbvEQZiRMT&-ii#m;jj;urYl%i^5a3rai)! zTeGyTh{(@Kyr}8a=()Ci+poe4ipD4sqaKEZP)New+0D2u8x%{k#(x#3$mtW}+7XRL zP<G zlUc3sDI1~dgkGHP`e;FT@v@|LbjWf|@#p&f*-Wat*xIO*M8MbsI3!&ze`=Q#tyA4f z%7kyKYU44ApDE9elKI7QFyp$sb&?jtrh{bl6lOhw9t!k4%DaZ-^!D@BeOSJN-2rPs zr(a_HH`7xIi0VHEpozl%qP;Vzz^O{>*p~65p7c%!x>OiFoI&C14(&DB_wWhe=Ds;~ zUF|g{M18FIGVIh?N>Bm;95NO3ejVPzVkM-yCdg>`m|Rimy|8cZ1c!)dq-bXt{_>Xc z+|sSSi8E+gYwsNxr-=@9mhsOPH}S`Qnp{qPo&j{)xHnh|1X>cXm=4H4?Aa_+8ki%; zzSRr*s)A^Hk~;7U>(t9z&0o6!IQ%PdKVPSwmqm!+1;woiq&s{7-7O@s$6U1y3=$;U z96YM{CgxDS{`0t@T)RfzGCK0tpd-@ur;7)d-$Is0DZW1bB zH0p{xw4yoT5tL@PxZI2>4u_NYN`5GF1|5Ylb?-SN8`$W@d-maUn}Cy`zIZyj_i@}8 zey{%L!p7&?^eEj>r`k^;!XacT*@f`qBuQYvrU}F~c@mc!854P0?bPKS`f3|BiSU9f zN>!hb)%8F)0%LSXNIW$CFL*UYCP(_(PH}a8Ms-7X>CYC|uo@Kl%d4&_ttVN&4Ea*p z3P8G!DL1yf{b_}7)|B$x#F&E986BpdP?2bCXfQk&&x*+X71&&8;zN*yOPs+ekYzgG zpIwh{ey4RU3IhKXq_leU z34d4WL)k*5^s1ISe6x^Q%US4{shkbgnfR0~H?A~HTkQ#v)c<79*T&ic4>F%Q5;Za} z^^DC7J!%^-f)Rf=h$YB@S83#8d!dgHWcfaifbSXDBO7IV7HLjZTuS-fa0Mvbt?%cC zVY4|Pl~7^&5~$QFMn?c|i2pm4NBA~r1Yffi>cC4TsYoD0Bq7MU`^n)B8wP`s_JTDe zG5C$tOqf79xZJ*24;&%7aie@J%b#N?0LyakJhlCwE1LXZWbyqNx$ACCI^3t)ILVMrW4yxd&&li~^g zUw+hmkyQ|rEYZ`6@?lpuLTlA;603)GpF&rEdQaw2E8Bxm&fnW#c(b;1EDjBu{Yyjg zHE*JvT$f zx4PXls_zUeZSTl<+$*V zPIGD4fB(Kc)T}+8=Z>hX5RGYxx9^-=+vfYQu;dopF_X8U9(3eQyAQvs>S`|G|0vds z!h@n1?tEyzTwzViejaZ-;7EQMo^Lw&@MV8&&xi~8hV%kdqmQ}8O9_c{5+%uvS%QED z^>3)z-Mjg{{oQK3si~=)8Q0&MwZ3bZNy<)X^Krt6>qrKbf8=7qr$9h&n5t-3r+PAQ z+`nrg#rImtEM?^MV%-dbzgEtPrrabHncQ|B%Ad3Nm%7GYuVpqCK{GE~I__!KG*>4Z zIPPuo;9O*!3u=SDEH@g;5cG$1Pl4$#EvzVCc5E(X}ah#k@}kh zBl(|l0S-*qQf|fZCLub1>m?j_?87D8zrx;P#$d2DwG+GFy*^huKXm?6!R^0Jpoa}J(C-@dq~8>9%|4qgP>&~T z=UtK&y}HpCwgL_tlRPW%pG77nm*5MhhIE>-?ML46SAfnmz=jPfG@Sy7Hqk?=npTZ> z(E$7R=(*lIhq;QV?>;VK4RR>h(FpyRH+@F*M3e(jG;3iU%JEB7tnbzMnMkr2aea?a z-)rZpY^NNLe3LExeSk#7@VPLFK)?GoL%Dzvq+9RL@Yp*hCpPK$16$|IS5^rz5z4z~ z&oqb*oUlTLnqo0UgvAlZque5iP4(n=i}UG39zcfcaZCm07o++;prYEm51&v{Rypp{ z_&TFoKMwU&>s#u@Gz9936nj^1U+&peRV;~96RP(^)*{HtY1P41up&yU7S7d;zFKKS zPgs5J1En{=zpB1h0T({X+gYSIpCK=Imx|(#TvoR-*JQpS&@hdhdT94yAE_(&x*JgP z<9dZ7$xNo_kmeZ0cq@v}UcLl}T}HVdXkn-POY@}I&ZNsZpDMV1a4o^RDB?GHu~adi z3v@=m-9tm7Q-f$}DFnL46v^Q$o_Q#G<(~@@3gS6m-ctv90A+yT1z6!SXxqRqfJ^_u zCWIZ;UCZCW%vJg|j&WSP)(87(h9Yf-XITTYag;6*TnUlFAv)UKX?$|!D={nQCwDEmMz3xF~ zMT^}90NF=&BT-31Q7aQuU-ASTBNgOj9q?Y47`%S6>g6ThHj6Gay~)4v;Zh&Lhtng4 z2Wr?e?9fKtBNY02FXIz^vV!ycLqG#MEGgU7EjL7Z?6l@06idM|km;J7BO6GtPBVlU z3^QZwqd2L{JarNDz*!DB>2~JL+-RlPr#^6X6`?5w2A&U9(Qq_T@cJVvHRSqXk!X{! zF4IfDM9S~o+&uivcau;2;*L))Dh7-uUn(R)w}xDng}xxUMK*}L(K#0e_$|WY+pimT zuhmxEJFaoL4*Q$~Y=!n)|JqtxTPt5J52dU`P2}XJxb|G~%dV6wIE;&rf9`5iHLX{6 zRrTM*7A5wj#NC%_sa1EYj0te2jE29W;n4S^&jLg94|#2BV1@^ zoIHC(cTW7N|u zI~Y1=mtGW*n1juWgC)!(=;>(uQ_kL(*bmJZe_^<5JM!gkr|h3?>m{LaMzO+8$ZR)9 zf**zL=xtaSq(Ao|6kU^zFcD|~rUyMy% zYB4J@@W=K#z`C7F2zz zs@e)7D-8pyCJ+BN*^HI0Znk#S;^2f}-!j8p-rYOSJTWacc$5P7Cy-@i7{~{0<3w7q z*s!wUNNU5+_D8GD+%C_1wrf8M!NvnRbahrkdieJV=a&ZO*597o zl^g7B@h438uEWkzI-!S@YIfwc^k(LMwXc%guEjztzsa`#JsKEZfDR zt~VZw`EyK~bAh8U{63RZ?M?KcR8SQ%6`<(#l=zeFE~7JDn5;_~&(FsI00MXacP{`o zIw>9l%%xSh`5oB8ioJOjTArNvW9;@PW%OQWvmjf8dPi zTc{|O<@51wcxKhL41H91?dR&rPYpSi6AJXK{tb)v0zlR#c-4>WAZwpzDvNZGAz9bY z@0JR>pabC&^c$7W5%0cY{x9wV5&gTU4OKc}bVsZ!A$7979{?O9m!UCR9Jmv;_T7EKasohC z!g2>%k!uYB>qZ&Ne!L71^9_!v&y{XCQ>~6`yKbR(A!&X1NlW zSHLQ3*V)z>K4j)G0-BOn{8oI}@d}X5auqapVbx{V)z;hF+uP&+D}x=4VM?y_JHAyv zC5y_H(CljSO5jy&D{bz;%G2-oM)IsprCbTju68@N#1*^lHYEUO$pCEEzuHE{f0jGo zc2)bG;99ZoYJUf0Rhw>q&*m<$UD1GqI}OPG369-SD)1IFZe* z`gHdt3%&{e+AAj@wHwp=M;Y15o4=~h%G=zoH^8&})*t3xIdM;QWr!{S zm5i0FZ+E<`?{6uE)ctTzu4`lGMMBoMJ6_iJ)k}4bueio5+xP*%F>=q@G9|n9-HEl* z#&2AtUFFUn44JRLWaZU3ov}LG_{~44v*phFEz|ZB0LSRv+N|3AFD#DVQ0|_GjvHuY z``q1!f2F6if5m7J0Ddc9aGQ1a4OuN$+&sT+OjgRPZoOU+j(4wzR~!>nTmar#?w-Vd v}3|Ln`LHy?Zr=lkQf(4xI@oUcC;M*5s-OcKp3g2+S~@*7avBrIm(^dn88TcM8Y7rEjtDs@ zOk-f$$Rg08+TftW$dbgVAmBY@rD9iQMNV7#^)-XV*{4N6f2W7)hK4F}oOva+=l{Qb z|5!J8u{2iNbl$6_mt*x!)PMFHU^ti&js{H>pITnzry4=otcOPGw z%n;V8aG`x~)A#$2ukkak5prD6yLeIgcbTtB4M2nEJnngM_gC$!&5Sk$Wk2@K+a5ND zVf7rQ#@el2_wHt|m`}!G3{mwLQ zC#nCMLNy+Lj&1j5;AJvcbl0yz$wTO~9sBkJ4r^kWGs0CGE$6ojE9`On^K4T!122aG zm%2m5$!B)+otZKM8U!sE4{@1&wUiZeU}pYuj#D6R!_4!qFEB{SEJ*ij_`p2-#~nWY z1&mBpPgywpZ|r$q?!~|>bU|6&LE%~^V+mJ^$}`huVNlX~D7}2Y%yPC135>RdOrYd; zr(pV|r(YPP3>Jt34eCE@=|7o4%47k%A1FPx6opS(70JLWb72-(cjxmwmGH|9QZ@@z zfksbb5IP&{vF#b7NrQv-S!Rw4T?|5}@3P0TTu@-rEfI8R5M!C(@y=?+D~$#=A&Y2p zMiz_N3_{26vW3U8UPxd{f2Gw>U@}{0J23TMjb(Uy;XvdrZUvd6&rX%cvRzofl>Wk~ zfgxSML21Y82NUy{GZGpXmr6U_=sr6oeHY(?gRFg5N*NNhXKUEMv}<6Ku<-g0H2;(U z(6>*7pS6XzaVYHZ{u7ZrgFz}IA^$Loz_m@0o2~^gn%p=ruZ>G#PtxQkvWHnOG%#07 z>NR}mIXkhOSJI)G?@i(fhC}6Mu6Bkw47@fQu5JJ&qJKv0HCZnh)ON<`iTz@}C%kP( z$kj`~TywVS9F9LU-ITj(+3UMr3^$Xy7{YobTW!VOcU<2q{xNy={k8E2-t@&SxqjTA zd*P3b?U{ZJp}EQzPB-~*ChEU8PoAyS5PF|~%CUk1yon}3|Ln`LHy?Zr=lkQf(4xI@oUcC;M*5s-OcKp3g2+S~@*7avBrIm(^dn88TcM8Y7rEjtDs@ zOk-f$$Rg08+TftW$dbgVAmBY@rD9iQMNV7#^)-XV*{4N6f2W7)hK4F}oOva+=l{Qb z|5!J8u{2iNbl$6_mt*x!)PMFHU^ti&js{H>pITnzry4=otcOPGw z%n;V8aG`x~)A#$2ukkak5prD6yLeIgcbTtB4M2nEJnngM_gC$!&5Sk$Wk2@K+a5ND zVf7rQ#@el2_wHt|m`}!G3{mwLQ zC#nCMLNy+Lj&1j5;AJvcbl0yz$wTO~9sBkJ4r^kWGs0CGE$6ojE9`On^K4T!122aG zm%2m5$!B)+otZKM8U!sE4{@1&wUiZeU}pYuj#D6R!_4!qFEB{SEJ*ij_`p2-#~nWY z1&mBpPgywpZ|r$q?!~|>bU|6&LE%~^V+mJ^$}`huVNlX~D7}2Y%yPC135>RdOrYd; zr(pV|r(YPP3>Jt34eCE@=|7o4%47k%A1FPx6opS(70JLWb72-(cjxmwmGH|9QZ@@z zfksbb5IP&{vF#b7NrQv-S!Rw4T?|5}@3P0TTu@-rEfI8R5M!C(@y=?+D~$#=A&Y2p zMiz_N3_{26vW3U8UPxd{f2Gw>U@}{0J23TMjb(Uy;XvdrZUvd6&rX%cvRzofl>Wk~ zfgxSML21Y82NUy{GZGpXmr6U_=sr6oeHY(?gRFg5N*NNhXKUEMv}<6Ku<-g0H2;(U z(6>*7pS6XzaVYHZ{u7ZrgFz}IA^$Loz_m@0o2~^gn%p=ruZ>G#PtxQkvWHnOG%#07 z>NR}mIXkhOSJI)G?@i(fhC}6Mu6Bkw47@fQu5JJ&qJKv0HCZnh)ON<`iTz@}C%kP( z$kj`~TywVS9F9LU-ITj(+3UMr3^$Xy7{YobTW!VOcU<2q{xNy={k8E2-t@&SxqjTA zd*P3b?U{ZJp}EQzPB-~*ChEU8PoAyS5PF|~%CUk1yonPu@3^;LcfrASiT;SmL0sDY;AkFti6v?jYt|mEeRls5; zvdNh@-CZp9NTau3zkdBHFTC)=3%?=$wE6^Ic;ST?R^eOu30nR+^*mm9;Tk~m<%LV& z8*CYI{EIwa*bOftd!dx_t$h3#^=dE*wih`*Vc?Mg{vtk$i(>}CeegqGURs5AR=xfD z=RadNK~yAp;0qI?kJyy>!s(|bJO{Zc@r6B=0Doq^{rbNS0V;?sI>$xHa6QqnxM;d5 zNH0oslZo=8o)RD@tNH{uf|uC5<2EP4>k%G;jZ@7_d=au*tO$G9*h@jo$*Q+s|Ms;M57B{Rej`sgF+a1~1lR*ZT3NOWNifSj zIN71dOtnQR#*iOFVlxPGfGkg4hX~hZ)!VQC4Jg1>c9Tg^?IJzlAyB8<#1uox4<)Bn zb^tEyB@%mNRhs}?K*(vP%>)uuxo8dHji5@i3F)OIznL~7T*y-(_R6ZaUw{8QuK*P! zrZ`3t2`XHqa1S;|G({9CmY7}^0a|3FAT+@-6jyn3PXUN#mOiWAe*Mqie}%{>ljy9m z2?V&(MRE78B*TQ%$|S!`(n}`9sF&A}U?j8hvg$^FHN2!WyRQ@}39fKa?)KQ5D&BG8 z)EW_>mw+<*kp2N?d#RM!BcEp(v$E=-mfD$T!uja5pZm~uq>pJ|h#4+w+n-y}(D#fv#%aeY+X6G{i&bjb?Lifo=_U>7Az;Hl(_TP;Jq6ez zp=^Rv5@pG~^6cDsOI0@cX|nK>0MCe$-fY_d0WxmdllWT5N|{L{IGg;kYz^!}ggGR* zLuT2wpO3?8ZF{XYk1f*1+D<@*idu4qNyd`3P-=)kwJV$8|tM}^jF_g zywD+o{4z4|$B9l*B8!sSM#P4?N$n!sJywsJ2-Rtr3=vUfwVnN^@>GULldHA^tk0~s zU;p`E{Td+B<{hV=%drhc2vB+Hahx9o8~qNRi844zC?9p@jX^Z#tvqM| z_3<9`kOgH7T6W_r{;hnxAFpm`ee9*T6-KtpqxYyyjZHolqa;3Vj?tQqY>$!6k;*0k zip6!(nBLVvy)>EwwV8)m)UKlRcTxeO>W_Kv9g_@&3u0^2XnjLhz7dc)6e=&MEj+Q{ ziAhgf0}9Z(%BXI)FvF8^%=k;VozS-@<>pax(f5-P4LF61BIBPeqdLRV7?J*DL>J)f z_jg1+g{r|w|GJwY2j*v1Ujb&kFNyCbA=5=@g^nMIXiaPWNPiMCV=5Z&9Uz{v#z*?| zka^~c_->Ey*8rLKsM^a^hChqsd%=+a$_i|}28ig4k;ceUdQNQ-S@c-4%djnDPes(A z*Ap9ki0K3_`X3Qn*uVWjIuW8{ZBf$OZ0qcGlh;N4K8xtA^vWQ?$*DDi&;-XAc`9qU z+K=SCnUgOmZd>59d>?IHnQg7APYgX7%_hIvs%JUsi>#W9Fp3MN&l87iPIPXD^e{Vq zBDA7K)>XlC;}d?8h9GZ*M9U)V;_M|t z<8{wd9uIny;M#hilA6_$;)byT^eRKe>x&|v{Gzj}0xWjsZp?_mgcw#`#kn=QEU5l! zHqM(hz#kelm+@MR>=e(O|8wqI11L4dZ-_)fZFx$e*;&1=nqPyRvI&HN-zztSbD=ns>{$N+)Bw*lzxuUD&8byjl44}s$C4jv z%RdiLp#c>2K%v6)E_@7Gc}b2S!HB06N-anT7>A$s~-i1sh| zIvnbzDEWQbMHE|9Uqp2Ts9B*R@_xLb`^Z#|5)nB)j zPMc@U2H=Tm+~%LNrSN)R21m~(LkEcGjFMifEfTA(ocMUOjl@i^Jv!$)ME;tLId684 zkHhNK{SZ~>QGe>T3n3oJs2+$Y!N`0Q;32(M8@sp@3wq?LzHlJ2kdLzpSAc9=-KT1E zLamRK^R@I_uaYyjqLB=Y4&(Zt=H#6xri#suRZSbV=%`Jgb{Ih z`_Az8O@LPu%_G0_yYE$-hXk*cT1|LK&r5KlzMqeM-m4qEeWe(e3$NzXl3yw3kHM7k z=|D69dNycZZ8Ji2ya*~cMQ@-y_hbiEmdzcbC&8%VBO1F1`BmFseaen8_p5T5NAt!P zcO^d`hviX%s3NSPvI$^jihFF@ZnM^MrM{@KfeK`ZP(_F=`Q7ooL4ua2YV2m?gNn92 z3%%*pGvr3532js1UPMCr#vez*6G^0V|sh7M5ZlM`kvf-GTL z{|hwwZeyu88gv=@L;!CrM*jzML6OQY)KJIr zs0N5CLDl~gu^VOZw>4Nja1DL19fCq2>ZPa(yq1bwlU0}kB=F~@OrGxa*_vOW@wHOj z^>#kS{AW)^Jfl>Bd(DZg*BLz#QGpY` z8RA(?8QakUkCc5tl4U%@D7n3Ac`aluyb;@5hGGh^)y8~Aumw-RCV(L^?>r+n`Nck? zFMLPP=>_LJ(b$OWWDuJVdpuJ@iFk2@86Nf{>n|By+0S|G&jA*ruXJqCK93FCmjEwu zda3ENucr$}Iv}Dw6nzOmh;P(~5Z%u~AD{q_tHJ1OMz5pea^Q{fELq_@e*cNk$a4Wl zACguP;zVRzZnO_0zvG>np!Q0wFuAqb(7w?Qha|!Cl^c=Z?B!^`E%E`BxV$?0S&z4W z74H1M)rQ<1>$TN=C<0qzh2&SLKd-BwTbY|8Jv*lFjES2oFY&e7*toPvaF9DAPo3e3 zjnN!l%-s^BF{s#6we#e23tS4Wdlp555hy=^S2;JhlZH{W8g#D}(x zw!UE7_tyMnT(metp%8n=oePvw6?S~ajTIp3d}O;&zgDybp>~a}+@AY5J5=5)j@}!i zup^K~a_85PSnlMG-^aF{VsZA^#o;;g%stilt8k@^m7Udp{(!Wlu46$T%D4=D6{bc_}r3XP8OEEmsX`Up6*>Wc zDZt)q#}sxD%j~(>Gm4%m3-TCV|9gqArT%)afqco*+gWj!52dW=fzN@* zufi4bbxXk^Dv(isEJhD&Scpgv_iKPFRT-i>MdznjTqEU=70d>&wR1{hB*)z+yM`-{h zI&=YL6(4HX8KYKP*V6fG2o1;0_VzKD+p8Oxt?;PtOjao2dF%`*bc~nW*wwy;M_c7a zMonsy)6lcKsNeV0-)IhtAn)(LH%PDr_4ZV~JaJxCke>H$u}@|G>5o^SW2wHDE!x+! zn*;^XdB?Tpcz^1#sNGAsXDGJYB)oCJ^r_>#(bxCk__YPaxIHlA3fL0l@o`wC_*Gxe zw)gcok-}4Mjd=l$!J;ir(6`ogubT7PZUQgqmBOh%h7>=Z?HFa}Em$y0O{>+jXqX{7 z|DI~B02%d$+9K~q5UW9@`z)9F-4wIpZKM5O+Xk3eiNswr-Wy{TdcD=Zk$1#ku2M7` zZ*AimAo_l!>_#_3JnttXn<_9j9k0<6xKi0MRjdVwF1AskGird0UT-10KOYRKdHSt| zeY~`A3UKtkOn5&D%q91X=iSt*0Wwvl(ahGFG5wQ1Iunhf`XRn8`ydf|b^zH|e;>7% zAuD|QFRTW5et)#p?>%E!r6*E{ofP+dO^nLy(z$BTi|uPWm}`C`X(#HtQAy2qI?bTKvpeK zRR;vY_+1fW>ew$m%`vuP+Z5PGNh)LocQqd$iC@a2BfTo{Xm6>po^p%MF##+EuvCPV zck3G9xi_i-6q+IVJHb(6W7}EVyO=;Kw2E4ws&<$Q1)b~FUn%DrV6QmLtc1mAAN`0r z+U|tna*z7v+mBW}ma3IkM`3>l8ylW>mI%T2Jz@mwQ`q~Vlok9w>+xqXOX(}Xb6>BW z8FTcp3NJzZDL)dx1|g#U-k(}~nu1kuthN|oa@~+6yVe4K9^+ApbzfCVLcGG>wN+l_ z9HlsmhBYL4dm{81_3vp!$j#d~k^C$uT@as>Nfj@-dFM>RXeYoDnzs+x@l?B;zjTg~ zaph+bW-h#05$pTYQ~P-p^9I8xz*^hjbH5jYEpsNJY1AJ-!tSeWlzkIm5oCl2tr!Qk z!-!YdO8{ zZiAiUM~h-9_TpysBir9df8$dWKR)_f+o)o@hT=mCOHrEkX45BGJ}bbEL!WG-fm49~ zvz-Xh%Zk{H)+xrOy`~{SlS$KzjSvV4Ms1jw29%}W$9|g;HuNfRRP_~lssUC9A1S<% zO>H+)Yd-;-^L*?>5=2l3m3O`Rw5h*RttO>G;saR6N`Q6fai5LJICyYHPbj0%jZ zLB_@hHKoqXxEoov6(FJl&$gZ171$fgC^*lUV_XoC$6#f-a;zS*D3#F` zz2i+rB+k4sP1G3~6?nko1>XYtc)S>99XXPD?F__ffHitSz^XiUTs?{tO>G9N{Bd~3cWD6|YiYCW_Cy<+8?P4yF@h_fE4kxFx{J1{8Xs5fU0GO|Z8N8by0 z4R8c2I220FQ%?*aBh`CV3eMHFW{9Xj^cWLsdrI_FninHc;5bI_Dl6}yE7YRzsOq_t z%faUy&kC>l}=}0kFZhJ`h->rQd*#G=rkTlm+4(c3AX!#E7WBk9cj)^5`067e)RO zkDtTs|1r&E2jN9XE(0i0?zN&L5T@wN9fuewaqYB3tp}S8G4)qUs|NVk`@(kcrj}P* zuyzxGY+*sAM*U;vd|^X86Xfl2STCZ3;9ZBYf=;JRR>zAq*Y}}}C){3<$ z(Pq}|v_Mnhg*0490bZLZ=g5l3EpRqLA%StxkGOcxfmM=AA8#Sz*5f;b+BwP69_ z32SbsSj060_2aNSbr3ZYSJD}m79}=6X52wrd6!Ov8R{(I82RyopF_^98M4EFOY!kz zFzOFMU+@FaC^ELmwi`$5^9*&jgI?*NUZmCYktFy1V~!{G{WwepWh=9;^*AhbZ9~2K zI_JnBC&e86ny3f>eGn~Eh>Ez1#(7|T?spvG@Xeu6cnaN%U0uEg*mLfMAA~T%Y>M&S zh_Nb6=#f`G9D#tB^t?pp5CrcZy2n*SRNj8du!RWGMV62Ht8fwJ7g<5c0`ni^5)pZ_ z3p0i%Nlz`sbA}r}9xJ*FVeQfFv?E`XizI^l8-BCmgt=n&o(q2*R?Df@j~Jo0^_YbZ zL(K{*CTRO5+}2v|sW8>r0>NS};oX)pww6614!(c*6r4vXUdW55vtVsKnPc19kHZ=T zg(r9rB&)j?JT4g_vQG7FnChYW3~E~hBHh8n4XfBjKtJ)&$Qj$0St*TSP1RQYkbXRK zdRG#A92TnPka{9ob41nPX7xZ-P;*-&L^pU8V$?8?{LULsniJDrSv(31;dRukOP8qFPdO z7IRFRBgO`kx;5ZyqaFhu>~HjWTW;Pz+JKF z?F86O+(>An_ME#|s#VxWZ=_6sSG}p)2MVwhh2K9hS|CwqyvEMLZn70|I=UHn>-+Yk zwiv}`sYO)v^%UO*N|2=lbPnd`$8+0ATcEmw)d&hr0nWiJa*BnLAwEzn9AAYy1GyPu zwT+l5Yiy2og^j7nvGu#&9IdnQ z)E)2bKN<;PmqhKh5flaLxlr6&6yKjz*5jDDd*`wkXhoNuBUVLbE7-f-x_O=hSAZjo z0*DPnb|vp6zzMIraHZ^e7p{~7-&lBRuhDBS3)=}O;wbl12yFB#ZgS*)3gH{QY3eDv zgSc2V*ct<>yy2J+{&ISQ2esc9Ud1-j7L4vVfhEII#@`}RlI21ubol}HvxvuBKmK@k z{=c@g`g%uYpv%1ehfI2;cbCHno2>RpWH$l)13Z9`Vr>_(DWJ_^Hwn9W`f!Nc~CBM`QheJ?OO z3b1yQ+7$=Xw2}{|I)&X2e6&SAsteWt4=}{ovPZz!9`<4|o?$)^9r5CMY}>l^zjcIC zcE}vA2FM1;c?=tb$6YyAQG&&c^vX!+iWl$jg&4xpD1#*c4LB8;xVnU=(bf@$dIB^<|tBB+ql~4MrbWV;$UfW1xjA|ZT(+#}&4D9!7dRS7^&Ej0I(E@>)(Q2_kbGh|hu704*@JJkNYB81=#of;*seV-6y! z&YIJsEoz+Rh^jFKSt9dq1f?hHIY%mRJi3%QS*Oc3&BG8{$PUb;V_0H-2GQjl!&AFq z=SHty=PyJQV7>iSBP&+R+}O#B-x(P(FYDGka6S@P;2Hn8AA?QJ65+btjAaELbwv`bHIKUQ;kWh^Gwp((dBc_CP4+VH$O7486nPyFYV^Cg&l69 zJpMJ08t>z&C5B$$c#o0vxvWe#tWk=}4_H{*!h=B#(1RIU-N|HFD>nr+p3JGmjgOI+ zJ=%}K)GKy$4sReg?f`p8EyE4DM3@nLia7``AnK2Wnh+C7a2NV$(;72V#{Nq$Q9f&c z5%uS-8MXomnZqL2Z=Jb|rb?l7<$6REU8KU)sR1GoERYYx>OsP8B&w$)kHR0mR0Ff;1Uvom1gHxV9f zgv_Uk;dcIH#RY<#4B;M!h13ZVb%?0KIsjXCErK-^AmSdi3-id0S>I>QN2)-JoXEuw zB12|zM(h$W(;Hk3FrxmvHnnZk<_25=616wCjUo|FeJb_G*o?52EWJlnUm@c!MARP> z2S~hG5E9&sI8jkf+pAF@@0BSxy<{mnMStnoSZdJfuUGQB&5uUV&T!8@^*^R=*Fsc( zj5I}6TO5%Ux#*?z7odLhc!}n zy*@$Q7Gl4-a4oKSwFu1*aXs?dJx?+X>2=kO9y9BUXTp(dOK4eNAPkl^#3u2;E z`hSnuMmE=xBaWc6=YJKBQGQ-!*HU$eZG&hP>W^Kg_Iq;fXnoI`W26G>wi7JUtHBam zYcXC-jwsM8%Gx6;@OWLnDv~y@eG6Rn^%WrFmt~vPKf^1N)=ch zZ+x5)jR6I*C6e9DcyemfPok(AV>N*17zzz?LR9@_kYl8cMC7W^hyZ=`&y*U^h(8QL)g}(+ujQtz zprs#LADyV|S~osgN^hk8YP8yV+uw_0O{{+nS%KF8d$m8yrf%!0IfXJbw!sIoL^?C~ z9#Crw=iDulD?l4-gSJiH3isf->Mf{LV7msmtE|B0X)8d)*`A<5R$!0fYrQWzH$}&^ zF#kr0=PW%}&AH@QK4ol1V_G&XOeVa@W3j9@<W2cm-MUJO--)BI?f?W2rokG90}> z1#r|&sz9@#Q;J5NtqG5!i>ko;YiluMNv<`f^%0xV`xZQ78t{0>8e?5)j|#+VfSzpe z$P+z$H9#aFqwkrdXuMxRYS!Ae&sI44mqt}!qd)5NGU5FsWXGB1y}n3bNXw)CvNP}* z{O15`AWEd|-;9~cHFG7L`qTXTp5xri& zviDSb%L-coG64|@E+()YH9+PvX1mmDn-AtHF;@Z5+YzF(Y!%yIk1YUYtOPZ?g{urfbslZy{A!-o7j9UY1DpMJ3&#Ush#5e)o4)sRDjVy@T{=W0Bg(B{eI6UOF9BW;GhS;y38p?$=~#>; z#y+xQyah&_$^;b>oQ#d75Qt#3D#R* z?*eO$*#ykh?i3rs^M1}gI?K`f(LAcf7zg(-1STMmfVRf;0vy~PF>6$on`-Pqj>^FE zYLC-gR;;vQ#)=IO(Rm0@Op?mjiqKTQe$G_WBho-Qwh!Oqun-03oumH0RS!hwIDvJG z(oOR|{=%p{b}mD`QDa+k6`TY;B^L$J^^TDYOUWs%$TqxXBr3hm>t*Z|B7 z@;jpBr*spG*s*3(bMPv&ae-`oStOF?%9vql;K+NAM_9@Ao5wi3{qQ2jEbsPbW!iMVWSh`B;l6gvzGHHJdZlG zRNTlGQG!K7ek~}}Uk)L96dUu2v7QYwhqDGadcuo`pp?WYM26OGL7@aS`=M1?^f<;> z15^hb6VQkf{Lqn~$4z_x7FJepBAS=10W#!QoVL9&2qtfpRg1G<3BCi?o+Gv}hY-2% z0wW3!QG<-yYuQ>#@UXdqllJ@C_~6k6$ULoiy-Kje$xlso$=hC!&Yo#=#AB=mSc-39 zY#~Qhd@4XB7)J00lV>GAziuf(2l?&orXDgYi(cAxTuBf4jmD#Wwap08@gjIsV5<$; z?eyB(JxDAd^Nf$Gw#j`YKH7(Uzgnm0N6_H>+B9k-0P&Z-J3*EZP>aZ;@cB+UnmFX&{^Y_Ne`N%IVBp5fUtAxT!*) z>&KE9Vvk}GIiBGou67$UR~ugPLlM`iBk(st&>)Hgr?>5AX?|p)Rl5CA^2skUpH?NV z`YVV;h>#ykaFI4A)UlxSR4*6o7@QG9xrl9nSyk6-LsTErb|S&-_38J3QVI6#%9z}& zezdK1JXPC;5cE{pYXS9VujV&Fdi_=#+TUwy)q#u~APo5>=$wVf*vJd75bJAeOlxlM zm<^CY5576k{Sd*c!?Gp?da5VPu451F%{CoAk4U{85@$9dGle)I`XTUJ^k@RbX!pRtc8o+={H~wX<`_Y~zo^ioRCQXOQ1$ zeNvF$dPR3YaVFV;etH_TSLLe-?F{vN0x05<&4KYF@vSqgRDY+pqYS(~OfsDTJ&VNS+;msmT0IDmt!c%>o@I>)GG9mUkz^vB^0h~>K*TM|S zCp5-L0k&S339m1T?8P}g>nd5%M!%xxt^HZ!5yclBe+p#f`ZOZHHEPa+=Weh{UyS-X z?H-cKBDqb7(NnecD8bU`>$Ur`XEbI;L8(1g{#{_p&KuB%G8nBjv&e6R^_u|C8H@ro zpa#53FG_e(ViZChLmo=Obel)r%{o&aZ~sxsuZNaz!0Xw4-Z?6GFR7ROGWz-$DZO5w zXG|&MhbDOEEb&D$qt81>2i~i*nWrkuloZ-N%{M`qK5q~*eU0<^+T`a&)>R=@0;2$D z6QdZjVExqQqv{w%&IT zx!M^@ke@da;SuuFXWiSsj|k9uA0f!>xea(-WR11@BV)&iq>j}eAwgvP2~pC?Bs7t7 z%=AR&a%Ce+QbhaveX*M$*#Q-K-@!#BLT#dk#gjl8dqr+hyNQ7;F* z2;UdIFF^3IBS|B#a93&AcNNJ}r24;Cd;VHfbSjrqpf!K`u z-~P=*fUUOOYz)jIzKprXm_>r94MKz`AY{{O+s(#+>gp=zB0bf?%*=}0JQH9ke!@|b zmva6S(66#xll)4VpEjtSkzSd^w_+{;JCa~29wJm`6>6uM3nRd*wwhhtX?sEjNzJwevCEZ@e+KZ3 z9Wn*8efw*R085!qf-~Ch*oF8!_BgUrK%9=5M6{|M#NsO-vj0SOPJ!xh2A^iiaAh5Y#nVXqjk#TpZ2`sA*=_e4mYKuH& zV0LEl%l8F-3CggV@N=U1`e8bAj@Qoc4U@5soHSrh< z(%EGWKUSs}F^7V9GKrC~abVI_7R|AP90)uN;a{Qz$k@%a5kW;*Yl-g+)utzR^YjE2 zAZa@pNZgpEB#V9(6L2(T26WU_2M z_6D*DYG2~3k(CY!^D@M$%nVYx4)L`xLmLQcfQm6Q zj*)~-ymZI4v+C_TLx2aoDlDSjCf5BJT$}Jl*spT*2fRcyd)y>!?5P0eWR{Nr4=6|} zI>%&U{JMl^VZ)mq*>h}8d_7!`5Z7f@i~!G26VYPD_xlo^2Mbe9O;Q&nJp}8Calfq6 z5nzoO1UBIbFfXX!NWU3m^o0q}3m;KlGqc`G76Dq=w5IpM7sQL?Ug%&Q0ebNw&fgO+ w5_=&5*CW6eUU=aJ5n?}#{=y3{yl`RsKRYrR%_cZ=NB{r;07*qoM6N<$g3B>&DF6Tf literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_888.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_888.png new file mode 100644 index 0000000000000000000000000000000000000000..c4301da47c53003aedb988f1c486a3b791f2d9d8 GIT binary patch literal 15287 zcmYMbcQl*-|NozaD4~i9(V_%J%xJ52?Nv1*ViT)&YmX{It=fCEsJ-{BO^IE!R?SkY zwRda(@_K*H_x%32p1DrWGgnTo>-l&-?vLC3k@uSFO1B~RAOHa1wu-Vm3IG5~6VCTZ zNeH7eUz@2ph8b z2!H|&98~xw3v(=Ewwo6A{$BjM@4xc;GRoiV()z7m#m@4SE6X19N@Ppt#ol9oGh;gn zs*a$E&tkmo07bj(YMo#WSS~!N`749Pl4fw3-f|GKg6l0CSDtc8xOJqn@$!-5tG_qb zuUjz%B)L)#~=mV30x!%uU-;Us=Iq<95>FJoxMg7He$LNz?^;aXcqhp+r z%NeJmBRe7P{y$-R^o>vGi(zZc%HGbsu-Ejhu|AiSW10Dl+r=B)^lPcsyI(Ez$ZFOG zg?;+44*3xME2gD&@ElTtz^_a~21=Hrt z&vLutPekP88JWeU5o{S2o*#WCP&N6PokDPw9Gi!-M$Y?`-m_@Ul-evE^zJ>a1UQTs zTK`B+zy|pQ35-Z_B*hZtKFg)YI^C0rRwlEU9=JcoRR;H^eU&jMkWBL z@}zmbG$Q&Sft3RsTR%nW(tYgRCH1^4va4xJRyuwD$X+n6QAj``91+lJ>550jrZ_~~ z?in;<0FMljpqDvt!>>ER$k@X5)5*>-u+uB+AP@agR|?Ab6!H7Rt{(+(Whtp1v}GxD zy?WeU4nEU<9Uz$xbIfH3n1F=d(ZQ3*J_dS}*n5Hbbk1zD!h;}me)hw7wBSCQwBz_~`8<&(&B zMt7;@rfyDv3ZV^zpXP3Xe4dRI+9G-=HDJ8I%0Ubpa=aW0m%3BBcP<4ABEU zT0U79@*51od>E}#{)%rNu#&=8*FpwvCun!)3Ktl4!Q8kX=KSQ@R$CFxN8yM zO_QngP^1&D>HSnwK%duFgDPZ3^gXQ;FMC|jo`e*vf-pYmXcs64C}tkgMsU4c)~fNI6Np}B ztvRD$eHUV^^e3iEz9GCUI7?x9X0w0vV*hXzwzwP93ffWQ3I>Ih>CSjwVbZD$hx*XvB@A!m^Q`NG=E)rOO-f`iNpTQ^QW{d zZTfki=;WggwFEoO<{ojJn*M=BIFsC}dReUvn_Z+kCvxk!JA#~Lg(EnTJ}o8ZB)Gqo zw%jxg(`bF#K_bT&$yFoUS8)f<7Mv){PX<`z(6to zE%8C0sbeVA@GAI>Qm-`c8qD6;BI68paz7t0(xvU_^!%i9qk9Q<4u2{HivT-4eg9Lt z6(eH_-uJq*Kl)=MfAov6#qVc#i{>cKJ|s65!;c&FgO9i?V&F9A%&X+d!_rk-o?lp} zpP9USkB(L@ix)*J3MtI(#|22wrSILVyIvvNmpX)}QV-ks(rgVsmWaCl$}uekA%)VK zIM;6}3RZ8Dv!WY|C8y?Z1Kn2WP~pw_LN8BZKumPv z-ZZdx@#gkA%af4Z(lE>fhRceXrfuzZhBucTPguzh6W6qiXkl$qsECPSHS~3Ep{B%> z#-!pqBicUK7?I>iyJ}^)JW2f%gTZ>CM%{&pt(p!0us6ex%#%ZYaVGx|%Pz#;vBGQc zCcla1dt(v}OzQfWjV@2@Y~d6oNlb~5b_}G4N(=l|kBBO6vq3sNKJBkqsc8i;yRHg^ z7+~C^eHBGR?os!fQV2n{f&k0K&`yh2+1tdyyGAdv#?0IT%&|6E-$6y1V(N~Z`Q8r< zmbImI$bZ5qngNS_TQS`pyISUNdo<+}!==LE_nHGk@t6#I@YqOcRt@=^%-6(tM%n3! zTL?a!N4s6AT8>5f*9LfWV6ankZ9623?=3~t=H(bJyIRCr6TPv()!MXCDL{P(D=x!y z^nh|Rb&q%raFogE*IYgVbg!tr+yiIMd5nxv#^B0PgyY-t*PW8adsz$XD~$F%a6|^-V6V1CS7rpr_+HEhk!KkG=P?C7Q2O#fJI;E=MQGPZXW*A94;49Ii#u+Q(uZzWc%A^JW zN~rXxDxx|(Nl3O^zgH^&XN892q4b>; z@hOJEXrON6s}&tJ@u+$Yy?q2sdRX!4vU#n49M$2dt>i@+vAN#<_t~!&#BXjUgIw7^b<#0F?d=t znf7qW#_yFn%*0H0%?vki8lFIO;G`OT4<41Flib z4crTF^*Kn?r}^~j97;pJ3Q-ydQwCU)FFCF%jA~y$`qQ{k9#5kpv_HYxrj0=sb3MDB z;pf4H{>v9FQOZDR*Y!2R~X9?hk=rdEic-XaaLveHVlk-Q$$@% zXz&W5%GqaW1TLT-$>)peNdCl5k*{z_GJ>Qd6h3y zD1;O722rI9{rEtAQ4vrE>KkIkW;g)^(SVkLBFsIRSHw*sR8^34e(a?MSQk*kw~J0_D8l$}V3X zF2vFbATvSWvG4mX%&d5WG)86hkK=&>t80nH_rDiv5-OWR9lSO*U*4zN(iG*<{KR|+YhEeiwdM#M|u0~kuD9RcY2MDW2r>Y z#HHXdwlA9662ZrVQ)wIWg0VAl=2wYls`#}I(wzYDbsncgvf^plQYQ?cCbl4E`?aUq zdg`Mf%x|mjGezIDnR?1XHEjWL~E zi7=k>S@GCd#=z?J{MKW?-a8nchvk7<%SiB;&+YZpTDM)9ieF55_TLP7KR{o5hyD1~ zr(ptQRF4i8e(*;jJKNZpVOFwWWvxQq0mm5qDBCxXk!WY@FSOD$GJ5vZj15jlJy>LQ zETeJk%%1FfZ z4g+451irkMl}{CZq%d2(!&39&+df4Nsa8mY?K8i|OyGyP%zB59!CwLm0IT3xV=xs_ zY_O~ZkU5*mERz%V94TZ)vyq)z3qiQ*tT#-8Y>F-Y%vD%lSf6__$)jlQ$& zH>)|5hF@7@Pt`}EMU;fqf&6DTHCLbnyaokUZ-}HFpiNIvYBdTVl>1Jkl!tVODH&1) zCyGV@h6to3`nkg4?W!^bX!I{$I`!R}={@jEU#K@vPEYL73 z?T~|{`~)p+8>=38xr|q7&I0C=HapzOD$Lw1E~?cCim|L!=J+_8$Sgxy7LN56C9VST zfJk>Sgc30_KXww4hb~nRieaHwdRj(`1t;IxKVWlutk5GN#vE0~rMQu4YeRIbU~JPk zn620zF39rC?30eP4whROWyX<=GAqu~(_<*2m}(nEA-LJe+5XHwP~$t={@quZ`-?v);b3p{moFIg-UU6?)U zu44kYN%h5Wy_^tpGct-|_@IDR_)K_D&8LK}*SLzoMogeN3etK zlD?QuW@t=Uepgxd_rc>sxBCB!fyW;A>QPN!o~*2S-w}nNQJ-Tt1&lFXsnk``-kGcyvKSEkrFT! zcS!R8g$a*TY3K6WC%soEnBedS&lRf^m5}kTaaMfIOH~yptPVNLVZtWI`X$@K%NK18 zWW+Q(luWYgtOcXp3Ay2rEYvnt8AN0*I=M}ll!Q`@Vp5xe5lUEribbSwJ7qX~?QOO4 zA(f�i~jU>H#B-Jo0@FkIX5`)dUo`*lS+Q$eD2lGU3J z&jj!>ge1yFXG3kRPUM>(c2RUQqQKhgOfNG=k-b}cJech4N1D&N4%c6N1Lr}axZfVp z=lZ*#(JnlXG25s2cph3UTS-r4JA=nQ>i*%nTk6V+`NoxLcrt0xe5Yd{iC53i<<(B# zo4XgBM!C-Gt=TXSiYF6C!Pi`JF8qpGH0UQBrXkJ_U|^CS*ALQbYtP8*#F{MTlpXftU*!76`S#qr`2JC7&i zVMtzcOXdwNKVh4t$tm#S9k&~q5oTaGw&^b^gx#t38zg6`f!gzdQ6wV`TIxT72v>tC zGl;Cca*rX$HrCe+Bn%~tCw#m^5}#5^+s^Mf^DH=mYeqPyMwaJ$*>}po6PodnF|PLT z+3~%}1W@YCpgoVUIhGH10NNqsk4N0tnm^Y|g(&@MI<}bUQ_JXFyJ19Gc*|!p`pz)a zYQE}0t%1Cl)xCFBrS4~gElSAL&GtbE7PIZ(15I^Q8Zx<0Ohn40h}T}|+^uxO*ebYb zK8Pn%+2QM}{>9ZYzs$w_V?|Orh6u8No<^LV4*-jM5sjkzNOmt}Y&AI(8)yJBlw zPxoam>M89zMVWQ8_hXjVgJwlIKct|5Cry>1CSWuRPZi0|G$QoC8mJ9eCIpUR&~`f? zG={>E>d^~{ow5;2`HxD!X&=WGN~i^<6#S9;EIxsF3{KT~sO>>4T7?K)Su1fuW4|3= zF|)vPzn8?mw^68xW}&|Y3R&_JB`&)=>$*$0=mcDqIw{FeCr>}G9yX&;{YxMS#42+Ug|F-H$;ev7bFfK@)TAG?I{D*OQpxl0JR& z)Y#5@j_)#jSHGunWkWJl~-&w%T`Ki&5u zl>PcHuzqbM@BD%%kXIlB-Thf5@}dWn=IKt57t>dJoX#QDo^%Cg8q0`9)TkL}7u@|s z(}UG=+Vs}Am6FakS-0B@+~7Y^D^F4p8%glu!R)?p86A-f{@U@rV>Ur;;#y>y1={BE zS-)aK%l1B+7QsI;3N&y&EosZ40@SpWh;^(;X>s8w{^~?n6@D6+xeK)@4VL9ouf)HO z-1fv#9NP?U|C@=Rx7`9~^>4@5e-5ws&Gu+P>21m;DCj)rQOv|KygF_3JHNCt^9lcx zwp;ybW%;FUh|_R6aFC)^gd9|Jx2hHJ(oz%nax`MHe>{nT?iBLS?ZKd+%XPREu}m!5 zDI->%aI_k&moh6`yltoJRF_56E7vG5Qm_7!UJ_$1UaAp?UJ!?!rZ7F9)Kz{~>Pka# zyd9`@AV@D7W6O`*-a}wmbULjz>CB(zK|WJ_|7*B4bC<>Eg_{9SJoVb4UhwLghnzV% z%j{Dzx+Jt>zacZ0f%r?3vBb753)yB-G^j|rMAo8gg5tJ2j~w@ykP6znQ00nR1o4&! zgCe7}W>C6SNBOjP><1|9ZIBIj9xO1fT|R4atb*Z2urQikwq}<5<~dct4|nMZ+QSJs z#5-(fR<~Y2fhw7%U`M%5u^W{f%^b^n4jm*NT&M$MT;euHooQ}w#rB3(+-O55{%{2{Y2!X>vqM#A@?9`e~-nDZbwC|;R+o^bG z7=<>Kj(KKCX0K&ci_rllC|Cwh*WKnMgw2%I`rpPTFG@j@_^X&=@p1NFfWFI&d!AaH zB}VGwxOvLZhh??9H{62vv=68^kB+f*w4k$ygXBPR<9_8u1E3{u?~muzcTqg36pEL+ z)`2u_P-tg(`AC)}$GRLaIC6xR6zyO)@|{2%?$yeManS#1r5YAAz8fEr#(Zb{FY@}V za%`KkOGgu;mS?G57Kl=Bf^4rKpItLyLd%;A)Os^Ty7f!5m^(jf1c+}yqLR)W_jaMQ zn-ZBde*vC2G2jn|EOn9)De!f1gIM3^jle^4?5_M*@y#BadKh%CNW1 z+Ocqa%z=dKTklk?R;)m!L(c9rLPfu;Tia$GPh)pk=$SsLa^=#kh3#WvsJunpiXv%~ zd|nsw7d@(tY0dQ|)hl$L;?t$$a|B;59%hIYVc zdK=G@l+n)F?(7!Bc%ZKDD;5q>E>4LKaWk zIUL2z7ZWf0b-dqhi1!_K`@{0)zV1WY$F^CpP~EIzRmyRXWGzqVy})?B+Oa^gE7D=T z;x|6#6{eUe30e!xLXUK5^B>pC4ZjZiK}27(#iv!+k*)FXD!R4S-8jA%^pa&z(Hj&0 z4Q74!^U-N-{NF=H&+(Tjx^=d#M3qv**KI^RW1F5!w8l3^8exn&B}t}IE& z{3!$N)SqVsqAQ2BRZffliyE_r`Tf>Lv|y1& za4&{C8-VdB4J_$j8Mqy2)gsH0i>{B~X}Pkf;(zKcdz5&L_M_|oyqxp+Ub8C1l=gAV zI#Pupq$vw6-;klgbgQ<0+9aU6K}ER;@RGUoiHb5_Oh+Bg}bh+yj;gGVfH7%TNKqBtwKtmobmRsbWBYBkiNc)$RvsU~B&TrK1+ zl<@7v!;fOTr>cl3kH?Vow4OAy~ZiGm428Zlp;+`4WPQ1eQyJqUqBG zT4w96LlK+vM+AgC?+gp~EIGPjYXv6i0{q1^$8_!h0QX(~>jfyfxL)W8pdm~mvlFPg z(!eFt)~W}OY2sitB$%C?2gwk2 zChowYiD&`fDwQ(l=IB9q>B%Gb+0&9n*Xyu<2Loq`mD&ug2xy!h zR~tbg$IX^<_KpX~^#69ntNAT^!EenC#`!&(N3*{Y3CWkAB&d9ovtWnt`~xlK2EU<~ zxK#b3H~fF^AEXmjLtSZBY-`t?TaUmFw12k{HTj36-Oy7u5)bcG#4y{ zT-Gh`h4>Bs)7o(Ri|zn(>FFO_?_zn_Bql3ktGEz5Q$bZ~REemL@sCyvs3S7Si!EeE zo$;Jr^Hmv-^ds^7hEfs=?YCe%LM^GZYBPt7d6j|nJY%#z?lNqI+;v+l2x?R2M#TGW zUs$Iyh0^>NpWWN&-`ZGw7Vl&Hi_G|MhhF;Ygv}+Yi{@}-JhxtU`=8-b9^g&Oh)YSO z#Cr(_E&doCWs_G=*=qop&T`oj-19)I1zWzg=9k63Q0cY1ONczC-NTh8z?&^r18QWl zK>;~DS+f=_I4f}vA;wFm;UUB3t@Izv-U`YKWGm12vZZH-&Xc;xxEYit#LDvd->K?0 z&WLq!nlJ1ehLu&0iCP;}x1amn5p2;w(g(Qw`(4M@1W#Mh1ZHc^L{vgZW#Hu42A=hO zJ%83>SlX@rgci%+SyFBbbbr*qnr+%T03AlU;y-v-scS0YCe5}m_`R}e$m+z0ViS?1 zh`X!IuC2dk5%@o`{}+Q80lDeERcy?GHva8`iOj9|2wfyUe|LChjUZj zH}k(1?;M0}*X3wu19C33)-+4PrX+P{Y9Q9ohg!r*9mK_*#b3($4d=@)J{UBO^?V{_v$o$nX^sN4eFUbqR8u(|Qut%1&jJjF#u98ON#5awd zuIbJ)e0JrnB(6__IKAz{pLSp3gSWySVAH;W_dis4@5OYcW%@(rcxgKaS@(Ks*R@WH zJmz*ZT3-;GH+CR|-dBHVlVzIGN^-aZH3tTC#@x*uY-X1kD(F1eL1G zhF@Iyn7kX{7wi?M$o_rN^{nq->ze7$FaKVyNd>c;=dI=Z(mPSpvl(8n%?8wr?Xs8# zhpa;7s%bWdY!n)~lYUJ94Jg_JzH!3*p9H^F+V-I~IMBizQ?d4G%=qB?<@Ay0w>{XJ z>@zap(0rLeG5H7AX@lT|g*LG)R#UPu1YIeWoPq|+ktOn4dr8LzB3PN!66O26-LI(g>#M{fS7t6V&2+#fEu_CLXjaAe_Q1?$l16$fzM0FAZ)}Qxa~H95d4ojoa!1KMSNou zbDiUUV*+eFy!nwnmpSY#h->qJ($yq5a(X_@2fUOjl|{Ac#4!**54NqOF`w9)Pw(d`%D<#U zda$$F#oHyJ%f*_Ed#Z%ygX?Gx8=oXtV&-2$H*xyirv( zl!c>r>ViQ7L)-)~?bauTjq)X&u!G}ANN_z=gi4xKzWhCv7GOpky|!BiA`58wPM>Qi zq}D~Wno6H7$xHDtpX=p!DZe|3e^t|PLQPt+sM1Y~^# zF~%5f7G+7sy)*OU$RM%Co}ims{YzSV8pil4y3%0vH1vHaBnwpGBkRJih!z`YtpgIZuW9sKru$D1{KCC8>2gAi$;t23mTTs@)CUqMseZFOfgGH7v%Rq zO~>gC1{S019Jh4elEKVx@TU=30C6&^Cx$Kj;V0CZ$_3)#t_sms#2BA)l`a zkmObKnS2hZ=u!GIwfzm9WeKLP|GLLB;$`+IFm<3?mBKt}WFF&?Pc;z!>k(k>TMozy zZx%OL>73C)T#X-ze}5jXf$P_0fYUvQCmnDgx`+tCcgpyZGS*NxYk#c@S?8o<-1_ul zNw0us0)AhIH(ASvE0)YorN)D^@iKxi}choYT*5P!|`I7lcqA3P9^+XxbCLyO94ftjtjX&80s$H9$75D z?>s^wX|1fs+Y;M4k0_%U21zvhW);W)#>7dGCB>&~J+v;C&ueeEzQF$$F-UqZ_9IdkXO{m+WD5+q#-Vi0&A3vIpoLESFTs?^<_)3#J(Klp zF1d@L+&_7eO8QEeWQ#L~4!)`-o7Zkz><^(|eow{f_WPJYW#d}pKNPBvRZ^wMG{LV| zmUkp(o+4ZPF;SKKh6x*5+HB^1knaDJKG$b);BC+=q4@bm+9tU75q4_+ zHV+ZL^B!Z^LKRx`ol&Ts7oZ(@pc@ZuejyO9K1jwDh;?4FLYsWu*ykwwFt?-1MgZnY-n^iGvd$W9~u+A+@bH z?y?ni8dS}N`-@#oalc-v;5RR}{p#6Ll9T*k?~JMkVUkZeH6R79coVA7`yVd9RlD9| zMlHzj8husZr2>A^?@kaVl^h}85!{qLs?PIdLwi4u@V|fmdzth|jG{hE0D+^1-?{ZA z(1}$a_TT-~6Ibvip{q%RkS~-g-YE-jjO9&B_h71yXcMMIG2}y0FjDPfjL{@`OZ4WF z0C(b|x|mCq29MG<1Y(BwT|N7BAyU0^+mbx-Yzf{3-?G_^6$jM=N<+gTB_IVbR34Ag zfg?E3+m~M$M8kaHM$QBS=Bu@8OyPW!KcsK0c9oYbDD2gkXfMY+SqR05eqwhRO}XS)Zn_JrkfB%di8}J+iXXPr4HM`-xTFveinS^xHY* zT1S^fc#YEM zvMC6GH`WenE*Jenlg`Kp=FG_ghLB0ASOu!3B36%Pg{wATdfUNyo#xAeRf;?6^`Hv) z5O4XkaF@{6qH1A-eBr=TS0Fk)v8#B#8lEHpmURELa&<#B$rf_yR@c{=**tmEw?2>Y z#baxBlz&O2mg~w`35U-{*0;}XW+2{uKVdIYrO1)xw9{4LKCQv{Fv_gOfuQ)!pu}*r zU)0~L=a*sXiVo~ed%_puox_IVDMH+_iNf?s(AX3U0CEh1n*jaxjsejZt!1s^On2^VCYabPzA8Kwho>xT!Q zE?hHSS0>X6l49s##N@>07er-`zgkm0!@qzTT$Vct=O7bTUP?m0DhYZ{z1Q^Sf*FN( zz|w?Nj<5{|!Rt_Q=wkoIq__@A9*Q>3!Id@(IX}5oWS1Yl&>qd|v#HHAttA~>B68}F zJV#m&pf@ZNYp(mT&#kZaPF05bkXikWh zVjTSz>2p2L>?2dZzx^x^w3Mv-RjJmTvO?nPT;yRs^#>;hLNB>V8I>tN8GWyrs>??g z_or%#;M1){d`luL4qds|K{Tu!b+F0v@&2ge$un6cFDjv+ZwX%#1P5oNp6Q9ZOAnR_ zOTWq#Z`vGxu);HGAL%9xU=JbO;tI}S&CvQw`fSgpQgRxQXRt;<8zZ6T&E8J8o%VfK zLiXUQw99Nd9R<5Uq1xBjA0U{_V5g1*{@+l;0Jh0fpNA)~+g7QmuV0*X$0bEFRH^lb z0C}UQnY;gLj+va9Eb*HnQBk(9=A2msi^_2pT~$*bDjy$-NBrh2fxrsNrE6$vc32ST zb&p9DL((?pL?nFs)tXEhPTzSNu(R=b$~2I12Wj$N-nXyDR`(~7C9?WQtQt{W+nNXr zt0S%xz$#rJ5cr*J+sITB{HwWJeW5NhM$>tM602NnGh^?MCs4*nnifVT9ywd$p=9+2 zi5(EDd91gM5L%f;qsDA@*o6t^lP4aKhLxNItHqh&)~E$|BueYH>po6RBe`%Rfc0WJ zzL=CX`|GEvND~z%QJ>D}PtkH`jL-f)h?@8n3YxnemL}mXK^9u8rudZUxp{6zLHi>{ zcjl_24wz()aEQjM5udtNh@X!F&dE!;B$zVen$hI{?}AVww3sTcbB=0T%SA{NsIolX z+?pza*W*}n9YJW4h(ieP72B-DwJiDvNrAk#e&33z93%)l+#XW-Y3?nstt@}>~1Clw!C|b(Obu6N&ag4NhTCx zO8V#~>w1Oi)n&IB!Ojjk_%T$?akY3pZ!lQ2jfct%>|#xbF&1q-|3dlRma)5rE$KDz zOypRT$p>+Vn=FA~k|#*b#1@?bHCn%$C+*M?@_NA!MSgq*EVJ@R+?xc zMDF)!XP?(KLMq>E1{Voc#!3fQ$&Qfxg+JE*jp<@ls6VN^XzSEb{aJmE|Gi+cB|m?k z9&q-mT7yu3FdXvwrPyC5f%o6QN}PMs(y`^A{0ha${#~d%us31|q5JpKOu<6_Q<5j* zy09urG*S6FqaT6#{a;5Yx|0P4hwXhvw4>B5pbx$8K~o2p=OJ@NXv*)4!ls3dpn_UK z#q9E!XT)0M8M41&B8PN2AzUv)u^bgy+JeYJW+wY@&w7|8Q@6ndvzr><_n0<5-ODn* z13TT<`$X42r+negutuYUfcH4)`kQ&;gEH$lv|BMZx?X!7@Rwvc@DFNW4_+ z*4pmCwOC(Qtum4QDnnYUpZy7Tu4(0Ja;!&uBFU zA=jep(SQ98`Lc&o<+9JiSVfG6~%Pp)5ORbMi(f8FC zd)ZPtNLIcbT&~82M>A;E_LTL(-ZaM1b&|da3daYjPY#NN88IW+1*UJm2P)BaQlP-; zkoUk8dBy&ra+=L5I$gkyOK3~#00NuD_;90-;LsVulSyhL`@Q9PZ@*-SYxXaH2Vs$g zWgGYw!3uWx?hpty_`@v{CcZ#A8O9Z8;tkOx?A@)q-jFj}=qzdSEj^ND>QcP{@rkp< zzOnMeyC1)2TAW2H^k_;rFq-r~K6?cb%UWVQ42xYLZ2vk*N5P5ybvY;b*#Vyn0*D*9VP zn(Xg-Hk1-D-Q&>;fgd5c3!gQHjz<%9$vEOoNGa`jPMh}v#(|Esb23$rQyV-#>j*{S znfpe*nC)pgENdGHOJ9f*(6GLXNrz|pBL;y`n+54tcB6+cZ4!;3X`J1Hj^8xY1+!?M z6RMy@m+59>dUX=Du&_w;(x z-4l{aIN|n!KUN0aZ*HX3YJ=_)R!0(V2&#%(CD=Jv@FE+;d(^1>U;gd zG*mTUmlzM|KKMr{dU`k0v#Z5AV*FqE_Jy+iwf&djiWi?e6U~bL<`H;r+{@qZLD7Z9 zX#`i)#x!;W)BwPx{*?d>`UzMBaL;804e3`DvkA z5F>-!%F=!fs#kqqJ&DM-xVF)SEU7CYJDtjID4mE!LlQZDtx>8{h-L#2qIhvq@^{LA zQ$T~Di0GH#iE4kEiRw}WBNR@=dxS}3GcT}oU1&ITv2QSkETv_U^eb#i^vxD_TI^qc zmM>dx17;aF%?i z#G4~zSClr&_a-O<^SkPDvw1A_A|w?J!Bubi2Iqrl-%wr&$S78o{j;N}*q^cL(#7!8 zg$9NyJe8gsG=Ikv()VF|VQ{zUt^qO0v5vHxmtCxH8ZXOBnxe`f)glcJ;l>Aj&8Ye2 z>=}=`?fQkxBxDveHS zv#ZXt?El4nN>nR^8|Z1JO!1jrNY_QiT);XK+B8u zYnwc-2f!NNwE7;d;L$ltyopKI$0J^*pC)Nxx~{48qi6r*Vv%n@ zwe7A@r&X?pDbM)DP;8oV;TAFnHJRd0wy0-FPDZI0vbV*k(~Eve6g&`+(+ye#yHe6+ z!H!4r84!oJn8cc=? zEMvkBmY!U7x5XFw#_ujw*se{hInbIRK5~uHyH=rV=B6!r6a zaQOr#kn^b(kx2c--FKzf;v~bXWUlJ_L{#@H{j09cABxo)ReqsWo99P=UhyXOz5KSu zSY8<&z?!4_Zc6P*nnEt&(jO8pZz22Og9QmeFhlQ$a)lF&sh!d z5ZEI+^|~X7dbOCTuSi>qgrhtdw9=!866{`io)llaPY$gzx)lO&2WmSAJ1+9M(GS~r z_Y5hEwzPJuFT!{{-faIht|$f!2J8^*Hmxiq3>8MB573=uy117`XnyDdSUBo1un{HB zZPIq>_QA4l2Xd<&?P7KkqqM3CU3+P#>apKyrmrE_N=y8xL0H%XEcJ(1(!daM$9QXY z$3Z+=t*)rN(||#)aneJON1A7cMGv87$i&9;Hu7r)_#sb_$%W{f8;)y0(M%x zN4!lV{d!>*5}DTph0>UU1(6x|shlD8QVS=KS(nDm`RRw6y4jCIWH(DHiboGRx<6QO za(hHZ#yZA)NH5)Ti>AJX5StO>Z5=^U)!FJUGM;4Ij+)V+LPV*Xhmkzr_NJZlYt**m zKWFG5itM#P{P#Yf_rO{`oB-qv`u% zeMR%a(%K)rzh%e@&f`tk$_H72Xy+$M;!{`Q5oB#u3p+PCps?8_#}Q_W2`lrtJ;Drg z!VHdpkF)fsM)%0);l_+Br~a)Vn-r~2HM;{AOK?qP-e4?Sj1u zIs%c-S^@NjjW=DFL6ZyqQ!p8p_cQL83!hVGX!cLit?Tk?We$%V?j~vcH`v6)pE_kG$WSJ>wGe!G6daJ8> m!0h;2L6A(6^gI` literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.mask.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.mask.png new file mode 100644 index 0000000000000000000000000000000000000000..3ebb967560f0a9d474e71d36edf028375d984561 GIT binary patch literal 450 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKX3pEh7h+$+kpa|o-U3d6?5L+b>uyuz`$}a z=>NQKwoUvCW(F2SGMmraT(g^fO;tJnj=2pJ@j#G)SilG4$Gglg9IhztVCfl!_(MP~ Xr-j+IP3CSbC>%Xq{an^LB{Ts5OZSCr literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.png new file mode 100644 index 0000000000000000000000000000000000000000..77577a65601abe1b7eb8d0771e8e1ceb795e8400 GIT binary patch literal 3930 zcmeH~c~lZ;yTH-Rv?kYS`O2-d8ke$0GdDD+!qn1qOc4|r7Xk^$psJ)xb)rgoqNvx>)vzjpZA~VyyrRZdDi#+J@5PY zinsgjodd7ti|v04isd>#rK>N*Q;Yf&>7x4|$w-^G6l)rd&y1T6KG`Jw<4L zjGsGrv5eSAj9)RW7Vd|*ckaBSUND?f{GrUo+~556&C;I;u{MrZs2bYZyYFP|e)fkv zaMmWvT_y(o4_jLgnX^p5tn9Wrn}5k zHmZm9PeXS5SyA?r1vd)@`litlQQ9#T+AgW9=On+&fA{lTaP5&kcdU+L(|L&)!ClzV zl)#^{%#EV`Vc`qf3^ZgEJw79y#-kvz9=p&QJcWI)G6iu8y?zs^-2h4#JM@S`WfDGeuodVqHj5rjLMUNPanaaSftP&!Jea||SoN4H{!%$G;{L|Mro?$J>qh3u+Jpxm= z(En&kRckOzCdOIpbMuq%8bzbnn9(ymOn#hgDN;li8~HdH^EPl`P$hHaPxoQU+!DEq zC8iZM2ry*(+bnsIl>OtY+&MO4Frb(&tgP=5C@A6<7v@$76h9(qDdGZt_FVWJnszb} zedfT}yh!verjo*o&G&utsGQ-|@d*ODDe4=O_OKqE|Ct7SKr&iN<|{&iNw(2ZEvj;Cu!1@EotY5fCjz&J=o?I_phLXUtazl(=cAJH2;O`;KQGA zmnw3qtZX0lQ*_xDMy9ZC67@Vf`lbSpP$f0!_ zgqvc;CDm~|5CTypkIKXVg_7ri#T4`!A9b9+EF9a|K^1#GCZyQP>I@RHH@I|osG1{Q}N|J>9aG1lY&)IgRW6Y@)?qqB70*hnFVns^AUL^6?-t^ z4u3lvIZQ^kWeM^NBH5#sO(Buo(I4p`n;>bLWn!dROb`Q>mV3RR^$1URs48xqD255VIH-Lk_fLhrp?NTG2!aqJYRIBsYsT}b=m3XE3cT_a>e08s2P z!b&h zj^1?Fhwu<=?S_6-@hD!cDGx(sLVO&kk07^9XE5rxglB2+8?}S{8+V+(;v2@_=bF?X z+ttFEUMDkt_P)IbUJ}0cM+37&g%-vUpdw_jmbh2JY7X%eyf`T+D1aS%ToYQvKua(4 z5^({l#uQ$~R<>JwfGu1h93^a=@Wkc2%Sz zRAo+Q+R>Asnur)6tYD0wOKeevj6$`_L{IOZUZ;MZ91XxF&PS!&xSQ15lUKQ%1GV*o zFxiccqbt)JHtZQ&)yYm$#W~iq%Dy&`dJ!7k1a?4?oo`8`o?s_;h;I>t0s!oG(^XhM9H{-H9LG0#&)b%rG9&It?3sgLY;v3j;&9 zx2~kXR|=gcqj~T{Sk{%(LZeq74}h`Um*k#Bi&*&#<^ui7q+^QpL_E3y9kEpMu)5?- zJ_yT_U`*p9S(Npt@$jgJHrnGzd;$oIs(z^YD9(M474o@k-+&uJ&S=OrxT?n7MM!6U>fuNix>`Q~47$1K=^ViLUe0yk< z=XySFmpxQ8r~^6Zjm)rck_5%<@QTb5QHXl*rQ4C+NyP@F&z}A6pX#{t73l1U!Zvrk z=%`7Uhukv`#v@$lHLBcK&V6Svl4O5475gQ2z+OCV;+#o{$;i$OpPB;Nv}VqwXz2pT|_(ru9`9itP`-8Bif(GwS0~)ETBR*-Z2Pr#d-HA?ZobTSZ`DBGZ1w>8d)VK1;GDM3j7}rKN} zodtdo%a(zz!w?ysIb#PVUL7l@JdBD`qhCB4gDn?s)=Rn=ofWO}_4q7?`uw{`?XEVDfZzq;k$gj(uB%ZUZGM-AJzJFq)!1VvM z_RbMuFV#WKB`e!eSc14?+sxEGIJ{Gn`^4FO4*qrjpXhh{5+~K0HHOPRG#_y25B~6v zVd-00Rpqxa0W(uANL`8uJcMnq-rV?O-~RWYMy)V*XbLy*wk7z7R^9| zypu)OpMlCK9K**0EaNU*U?*kSVF#FT#>w@&@mxLr_BTk3yHO4Z0cH|wd(W;Q_GP3S zfxaQxXq} z-Gduff}Pp}o&A8A*}r=%$D7wQ+%emDMh1KP*ejtK9eNEY=cT5n{zL{ofMvQJD^by& zRJZxZu??)-!SMcC-7xVo{uiOmW6aVBV;mC@=W~hU46^>73MS^jy@ye@!xc4EPY2hQ zk2mh{`4ym%c`od~_=|(81`8|Hfm_;RI{`Bx_C8vM^P*LTGr|kU~S$#o24y2o+IJWbl}SXoXtv%zqiQJRTj+nr-+k%}YQ-~gngIWVX+oOZjd zCf3fJ5<~+uQ-W^iXj@bg1r!xDB~wIF0|W;y?fc=L``nNBd^}&)v(|dnde>U-df(rF zt-EJ~{WfpdwE+MCY!2}M5efhpc<1hB+5$$)^8z5uI`4+)Z%bpZITiqDsi7iH)Zj@A&c6%r;u*5Px@740c!b7oG%!BAwirx@(YttM*r@z056wRoj z)nk?ToP%ihjRzL7iyN-g3-e(bILOyoLxw(%L*kvz;;bJ8Ab8nGj`Uasx-Na|-YVS| zd|X^yKbp0=Z+rZxJ>pH#RMVV>u5n7S{DV4rtFRi+moeH!S{NU-3a60V~sj36W?(ZNph=1b%HgSzg}9v0R1DY$|b-WAB~Y^{>F0eFxh zyzy6mvNXc&bRE2IZova|!{1|(1L=Q$uR#WC9yK#K%o?0Ou28E~#JrWIzVdZ~f;sne z!IoC!m&c6dbZhGsum?*zle_x(+zQsaV~k{x({<_I_oK{&RJU4_$SLzXrOw*wRGi$LP(AK$KrKf z>+O(|1J$#lYdid;r4g>Kbvb8nYmu_{LTL<935HQt;9@vaEK8d-zw(^KOkYr7oD?%_ z0|o1QG=X=k2uCN#<(c;jrg+YfycNt7rIg0f-_oLI|0Rbbf>iPNfbV@M^OOpdxnh+;IRV(byYsVJpDkZ>Ipp z=GzU-y(9kJ@h|m?Za_H}FX*2XzJBuFslX3JYvGvuxUm#cu5cS{8iLv8E-5xwdn(3DY*SGw-amp+uop z6GUHESP4;U*3)fRaTFdY;U8EZP%oEGK}B6Ebyw*^vJh5LEf1lxv#LlT9DiHNWosR! zIS7R1ywr!?Ko%mJml{V@`OCfpQLeu{+(QR*_sidsX=Q@nZ*^`8aE$BEIU;+I= zl?Q|{r`#XmPxf=;5$>VI9fZ~b(GaOw9KI&f7K{3$6uj5X1$V?pkS)%LRx)`a2E4wO z(*1F!e|-HJ4;V=p6S;=8IXN0tNWb;K-@_H0MxZWXf)h z7}Q8ETJi80X0F10TZ|Mq%1;%>2+zo;XVeYCsNhFaj=d*3C(luZBm-{zloFGufz#y~ zt)}Ud^x+BBy(5(7@hHRQVj*t%j691N4zOj)pRe2xr4|cc8X*}pp&0`QBblQ|9Ty5@ zO<`G763_StnoYDaP#!a+d$A0_Zx)L4U5{X*4Uo-N)SwyOCRmgKWrgVT`O%}sYH>RW zl$$VpM{M#c-Gqm%XuSF^N^jhTwJbnfJM77DTvJMhyB7yRd&sCdtcdV%gmJzGPt zT^DdZ4YdZp#94t$$pR0va~^2N9vg;Koj3^<58(C(WR-?kCQjBtMjp>ds-N!au|W1Q|Kp8vR|$R{i2?$UvBr4=c(w-1m%Xb($q+OMHBsNM); zMXg#Uo&5l_eJQ@JU6EOuWeED_I+51O4$y6K8S^_a-PCVJ zG6;NIZSgnY$*%9Z!GGGuSGO6nqkesOpJg!Sn0k;e*;u}w9`(A|O&5IVfU2vsYnpV3 zUL0~_`gv!t&KzK1_g#gt!T(5RCc(TULze*%epq!e=H>Pq#P})ABVOMr%#dUNrnG+r zP{01x1*$PRF1Z81Fzn{Z(}0F?A3(8d_@9G~THXRoW$!Al!rEddBlWY5xQ0fi`9AUg z4|P5&T3zSY)Mi!7eA1q|o#NJPe6vwoJH4?^rQBAC; zS44(nW6r5U^;|`mx%_oq>H<9nb~1a7$Uy3>N!%1r6eTti7ou~m2%6W8_1d7e%!>_{$4QaXeuL@Y7MKL zc>A)QB2Vr(ZFy+o)&NBCMq$DxhWk8cp^#Yp!%rPUDcMt(*_avf3THMfGJXE&wAj{9 zJ|8W`QnCY5R;Q+SQ8s@-fg=|2Nw$Vm@{|uC#=X`z2}HX;7?EXq95(3=g6K6& zK!Xpa?e5+Oypza9PMTj!N&+fCg-^=n+}UF6gO$Z$`KLV7v(;6hkXA?ju!kbyT)hy- z%T)dc?b8Qn+c|yoscHt$f^^^D9JOd~&kuNx2XDI!^Cb!{R~@;wr4fkE%-QL<-$A^w zfSZ_P^B@s9C>#ijx65YapY%2Z>4~F9h|e<;u=AQec2I6wFIpHUn{9;jcwf)*jX zM727|1XgAbLp7vFsu+f%sWc?y=0Kv2$V0`!-#zM<-rUvHeG@02X7smtGMl}~gTGQZ z4vZ!XGO{L+3>qF%nH#&HX3R0l3%_Ju$20sKld3&6*;VOT730b1r=+(RT-wj*c6FCv z7@agL&S@!f6;b60{c^ze3p?d-e%cSZonKvibIJ^e+_`{oNYU}=`^{uA?H5prqiiSh zEd64uZ-u+laAoAaGwum^AXjV%!hEM^snxe^_=eqY1RXIPYk-Zv^4t7#zNNnD6|=6{ zi-yQm(W=9KQufsdK<$ilAa_eNr-{Cs?Jiar6}w&y!qt~=FH8da$@pH&VCa_XsBN~l z9}oTJZmY7rx#wzZP@dVZ_iWf({+@4FpP1H{c&CrJWAKlKd^?6Bx$ARo_@$I24OW3Y zALNeR1-dv^7EIV4vD@zchP}o>yZb?`ZI`EPP1jw`t>FR;#(?0z7}}BFQ4NjQVTX;3 z&BK*UwgU%5gXRHVm3^YIp*R_{p`B&H{+>8u@=oIe@ED8KC;FeFp#XVLuTpFjMG3y9 sW54|0v;Qv*`foY;CsF$64jDni4?hl58{O0YiU9$qf`4rIA^zrn0aVg%82|tP literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_ARGB_8888.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_ARGB_8888.png new file mode 100644 index 0000000000000000000000000000000000000000..90b422792db10a0205ab8fdaecbad4e1588d2106 GIT binary patch literal 1024 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoKX9-C$wJ+|*$fQK$30yfLn`LHy>pQBuz?7x zqxufUulFZa1#h3Q8zdSiVKmY%=d#o^BC`L^C&hYl_ z+s)PDn5Ki&-DBReef#-$I83kPzi{uK-R^QmBsU>Ue`@PcUH$vbsKwCO8Vzc+WHKZZ b*H`Ar7Xk^$psJ)xb)rgoqNvx>)vzjpZA~VyyrRZdDi#+J@5PY zinsgjodd7ti|v04isd>#rK>N*Q;Yf&>7x4|$w-^G6l)rd&y1T6KG`Jw<4L zjGsGrv5eSAj9)RW7Vd|*ckaBSUND?f{GrUo+~556&C;I;u{MrZs2bYZyYFP|e)fkv zaMmWvT_y(o4_jLgnX^p5tn9Wrn}5k zHmZm9PeXS5SyA?r1vd)@`litlQQ9#T+AgW9=On+&fA{lTaP5&kcdU+L(|L&)!ClzV zl)#^{%#EV`Vc`qf3^ZgEJw79y#-kvz9=p&QJcWI)G6iu8y?zs^-2h4#JM@S`WfDGeuodVqHj5rjLMUNPanaaSftP&!Jea||SoN4H{!%$G;{L|Mro?$J>qh3u+Jpxm= z(En&kRckOzCdOIpbMuq%8bzbnn9(ymOn#hgDN;li8~HdH^EPl`P$hHaPxoQU+!DEq zC8iZM2ry*(+bnsIl>OtY+&MO4Frb(&tgP=5C@A6<7v@$76h9(qDdGZt_FVWJnszb} zedfT}yh!verjo*o&G&utsGQ-|@d*ODDe4=O_OKqE|Ct7SKr&iN<|{&iNw(2ZEvj;Cu!1@EotY5fCjz&J=o?I_phLXUtazl(=cAJH2;O`;KQGA zmnw3qtZX0lQ*_xDMy9ZC67@Vf`lbSpP$f0!_ zgqvc;CDm~|5CTypkIKXVg_7ri#T4`!A9b9+EF9a|K^1#GCZyQP>I@RHH@I|osG1{Q}N|J>9aG1lY&)IgRW6Y@)?qqB70*hnFVns^AUL^6?-t^ z4u3lvIZQ^kWeM^NBH5#sO(Buo(I4p`n;>bLWn!dROb`Q>mV3RR^$1URs48xqD255VIH-Lk_fLhrp?NTG2!aqJYRIBsYsT}b=m3XE3cT_a>e08s2P z!b&h zj^1?Fhwu<=?S_6-@hD!cDGx(sLVO&kk07^9XE5rxglB2+8?}S{8+V+(;v2@_=bF?X z+ttFEUMDkt_P)IbUJ}0cM+37&g%-vUpdw_jmbh2JY7X%eyf`T+D1aS%ToYQvKua(4 z5^({l#uQ$~R<>JwfGu1h93^a=@Wkc2%Sz zRAo+Q+R>Asnur)6tYD0wOKeevj6$`_L{IOZUZ;MZ91XxF&PS!&xSQ15lUKQ%1GV*o zFxiccqbt)JHtZQ&)yYm$#W~iq%Dy&`dJ!7k1a?4?oo`8`o?s_;h;I>t0s!oG(^XhM9H{-H9LG0#&)b%rG9&It?3sgLY;v3j;&9 zx2~kXR|=gcqj~T{Sk{%(LZeq74}h`Um*k#Bi&*&#<^ui7q+^QpL_E3y9kEpMu)5?- zJ_yT_U`*p9S(Npt@$jgJHrnGzd;$oIs(z^YD9(M474o@k-+&uJ&S=OrxT?n7MM!6U>fuNix>`Q~47$1K=^ViLUe0yk< z=XySFmpxQ8r~^6Zjm)rck_5%<@QTb5QHXl*rQ4C+NyP@F&z}A6pX#{t73l1U!Zvrk z=%`7Uhukv`#v@$lHLBcK&V6Svl4O5475gQ2z+OCV;+#o{$;i$OpPB;Nv}VqwXz2pT|_(ru9`9itP`-8Bif(GwS0~)ETBR*-Z2Pr#d-HA?ZobTSZ`DBGZ1w>8d)VK1;GDM3j7}rKN} zodtdo%a(zz!w?ysIb#PVUL7l@JdBD`qhCB4gDn?s)=Rn=ofWO}_4q7?`uw{`?XEVDfZzq;k$gj(uB%ZUZGM-AJzJFq)!1VvM z_RbMuFV#WKB`e!eSc14?+sxEGIJ{Gn`^4FO4*qrjpXhh{5+~K0HHOPRG#_y25B~6v zVd-00Rpqxa0W(uANL`8uJcMnq-rV?O-~RWYMy)V*XbLy*wk7z7R^9| zypu)OpMlCK9K**0EaNU*U?*kSVF#FT#>w@&@mxLr_BTk3yHO4Z0cH|wd(W;Q_GP3S zfxaQxXq} z-Gduff}Pp}o&A8A*}r=%$D7wQ+%emDMh1KP*ejtK9eNEY=cT5n{zL{ofMvQJD^by& zRJZxZu??)-!SMcC-7xVo{uiOmW6aVBV;mC@=W~hU46^>73MS^jy@ye@!xc4EPY2hQ zk2mh{`4ym%c`od~_=|(81`8|Hfm_;RI{`Bx_C8vM^P*LTGr|kU~S$#o24y2o+IJWbl}SXoXtv%zqiQJRTj+nr-+k%}YQ-~gngIWVX+oOZjd zCf3fJ5<~+uQ-W^iXj@bg1r!xDB~wIF0|W;y?fc=L``nNBd^}&)v(|dnde>U-df(rF zt-EJ~{WfpdwE+MCY!2}M5efhpc<1hB+5$$)^8z5uI`4+)Z%bpZITiqDsi7iH)Zj@A&c6%r;u*5Px@740c!b7oG%!BAwirx@(YttM*r@z056wRoj z)nk?ToP%ihjRzL7iyN-g3-e(bILOyoLxw(%L*kvz;;bJ8Ab8nGj`Uasx-Na|-YVS| zd|X^yKbp0=Z+rZxJ>pH#RMVV>u5n7S{DV4rtFRi+moeH!S{NU-3a60V~sj36W?(ZNph=1b%HgSzg}9v0R1DY$|b-WAB~Y^{>F0eFxh zyzy6mvNXc&bRE2IZova|!{1|(1L=Q$uR#WC9yK#K%o?0Ou28E~#JrWIzVdZ~f;sne z!IoC!m&c6dbZhGsum?*zle_x(+zQsaV~k{x({<_I_oK{&RJU4_$SLzXrOw*wRGi$LP(AK$KrKf z>+O(|1J$#lYdid;r4g>Kbvb8nYmu_{LTL<935HQt;9@vaEK8d-zw(^KOkYr7oD?%_ z0|o1QG=X=k2uCN#<(c;jrg+YfycNt7rIg0f-_oLI|0Rbbf>iPNfbV@M^OOpdxnh+;IRV(byYsVJpDkZ>Ipp z=GzU-y(9kJ@h|m?Za_H}FX*2XzJBuFslX3JYvGvuxUm#cu5cS{8iLv8E-5xwdn(3DY*SGw-amp+uop z6GUHESP4;U*3)fRaTFdY;U8EZP%oEGK}B6Ebyw*^vJh5LEf1lxv#LlT9DiHNWosR! zIS7R1ywr!?Ko%mJml{V@`OCfpQLeu{+(QR*_sidsX=Q@nZ*^`8aE$BEIU;+I= zl?Q|{r`#XmPxf=;5$>VI9fZ~b(GaOw9KI&f7K{3$6uj5X1$V?pkS)%LRx)`a2E4wO z(*1F!e|-HJ4;V=p6S;=8IXN0tNWb;K-@_H0MxZWXf)h z7}Q8ETJi80X0F10TZ|Mq%1;%>2+zo;XVeYCsNhFaj=d*3C(luZBm-{zloFGufz#y~ zt)}Ud^x+BBy(5(7@hHRQVj*t%j691N4zOj)pRe2xr4|cc8X*}pp&0`QBblQ|9Ty5@ zO<`G763_StnoYDaP#!a+d$A0_Zx)L4U5{X*4Uo-N)SwyOCRmgKWrgVT`O%}sYH>RW zl$$VpM{M#c-Gqm%XuSF^N^jhTwJbnfJM77DTvJMhyB7yRd&sCdtcdV%gmJzGPt zT^DdZ4YdZp#94t$$pR0va~^2N9vg;Koj3^<58(C(WR-?kCQjBtMjp>ds-N!au|W1Q|Kp8vR|$R{i2?$UvBr4=c(w-1m%Xb($q+OMHBsNM); zMXg#Uo&5l_eJQ@JU6EOuWeED_I+51O4$y6K8S^_a-PCVJ zG6;NIZSgnY$*%9Z!GGGuSGO6nqkesOpJg!Sn0k;e*;u}w9`(A|O&5IVfU2vsYnpV3 zUL0~_`gv!t&KzK1_g#gt!T(5RCc(TULze*%epq!e=H>Pq#P})ABVOMr%#dUNrnG+r zP{01x1*$PRF1Z81Fzn{Z(}0F?A3(8d_@9G~THXRoW$!Al!rEddBlWY5xQ0fi`9AUg z4|P5&T3zSY)Mi!7eA1q|o#NJPe6vwoJH4?^rQBAC; zS44(nW6r5U^;|`mx%_oq>H<9nb~1a7$Uy3>N!%1r6eTti7ou~m2%6W8_1d7e%!>_{$4QaXeuL@Y7MKL zc>A)QB2Vr(ZFy+o)&NBCMq$DxhWk8cp^#Yp!%rPUDcMt(*_avf3THMfGJXE&wAj{9 zJ|8W`QnCY5R;Q+SQ8s@-fg=|2Nw$Vm@{|uC#=X`z2}HX;7?EXq95(3=g6K6& zK!Xpa?e5+Oypza9PMTj!N&+fCg-^=n+}UF6gO$Z$`KLV7v(;6hkXA?ju!kbyT)hy- z%T)dc?b8Qn+c|yoscHt$f^^^D9JOd~&kuNx2XDI!^Cb!{R~@;wr4fkE%-QL<-$A^w zfSZ_P^B@s9C>#ijx65YapY%2Z>4~F9h|e<;u=AQec2I6wFIpHUn{9;jcwf)*jX zM727|1XgAbLp7vFsu+f%sWc?y=0Kv2$V0`!-#zM<-rUvHeG@02X7smtGMl}~gTGQZ z4vZ!XGO{L+3>qF%nH#&HX3R0l3%_Ju$20sKld3&6*;VOT730b1r=+(RT-wj*c6FCv z7@agL&S@!f6;b60{c^ze3p?d-e%cSZonKvibIJ^e+_`{oNYU}=`^{uA?H5prqiiSh zEd64uZ-u+laAoAaGwum^AXjV%!hEM^snxe^_=eqY1RXIPYk-Zv^4t7#zNNnD6|=6{ zi-yQm(W=9KQufsdK<$ilAa_eNr-{Cs?Jiar6}w&y!qt~=FH8da$@pH&VCa_XsBN~l z9}oTJZmY7rx#wzZP@dVZ_iWf({+@4FpP1H{c&CrJWAKlKd^?6Bx$ARo_@$I24OW3Y zALNeR1-dv^7EIV4vD@zchP}o>yZb?`ZI`EPP1jw`t>FR;#(?0z7}}BFQ4NjQVTX;3 z&BK*UwgU%5gXRHVm3^YIp*R_{p`B&H{+>8u@=oIe@ED8KC;FeFp#XVLuTpFjMG3y9 sW54|0v;Qv*`foY;CsF$64jDni4?hl58{O0YiU9$qf`4rIA^zrn0aVg%82|tP literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01.png new file mode 100644 index 0000000000000000000000000000000000000000..59928bcd1070454803935e4a81b825c039f3eac1 GIT binary patch literal 3655 zcmeHKZ9LNp``@I{ITRg~GLFZ)7&@Io47apdy6vDiCUhv0ki-~`>J;+0WKPPIrx|7* zhM1v)RMU1dwwkO54fDf1Z1Xs~o%_xGx!?Z(|Cj$a*XR0NFTO9X>+^lMGJo+zsPER> z4FZAGk?wBZAQ0GPyI)YF)p&8!K&3XoDl8eWz4 zTjhL%G5G-7d)E6?;7}<&y=NG5cEd9dV{M{;#O-YNgZMr5g^|)L@Rd~y3fdaaoU}jp zi0jA5<_?v_ckv|@oJoJV)Pl?&gy4}!r8Bhb6a-oAI(K|PPQNw_ZJxW{MCW=iGAoY<{Yg+3dm=Nc?Fk6cOqJ-P0tHF)#hYOzT^CRnetu9K zesi{5u3-7-aO2w1`lMBRm0wHQe?%+{{oc-z4EerOh=j%3jSyq_AE=gzXh3{yUGh|R z@&jA-`la6jrpRb>OT!T;$#T2X1@l_%@BPt6Gw zio(=KFS~-Mlez+N+p2#gno@tjIJWM=WjFoQaW6-vFE4K3r`E}1!>n3fD76F@@BbA) z!<)2#`o$yxbCFf02$We?}ag$?=ES~%2NL1n%byMKa`qWU@O^{2;?wD$nJ zvZ-)!Wu~aAv7XvA$R}=7k5rNuR*~boBuj-RM3{*Exk0G~Aw{jGfJ~T)K&LCv+9QSj z_l3fn!;nFTnjsB+Be=V_obVpH+-z-3zQeBJVB zRSX4n7!t+s0QejOEc42ESA}qCNfx(w=vjOiguW-;*)q2NB(=S1Yt-mr2gYew18y|; zzEhfr$rw&)De1pvG|YFeMLAr>e_ei0j!~RRV-)n8LYG4aJ@rHT_UQ(fO&-vvhle*{ z9hS^aM)Ymuw05hTq0gxpf7x(56zG<1Q;LqEON-dk&GBE%3g+#51W9%NXqXT=#7(kd z3Xl$d<)bl_srl6wHe&2$ZOa8?X)?b%{?5nck|8^aJe8(+6iX0#N!z}bieiW&7|?b; zSp36nQ(`ndkk`ViSF543nxE&$Qhjf3U=V4E0+BAl6S3hOd3ocQl5)OpN0uRd!bFZ) zqRtEFG2+bo> z+lbiWK*#vp!y#Q>m|ZD%3wRR+Qbgmc12;6kv3|q!%^i&QkL;D2T#G=9=WHos{xoGL z>=;t6AJH{TBkJetgj4m=%YpH(7wG4|9psh!VyrzQK)?CtZ7aK-Uu(C>sVC?&H+cwt zl1g|M24r?rSmj)=)PD94vHkDF18M=20k7;izQly-s5A?NA^ItIm3bqJ!wscwb$ryq z#H<(2iS@v&dP`A$YnW((|GLK;Wt|XXKJ-xXJ!Kg?6Ks#2$vo-0&{ro_p=JZIQr4@` z%NtMjU9|2Es47xAxMo>KoIbVE6vw)GgS`nSb=ThgG^7FbV!pzraA#z;!8>L1xBTWG z8+8V)H|Jxbs&$&vt={hd7X2wQ+rXVx3dK1S^?n9t74ul}v=*Eh&qBy1rRBmm27(<{fICLpn|D{XP3HVT9btK*iqqRS*HQbY=uyBu;)CT!GH`uuhh_(DT`5BdkDOESV>W4fwrg%avb(J> z*8>xrHM@I>ol@Uw_9s+-;+dwTBP<`j$_uLr4}67J(a)}c`~vO=k$M2%;~aLeNa!8N zSj)-wMP>SAoiC=+F7$9ZobNx2vg7YMkCBiB8VP~nonq-DyB2ok8X@+^>)euzyg!rJ zLrCezcgMsTCUsFCy^uJP9VR(K)23G%A{j0)?oh?_8Q>2kWETXh|Ji1@eqz7qmyKxB zA%2OL`SqfCdlymuK(cniw;IZnNjEQAoD4H%jfyLG&i6I<*g6PrQAvGjgNEB~0!+uH zfo$&nwM&i%zKr-r>g46kiKfl7Rs-~G+$z8AmsFK2zy~WGe#eL<_+A#m1rVFhT;E>9ZpC$>G6H|Ft;0g zQI`r_?bdy9_6p@j1b@i^?L9kcJ<4(Lnryu6&*Jp8tgpPv2bsF4r?J^*GrD)$ zao3=5?u5IX)ND1-W*I{cA})!26^EG#>^+e26(#ZXvS6Gv$!Hc}vojQLWO+u?81k${ z*fPA%KD_pFA~;5g@^`0_vg7X%RQy}rOx~x(t%}V-6gMEHiW*7=8ZF~sls1i8?$XsL z8$I@GjggzX)YDr0ccJl zusGjND>RBY-he^9!0xGyQXY1%x|T$B2z)zzeKk_O zf62i=S1d6Hw?<3kVYLLYJlfg8zo^LkeWeI7Wgud$&zu1HemV5nm2mJ3lq0@!`I?vC zY8r8}z>xp0nz4lUka)uc9+QGZN2Y@o+ouy~^vgRJ#2cbVk+)c99qFww*3uL*pZmHO zJAk`ojTgc_DG>JGs&!G;tm#B-Bd+d!K7(o6Uu{~;C2NUaC!ByhGz|NPz7o~x>|(+~ zbz%iJ?Carfi_y3x)dB2|x$iZyG^5se7ZbF!QI}vu{M+a`(Pfx-MzujkPn1notg8Y- zI||=C0Oqpik-p9ab^&=U^@8RH$BvFJ?`Ql%3u%WF?I{zDmIal>lMr@uThI4IW$=Hq zfYP9xICI`^-fPEF$hMGBg8gnY*w?V7@@qpg3r_15u->tnf#unTHVEn?h`1PM@Qijl z`E3jKXH9luE{nH5yX+1F$;_N~+cqDKbDDKFIejtM1GgtX2hyX~mt|NBkNp5o|3J{S zd$W{fND==!GkuHuHK%#2bET6^A~t1f9&I2Elr`aRMN()*-9QlesW+^szsWQWxM+Q<#do| zPxCg%x38T>!@<*rHv)_W=FE(}J-LZm$}Z?bN<021p&U@)4*dQ%T@}rP+YcfLdD_#h J-u2?0e*wX57cBq) literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01_no_vtiled.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01_no_vtiled.png new file mode 100644 index 0000000000000000000000000000000000000000..59928bcd1070454803935e4a81b825c039f3eac1 GIT binary patch literal 3655 zcmeHKZ9LNp``@I{ITRg~GLFZ)7&@Io47apdy6vDiCUhv0ki-~`>J;+0WKPPIrx|7* zhM1v)RMU1dwwkO54fDf1Z1Xs~o%_xGx!?Z(|Cj$a*XR0NFTO9X>+^lMGJo+zsPER> z4FZAGk?wBZAQ0GPyI)YF)p&8!K&3XoDl8eWz4 zTjhL%G5G-7d)E6?;7}<&y=NG5cEd9dV{M{;#O-YNgZMr5g^|)L@Rd~y3fdaaoU}jp zi0jA5<_?v_ckv|@oJoJV)Pl?&gy4}!r8Bhb6a-oAI(K|PPQNw_ZJxW{MCW=iGAoY<{Yg+3dm=Nc?Fk6cOqJ-P0tHF)#hYOzT^CRnetu9K zesi{5u3-7-aO2w1`lMBRm0wHQe?%+{{oc-z4EerOh=j%3jSyq_AE=gzXh3{yUGh|R z@&jA-`la6jrpRb>OT!T;$#T2X1@l_%@BPt6Gw zio(=KFS~-Mlez+N+p2#gno@tjIJWM=WjFoQaW6-vFE4K3r`E}1!>n3fD76F@@BbA) z!<)2#`o$yxbCFf02$We?}ag$?=ES~%2NL1n%byMKa`qWU@O^{2;?wD$nJ zvZ-)!Wu~aAv7XvA$R}=7k5rNuR*~boBuj-RM3{*Exk0G~Aw{jGfJ~T)K&LCv+9QSj z_l3fn!;nFTnjsB+Be=V_obVpH+-z-3zQeBJVB zRSX4n7!t+s0QejOEc42ESA}qCNfx(w=vjOiguW-;*)q2NB(=S1Yt-mr2gYew18y|; zzEhfr$rw&)De1pvG|YFeMLAr>e_ei0j!~RRV-)n8LYG4aJ@rHT_UQ(fO&-vvhle*{ z9hS^aM)Ymuw05hTq0gxpf7x(56zG<1Q;LqEON-dk&GBE%3g+#51W9%NXqXT=#7(kd z3Xl$d<)bl_srl6wHe&2$ZOa8?X)?b%{?5nck|8^aJe8(+6iX0#N!z}bieiW&7|?b; zSp36nQ(`ndkk`ViSF543nxE&$Qhjf3U=V4E0+BAl6S3hOd3ocQl5)OpN0uRd!bFZ) zqRtEFG2+bo> z+lbiWK*#vp!y#Q>m|ZD%3wRR+Qbgmc12;6kv3|q!%^i&QkL;D2T#G=9=WHos{xoGL z>=;t6AJH{TBkJetgj4m=%YpH(7wG4|9psh!VyrzQK)?CtZ7aK-Uu(C>sVC?&H+cwt zl1g|M24r?rSmj)=)PD94vHkDF18M=20k7;izQly-s5A?NA^ItIm3bqJ!wscwb$ryq z#H<(2iS@v&dP`A$YnW((|GLK;Wt|XXKJ-xXJ!Kg?6Ks#2$vo-0&{ro_p=JZIQr4@` z%NtMjU9|2Es47xAxMo>KoIbVE6vw)GgS`nSb=ThgG^7FbV!pzraA#z;!8>L1xBTWG z8+8V)H|Jxbs&$&vt={hd7X2wQ+rXVx3dK1S^?n9t74ul}v=*Eh&qBy1rRBmm27(<{fICLpn|D{XP3HVT9btK*iqqRS*HQbY=uyBu;)CT!GH`uuhh_(DT`5BdkDOESV>W4fwrg%avb(J> z*8>xrHM@I>ol@Uw_9s+-;+dwTBP<`j$_uLr4}67J(a)}c`~vO=k$M2%;~aLeNa!8N zSj)-wMP>SAoiC=+F7$9ZobNx2vg7YMkCBiB8VP~nonq-DyB2ok8X@+^>)euzyg!rJ zLrCezcgMsTCUsFCy^uJP9VR(K)23G%A{j0)?oh?_8Q>2kWETXh|Ji1@eqz7qmyKxB zA%2OL`SqfCdlymuK(cniw;IZnNjEQAoD4H%jfyLG&i6I<*g6PrQAvGjgNEB~0!+uH zfo$&nwM&i%zKr-r>g46kiKfl7Rs-~G+$z8AmsFK2zy~WGe#eL<_+A#m1rVFhT;E>9ZpC$>G6H|Ft;0g zQI`r_?bdy9_6p@j1b@i^?L9kcJ<4(Lnryu6&*Jp8tgpPv2bsF4r?J^*GrD)$ zao3=5?u5IX)ND1-W*I{cA})!26^EG#>^+e26(#ZXvS6Gv$!Hc}vojQLWO+u?81k${ z*fPA%KD_pFA~;5g@^`0_vg7X%RQy}rOx~x(t%}V-6gMEHiW*7=8ZF~sls1i8?$XsL z8$I@GjggzX)YDr0ccJl zusGjND>RBY-he^9!0xGyQXY1%x|T$B2z)zzeKk_O zf62i=S1d6Hw?<3kVYLLYJlfg8zo^LkeWeI7Wgud$&zu1HemV5nm2mJ3lq0@!`I?vC zY8r8}z>xp0nz4lUka)uc9+QGZN2Y@o+ouy~^vgRJ#2cbVk+)c99qFww*3uL*pZmHO zJAk`ojTgc_DG>JGs&!G;tm#B-Bd+d!K7(o6Uu{~;C2NUaC!ByhGz|NPz7o~x>|(+~ zbz%iJ?Carfi_y3x)dB2|x$iZyG^5se7ZbF!QI}vu{M+a`(Pfx-MzujkPn1notg8Y- zI||=C0Oqpik-p9ab^&=U^@8RH$BvFJ?`Ql%3u%WF?I{zDmIal>lMr@uThI4IW$=Hq zfYP9xICI`^-fPEF$hMGBg8gnY*w?V7@@qpg3r_15u->tnf#unTHVEn?h`1PM@Qijl z`E3jKXH9luE{nH5yX+1F$;_N~+cqDKbDDKFIejtM1GgtX2hyX~mt|NBkNp5o|3J{S zd$W{fND==!GkuHuHK%#2bET6^A~t1f9&I2Elr`aRMN()*-9QlesW+^szsWQWxM+Q<#do| zPxCg%x38T>!@<*rHv)_W=FE(}J-LZm$}Z?bN<021p&U@)4*dQ%T@}rP+YcfLdD_#h J-u2?0e*wX57cBq) literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10.png new file mode 100644 index 0000000000000000000000000000000000000000..59928bcd1070454803935e4a81b825c039f3eac1 GIT binary patch literal 3655 zcmeHKZ9LNp``@I{ITRg~GLFZ)7&@Io47apdy6vDiCUhv0ki-~`>J;+0WKPPIrx|7* zhM1v)RMU1dwwkO54fDf1Z1Xs~o%_xGx!?Z(|Cj$a*XR0NFTO9X>+^lMGJo+zsPER> z4FZAGk?wBZAQ0GPyI)YF)p&8!K&3XoDl8eWz4 zTjhL%G5G-7d)E6?;7}<&y=NG5cEd9dV{M{;#O-YNgZMr5g^|)L@Rd~y3fdaaoU}jp zi0jA5<_?v_ckv|@oJoJV)Pl?&gy4}!r8Bhb6a-oAI(K|PPQNw_ZJxW{MCW=iGAoY<{Yg+3dm=Nc?Fk6cOqJ-P0tHF)#hYOzT^CRnetu9K zesi{5u3-7-aO2w1`lMBRm0wHQe?%+{{oc-z4EerOh=j%3jSyq_AE=gzXh3{yUGh|R z@&jA-`la6jrpRb>OT!T;$#T2X1@l_%@BPt6Gw zio(=KFS~-Mlez+N+p2#gno@tjIJWM=WjFoQaW6-vFE4K3r`E}1!>n3fD76F@@BbA) z!<)2#`o$yxbCFf02$We?}ag$?=ES~%2NL1n%byMKa`qWU@O^{2;?wD$nJ zvZ-)!Wu~aAv7XvA$R}=7k5rNuR*~boBuj-RM3{*Exk0G~Aw{jGfJ~T)K&LCv+9QSj z_l3fn!;nFTnjsB+Be=V_obVpH+-z-3zQeBJVB zRSX4n7!t+s0QejOEc42ESA}qCNfx(w=vjOiguW-;*)q2NB(=S1Yt-mr2gYew18y|; zzEhfr$rw&)De1pvG|YFeMLAr>e_ei0j!~RRV-)n8LYG4aJ@rHT_UQ(fO&-vvhle*{ z9hS^aM)Ymuw05hTq0gxpf7x(56zG<1Q;LqEON-dk&GBE%3g+#51W9%NXqXT=#7(kd z3Xl$d<)bl_srl6wHe&2$ZOa8?X)?b%{?5nck|8^aJe8(+6iX0#N!z}bieiW&7|?b; zSp36nQ(`ndkk`ViSF543nxE&$Qhjf3U=V4E0+BAl6S3hOd3ocQl5)OpN0uRd!bFZ) zqRtEFG2+bo> z+lbiWK*#vp!y#Q>m|ZD%3wRR+Qbgmc12;6kv3|q!%^i&QkL;D2T#G=9=WHos{xoGL z>=;t6AJH{TBkJetgj4m=%YpH(7wG4|9psh!VyrzQK)?CtZ7aK-Uu(C>sVC?&H+cwt zl1g|M24r?rSmj)=)PD94vHkDF18M=20k7;izQly-s5A?NA^ItIm3bqJ!wscwb$ryq z#H<(2iS@v&dP`A$YnW((|GLK;Wt|XXKJ-xXJ!Kg?6Ks#2$vo-0&{ro_p=JZIQr4@` z%NtMjU9|2Es47xAxMo>KoIbVE6vw)GgS`nSb=ThgG^7FbV!pzraA#z;!8>L1xBTWG z8+8V)H|Jxbs&$&vt={hd7X2wQ+rXVx3dK1S^?n9t74ul}v=*Eh&qBy1rRBmm27(<{fICLpn|D{XP3HVT9btK*iqqRS*HQbY=uyBu;)CT!GH`uuhh_(DT`5BdkDOESV>W4fwrg%avb(J> z*8>xrHM@I>ol@Uw_9s+-;+dwTBP<`j$_uLr4}67J(a)}c`~vO=k$M2%;~aLeNa!8N zSj)-wMP>SAoiC=+F7$9ZobNx2vg7YMkCBiB8VP~nonq-DyB2ok8X@+^>)euzyg!rJ zLrCezcgMsTCUsFCy^uJP9VR(K)23G%A{j0)?oh?_8Q>2kWETXh|Ji1@eqz7qmyKxB zA%2OL`SqfCdlymuK(cniw;IZnNjEQAoD4H%jfyLG&i6I<*g6PrQAvGjgNEB~0!+uH zfo$&nwM&i%zKr-r>g46kiKfl7Rs-~G+$z8AmsFK2zy~WGe#eL<_+A#m1rVFhT;E>9ZpC$>G6H|Ft;0g zQI`r_?bdy9_6p@j1b@i^?L9kcJ<4(Lnryu6&*Jp8tgpPv2bsF4r?J^*GrD)$ zao3=5?u5IX)ND1-W*I{cA})!26^EG#>^+e26(#ZXvS6Gv$!Hc}vojQLWO+u?81k${ z*fPA%KD_pFA~;5g@^`0_vg7X%RQy}rOx~x(t%}V-6gMEHiW*7=8ZF~sls1i8?$XsL z8$I@GjggzX)YDr0ccJl zusGjND>RBY-he^9!0xGyQXY1%x|T$B2z)zzeKk_O zf62i=S1d6Hw?<3kVYLLYJlfg8zo^LkeWeI7Wgud$&zu1HemV5nm2mJ3lq0@!`I?vC zY8r8}z>xp0nz4lUka)uc9+QGZN2Y@o+ouy~^vgRJ#2cbVk+)c99qFww*3uL*pZmHO zJAk`ojTgc_DG>JGs&!G;tm#B-Bd+d!K7(o6Uu{~;C2NUaC!ByhGz|NPz7o~x>|(+~ zbz%iJ?Carfi_y3x)dB2|x$iZyG^5se7ZbF!QI}vu{M+a`(Pfx-MzujkPn1notg8Y- zI||=C0Oqpik-p9ab^&=U^@8RH$BvFJ?`Ql%3u%WF?I{zDmIal>lMr@uThI4IW$=Hq zfYP9xICI`^-fPEF$hMGBg8gnY*w?V7@@qpg3r_15u->tnf#unTHVEn?h`1PM@Qijl z`E3jKXH9luE{nH5yX+1F$;_N~+cqDKbDDKFIejtM1GgtX2hyX~mt|NBkNp5o|3J{S zd$W{fND==!GkuHuHK%#2bET6^A~t1f9&I2Elr`aRMN()*-9QlesW+^szsWQWxM+Q<#do| zPxCg%x38T>!@<*rHv)_W=FE(}J-LZm$}Z?bN<021p&U@)4*dQ%T@}rP+YcfLdD_#h J-u2?0e*wX57cBq) literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10_no_vtiled.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10_no_vtiled.png new file mode 100644 index 0000000000000000000000000000000000000000..59928bcd1070454803935e4a81b825c039f3eac1 GIT binary patch literal 3655 zcmeHKZ9LNp``@I{ITRg~GLFZ)7&@Io47apdy6vDiCUhv0ki-~`>J;+0WKPPIrx|7* zhM1v)RMU1dwwkO54fDf1Z1Xs~o%_xGx!?Z(|Cj$a*XR0NFTO9X>+^lMGJo+zsPER> z4FZAGk?wBZAQ0GPyI)YF)p&8!K&3XoDl8eWz4 zTjhL%G5G-7d)E6?;7}<&y=NG5cEd9dV{M{;#O-YNgZMr5g^|)L@Rd~y3fdaaoU}jp zi0jA5<_?v_ckv|@oJoJV)Pl?&gy4}!r8Bhb6a-oAI(K|PPQNw_ZJxW{MCW=iGAoY<{Yg+3dm=Nc?Fk6cOqJ-P0tHF)#hYOzT^CRnetu9K zesi{5u3-7-aO2w1`lMBRm0wHQe?%+{{oc-z4EerOh=j%3jSyq_AE=gzXh3{yUGh|R z@&jA-`la6jrpRb>OT!T;$#T2X1@l_%@BPt6Gw zio(=KFS~-Mlez+N+p2#gno@tjIJWM=WjFoQaW6-vFE4K3r`E}1!>n3fD76F@@BbA) z!<)2#`o$yxbCFf02$We?}ag$?=ES~%2NL1n%byMKa`qWU@O^{2;?wD$nJ zvZ-)!Wu~aAv7XvA$R}=7k5rNuR*~boBuj-RM3{*Exk0G~Aw{jGfJ~T)K&LCv+9QSj z_l3fn!;nFTnjsB+Be=V_obVpH+-z-3zQeBJVB zRSX4n7!t+s0QejOEc42ESA}qCNfx(w=vjOiguW-;*)q2NB(=S1Yt-mr2gYew18y|; zzEhfr$rw&)De1pvG|YFeMLAr>e_ei0j!~RRV-)n8LYG4aJ@rHT_UQ(fO&-vvhle*{ z9hS^aM)Ymuw05hTq0gxpf7x(56zG<1Q;LqEON-dk&GBE%3g+#51W9%NXqXT=#7(kd z3Xl$d<)bl_srl6wHe&2$ZOa8?X)?b%{?5nck|8^aJe8(+6iX0#N!z}bieiW&7|?b; zSp36nQ(`ndkk`ViSF543nxE&$Qhjf3U=V4E0+BAl6S3hOd3ocQl5)OpN0uRd!bFZ) zqRtEFG2+bo> z+lbiWK*#vp!y#Q>m|ZD%3wRR+Qbgmc12;6kv3|q!%^i&QkL;D2T#G=9=WHos{xoGL z>=;t6AJH{TBkJetgj4m=%YpH(7wG4|9psh!VyrzQK)?CtZ7aK-Uu(C>sVC?&H+cwt zl1g|M24r?rSmj)=)PD94vHkDF18M=20k7;izQly-s5A?NA^ItIm3bqJ!wscwb$ryq z#H<(2iS@v&dP`A$YnW((|GLK;Wt|XXKJ-xXJ!Kg?6Ks#2$vo-0&{ro_p=JZIQr4@` z%NtMjU9|2Es47xAxMo>KoIbVE6vw)GgS`nSb=ThgG^7FbV!pzraA#z;!8>L1xBTWG z8+8V)H|Jxbs&$&vt={hd7X2wQ+rXVx3dK1S^?n9t74ul}v=*Eh&qBy1rRBmm27(<{fICLpn|D{XP3HVT9btK*iqqRS*HQbY=uyBu;)CT!GH`uuhh_(DT`5BdkDOESV>W4fwrg%avb(J> z*8>xrHM@I>ol@Uw_9s+-;+dwTBP<`j$_uLr4}67J(a)}c`~vO=k$M2%;~aLeNa!8N zSj)-wMP>SAoiC=+F7$9ZobNx2vg7YMkCBiB8VP~nonq-DyB2ok8X@+^>)euzyg!rJ zLrCezcgMsTCUsFCy^uJP9VR(K)23G%A{j0)?oh?_8Q>2kWETXh|Ji1@eqz7qmyKxB zA%2OL`SqfCdlymuK(cniw;IZnNjEQAoD4H%jfyLG&i6I<*g6PrQAvGjgNEB~0!+uH zfo$&nwM&i%zKr-r>g46kiKfl7Rs-~G+$z8AmsFK2zy~WGe#eL<_+A#m1rVFhT;E>9ZpC$>G6H|Ft;0g zQI`r_?bdy9_6p@j1b@i^?L9kcJ<4(Lnryu6&*Jp8tgpPv2bsF4r?J^*GrD)$ zao3=5?u5IX)ND1-W*I{cA})!26^EG#>^+e26(#ZXvS6Gv$!Hc}vojQLWO+u?81k${ z*fPA%KD_pFA~;5g@^`0_vg7X%RQy}rOx~x(t%}V-6gMEHiW*7=8ZF~sls1i8?$XsL z8$I@GjggzX)YDr0ccJl zusGjND>RBY-he^9!0xGyQXY1%x|T$B2z)zzeKk_O zf62i=S1d6Hw?<3kVYLLYJlfg8zo^LkeWeI7Wgud$&zu1HemV5nm2mJ3lq0@!`I?vC zY8r8}z>xp0nz4lUka)uc9+QGZN2Y@o+ouy~^vgRJ#2cbVk+)c99qFww*3uL*pZmHO zJAk`ojTgc_DG>JGs&!G;tm#B-Bd+d!K7(o6Uu{~;C2NUaC!ByhGz|NPz7o~x>|(+~ zbz%iJ?Carfi_y3x)dB2|x$iZyG^5se7ZbF!QI}vu{M+a`(Pfx-MzujkPn1notg8Y- zI||=C0Oqpik-p9ab^&=U^@8RH$BvFJ?`Ql%3u%WF?I{zDmIal>lMr@uThI4IW$=Hq zfYP9xICI`^-fPEF$hMGBg8gnY*w?V7@@qpg3r_15u->tnf#unTHVEn?h`1PM@Qijl z`E3jKXH9luE{nH5yX+1F$;_N~+cqDKbDDKFIejtM1GgtX2hyX~mt|NBkNp5o|3J{S zd$W{fND==!GkuHuHK%#2bET6^A~t1f9&I2Elr`aRMN()*-9QlesW+^szsWQWxM+Q<#do| zPxCg%x38T>!@<*rHv)_W=FE(}J-LZm$}Z?bN<021p&U@)4*dQ%T@}rP+YcfLdD_#h J-u2?0e*wX57cBq) literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_565.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_565.png new file mode 100644 index 0000000000000000000000000000000000000000..77577a65601abe1b7eb8d0771e8e1ceb795e8400 GIT binary patch literal 3930 zcmeH~c~lZ;yTH-Rv?kYS`O2-d8ke$0GdDD+!qn1qOc4|r7Xk^$psJ)xb)rgoqNvx>)vzjpZA~VyyrRZdDi#+J@5PY zinsgjodd7ti|v04isd>#rK>N*Q;Yf&>7x4|$w-^G6l)rd&y1T6KG`Jw<4L zjGsGrv5eSAj9)RW7Vd|*ckaBSUND?f{GrUo+~556&C;I;u{MrZs2bYZyYFP|e)fkv zaMmWvT_y(o4_jLgnX^p5tn9Wrn}5k zHmZm9PeXS5SyA?r1vd)@`litlQQ9#T+AgW9=On+&fA{lTaP5&kcdU+L(|L&)!ClzV zl)#^{%#EV`Vc`qf3^ZgEJw79y#-kvz9=p&QJcWI)G6iu8y?zs^-2h4#JM@S`WfDGeuodVqHj5rjLMUNPanaaSftP&!Jea||SoN4H{!%$G;{L|Mro?$J>qh3u+Jpxm= z(En&kRckOzCdOIpbMuq%8bzbnn9(ymOn#hgDN;li8~HdH^EPl`P$hHaPxoQU+!DEq zC8iZM2ry*(+bnsIl>OtY+&MO4Frb(&tgP=5C@A6<7v@$76h9(qDdGZt_FVWJnszb} zedfT}yh!verjo*o&G&utsGQ-|@d*ODDe4=O_OKqE|Ct7SKr&iN<|{&iNw(2ZEvj;Cu!1@EotY5fCjz&J=o?I_phLXUtazl(=cAJH2;O`;KQGA zmnw3qtZX0lQ*_xDMy9ZC67@Vf`lbSpP$f0!_ zgqvc;CDm~|5CTypkIKXVg_7ri#T4`!A9b9+EF9a|K^1#GCZyQP>I@RHH@I|osG1{Q}N|J>9aG1lY&)IgRW6Y@)?qqB70*hnFVns^AUL^6?-t^ z4u3lvIZQ^kWeM^NBH5#sO(Buo(I4p`n;>bLWn!dROb`Q>mV3RR^$1URs48xqD255VIH-Lk_fLhrp?NTG2!aqJYRIBsYsT}b=m3XE3cT_a>e08s2P z!b&h zj^1?Fhwu<=?S_6-@hD!cDGx(sLVO&kk07^9XE5rxglB2+8?}S{8+V+(;v2@_=bF?X z+ttFEUMDkt_P)IbUJ}0cM+37&g%-vUpdw_jmbh2JY7X%eyf`T+D1aS%ToYQvKua(4 z5^({l#uQ$~R<>JwfGu1h93^a=@Wkc2%Sz zRAo+Q+R>Asnur)6tYD0wOKeevj6$`_L{IOZUZ;MZ91XxF&PS!&xSQ15lUKQ%1GV*o zFxiccqbt)JHtZQ&)yYm$#W~iq%Dy&`dJ!7k1a?4?oo`8`o?s_;h;I>t0s!oG(^XhM9H{-H9LG0#&)b%rG9&It?3sgLY;v3j;&9 zx2~kXR|=gcqj~T{Sk{%(LZeq74}h`Um*k#Bi&*&#<^ui7q+^QpL_E3y9kEpMu)5?- zJ_yT_U`*p9S(Npt@$jgJHrnGzd;$oIs(z^YD9(M474o@k-+&uJ&S=OrxT?n7MM!6U>fuNix>`Q~47$1K=^ViLUe0yk< z=XySFmpxQ8r~^6Zjm)rck_5%<@QTb5QHXl*rQ4C+NyP@F&z}A6pX#{t73l1U!Zvrk z=%`7Uhukv`#v@$lHLBcK&V6Svl4O5475gQ2z+OCV;+#o{$;i$OpPB;Nv}VqwXz2pT|_(ru9`9itP`-8Bif(GwS0~)ETBR*-Z2Pr#d-HA?ZobTSZ`DBGZ1w>8d)VK1;GDM3j7}rKN} zodtdo%a(zz!w?ysIb#PVUL7l@JdBD`qhCB4gDn?s)=Rn=ofWO}_4q7?`uw{`?XEVDfZzq;k$gj(uB%ZUZGM-AJzJFq)!1VvM z_RbMuFV#WKB`e!eSc14?+sxEGIJ{Gn`^4FO4*qrjpXhh{5+~K0HHOPRG#_y25B~6v zVd-00Rpqxa0W(uANL`8uJcMnq-rV?O-~RWYMy)V*XbLy*wk7z7R^9| zypu)OpMlCK9K**0EaNU*U?*kSVF#FT#>w@&@mxLr_BTk3yHO4Z0cH|wd(W;Q_GP3S zfxaQxXq} z-Gduff}Pp}o&A8A*}r=%$D7wQ+%emDMh1KP*ejtK9eNEY=cT5n{zL{ofMvQJD^by& zRJZxZu??)-!SMcC-7xVo{uiOmW6aVBV;mC@=W~hU46^>73MS^jy@ye@!xc4EPY2hQ zk2mh{`4ym%c`od~_=|(81`8|Hfm_;RI{`Bx_C8vM^P*LTGr|kU~S$#o8+N~3a06jU6u2+@#C5mSL@_r0}#|G)SCeE;mV*FNj)wa-55e7&*40F z9?X)gOxpA?bFcAA^uY4Z&vu_0t%N3F62lsy)2xjE7&<*)dsK~g@uwQ99p)z$E%8P2KA4C{uR* z*z1>14E$j@@!k2s5qC{bYzZidC!Ypm+U8QZtULbJ2zw|77#GE#-`@H50B_9C5C?UX z5*>;vf!zh5NgZLNeo@GFVgRHF)>Z`pwdYjx(pZ}6kAHvTeOYU)FU%BG+QjT?*%k?r2>Zc{eR&9v>%1wR8G`+*SZ?s zvZ`beZ$B_3DO9|U(w_)tAwV~?C)43jb0BxIH|KExlTNTMJZ4u;h$S=`q9l|W@_xe_ zB{!*3_{0M9A~}0LhMZy@*d3sd&*TEAcBbUDyQxHGP`w`Pf@)_v#WMmvBX%wBxfQ0c zww+%d6O{c40i%B+B|VMsr%a>~8|A3slJj}Cmf@_#1nS^Cmm-|emW1IrSVdXIj>IJv z#MW9FsNmXAr;k{o&|q>aliNkAAZ1_UE~xyzalJz7DGcR-HcK>!I#hJP5MjW6fAJoa zf7~2T%3HWB`0bqd2OW)>zM(V~dRJejGN`2C;! z=;@!xX>U?M+eOg>^RK9z;Vsq!3BZtm<^sO`Imd+x2YhkeGk!@e;M zQ8JRyBC5Ztd{Fsh+xX;%`&x(#76xpaN2bkfUyC>bjSpmwvvux53HZu6<9E`Q*iL|zB`J8I*W>Kzw%OYXrLHz*ubXN2l2 zxkb<-<1cwxB6KVnRT_Uc^~z|npCqdK``Ga%M(KIAfB;Njw`jgJk|P#foG zuHI+2sd~%IKb!T{*ZenXA8{=kHU{q$SxVNV-5b_4C`neU5T2RlZlS#mv;G!svS=Bl zegu0=Jqx!lS9bT#H;X(N5hR`<`K+=}0dY~0C;Aofo2W>{rWb>9TO zz48TeZhAL$W_!396v5_3tND|ay6%WKX6W+>&*mw~W#ezN?Ec^h^JL3TyPUjJv6RzE zJA4y!e4mJJ6eqw;KXKop3%>Yg#4w6<)Jz~&XYj!ekLx4PtmwS|z0Lk7Z?AJE6>~dR zi_y(&|9fP$$nYA%ntZJUy8rKqGy&502a3AzD}SjXByilvc~jD};8FPtP-%sP6VMjhkPbn!l52VuT6VDt~8%i{FgiES2k=S15n5Sll% za;aAu2>xdo77?wPRPJV8bQ)P+z$WJMd+(QeR*_1hKgi8p75bNp(@{cJch2YdmEJyL zx~CIRn16vCnjj}ep3r%PoSLcm>x-v@klhY8P&@m+DnHG}`)po^IqAO=*mG4<3 z+krq){bT?hD3j7|IuODV;Nl;xIMEU~h>1=4Sk2xQ?;)4Zgq)BT@c2ZJ6@O;)&Kevj ztkDWX_EQb+cfJ0y=G=MYy+qQxb20v?ibanJaU9*%%=Jf}%wndO^t3`oH;-|oGIO9T zp#{SY;|z5ASeU>GW26jh4|i_r!@!2vj8NIa`pzGPOyV|^n;jin{Fbi|v5Ntqv$wo( zqAyiFeBww<0W`z3zeNE2^bL3eVRG-x@u973l;6>d&VZH|X! zM(F6WhepUbv|=Vx!#?JL6*_(v2V=ueV2)TXydcKfLl7e$x#GmQ5bOMjEY(a;{mc5C zJv0{$f&Zg&NXVHkwJKR>*Op*eT1(=sS;6GHH@#k)m-oEb_^(!Iqpn8Z z(N(_a>~j0qHuOH)ndHCWXdF>$UBXD>8fGI|fk&jQ(^foU(^XowXZ=gyY>-FeDx1B82m{7YAy=B!T8|up zTh$UY-e)@pN(aEw{YX}BJp_Mk2PBiEL%QkAxFo%HlcV9!jXHy`2E&uNlm>jc~Mb>0ZR*S?~(9yV;p+L#pZ1Q*CMuUC&# zTGSg}`Mlkri23+-au%uaz@_ovLxs=8bs+U7k8n{x-K@-0dgt-(cHUeEVh_0eIHND` zEiqw1NB33UUcm6gR^5NoH>?m4*R+uXM5cyjt-@F~aky^b;W=_|(ZG zCd6KX;SWRr`s0V!A3~28|L_Cu*XfR5=xUG+iU?Q$FD6EqV-5LtF^RvF)0l6UBRC#OXXg;Z|DU z<#j352-K{7Rb-^OwvOHInF@&9N%#eSvHvZ#%R7G+SdeTLd3LUYVgm$Fsus=bAR*Y= zBT~%Tx=gc%SCPuCEn4w9tH=9CGT<7AnwohN$ literal 0 HcmV?d00001 diff --git a/ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py b/ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py new file mode 100644 index 0000000000000..6700c42ec4a94 --- /dev/null +++ b/ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py @@ -0,0 +1,365 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +import colorsys +import shutil +import struct +from pathlib import Path + +import pytest +from PIL import Image + + +def _read_image(path: Path) -> tuple[int, int, bytes]: + """Read an image file and return (width, height, RGB bytes).""" + with Image.open(path) as img: + rgb = img.convert("RGB") + return rgb.width, rgb.height, rgb.tobytes() + + +def _read_mask(golden_path: Path) -> bytes | None: + """Load the companion mask PNG for a golden image, if it exists. + + Non-zero pixels are masked (skipped during comparison). + """ + mask_path = golden_path.with_suffix(".mask.png") + if not mask_path.exists(): + return None + with Image.open(mask_path) as img: + return img.convert("L").tobytes() + + +def _assert_pixels_equal_masked( + golden_pixels: bytes, actual_pixels: bytes, mask: bytes | None = None +): + """Assert pixels match, skipping positions where the mask is non-zero.""" + assert len(golden_pixels) == len(actual_pixels) + for i in range(0, len(golden_pixels), 3): + if mask is not None and mask[i // 3] != 0: + continue + if golden_pixels[i : i + 3] != actual_pixels[i : i + 3]: + pixel = i // 3 + assert False, ( + f"Pixel {pixel} mismatch: " + f"golden={tuple(golden_pixels[i : i + 3])} " + f"actual={tuple(actual_pixels[i : i + 3])}" + ) + + +BOARD_DISPLAY_AVAILABLE_CODE = """\ +import board +print(hasattr(board, 'DISPLAY')) +print(type(board.DISPLAY).__name__) +print(board.DISPLAY.width, board.DISPLAY.height) +print('done') +""" + + +@pytest.mark.circuitpy_drive({"code.py": BOARD_DISPLAY_AVAILABLE_CODE}) +@pytest.mark.display +@pytest.mark.duration(8) +def test_board_display_available(circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "True" in output + assert "Display" in output + assert "320 240" in output + assert "done" in output + + +CONSOLE_TERMINAL_PRESENT_CODE = """\ +import board + +root = board.DISPLAY.root_group +has_terminal_tilegrids = ( + type(root).__name__ == 'Group' and + len(root) >= 2 and + type(root[0]).__name__ == 'TileGrid' and + type(root[-1]).__name__ == 'TileGrid' +) +print('has_terminal_tilegrids:', has_terminal_tilegrids) +print('done') +""" + + +@pytest.mark.circuitpy_drive({"code.py": CONSOLE_TERMINAL_PRESENT_CODE}) +@pytest.mark.display +@pytest.mark.duration(8) +def test_console_terminal_present_by_default(circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "has_terminal_tilegrids: True" in output + assert "done" in output + + +CONSOLE_OUTPUT_GOLDEN_CODE = """\ +import time +time.sleep(0.25) +print('done') +while True: + time.sleep(1) +""" + + +def _golden_compare_or_update(request, captures, golden_path, mask_path=None): + """Compare captured PNG against golden, or update golden if --update-goldens. + + mask_path overrides the default companion mask lookup (golden.mask.png). + """ + if not captures or not captures[0].exists(): + pytest.skip("display capture was not produced") + + if request.config.getoption("--update-goldens"): + golden_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(captures[0], golden_path) + return + + gw, gh, gpx = _read_image(golden_path) + dw, dh, dpx = _read_image(captures[0]) + if mask_path is not None: + mask = _read_mask(mask_path) + else: + mask = _read_mask(golden_path) + + assert (dw, dh) == (gw, gh) + _assert_pixels_equal_masked(gpx, dpx, mask) + + +@pytest.mark.circuitpy_drive({"code.py": CONSOLE_OUTPUT_GOLDEN_CODE}) +@pytest.mark.display(capture_times_ns=[4_000_000_000]) +@pytest.mark.duration(8) +def test_console_output_golden(request, circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "done" in output + + golden = Path(__file__).parent / "golden" / "terminal_console_output_320x240.png" + _golden_compare_or_update(request, circuitpython.display_capture_paths(), golden) + + +PIXEL_FORMATS = ["ARGB_8888", "RGB_888", "RGB_565", "BGR_565", "L_8", "AL_88", "MONO01", "MONO10"] + +# Shared mask: the same screen regions vary regardless of pixel format. +_CONSOLE_GOLDEN_MASK = Path(__file__).parent / "golden" / "terminal_console_output_320x240.png" + + +@pytest.mark.circuitpy_drive({"code.py": CONSOLE_OUTPUT_GOLDEN_CODE}) +@pytest.mark.display(capture_times_ns=[4_000_000_000]) +@pytest.mark.duration(8) +@pytest.mark.parametrize( + "pixel_format", + PIXEL_FORMATS, + indirect=True, +) +def test_console_output_golden_pixel_format(pixel_format, request, circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "done" in output + + golden_name = f"terminal_console_output_320x240_{pixel_format}.png" + golden = Path(__file__).parent / "golden" / golden_name + _golden_compare_or_update( + request, circuitpython.display_capture_paths(), golden, _CONSOLE_GOLDEN_MASK + ) + + +MONO_NO_VTILED_FORMATS = ["MONO01", "MONO10"] + + +@pytest.mark.circuitpy_drive({"code.py": CONSOLE_OUTPUT_GOLDEN_CODE}) +@pytest.mark.display(capture_times_ns=[4_000_000_000]) +@pytest.mark.display_mono_vtiled(False) +@pytest.mark.duration(8) +@pytest.mark.parametrize( + "pixel_format", + MONO_NO_VTILED_FORMATS, + indirect=True, +) +def test_console_output_golden_mono_no_vtiled(pixel_format, request, circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "done" in output + + golden_name = f"terminal_console_output_320x240_{pixel_format}_no_vtiled.png" + golden = Path(__file__).parent / "golden" / golden_name + _golden_compare_or_update( + request, circuitpython.display_capture_paths(), golden, _CONSOLE_GOLDEN_MASK + ) + + +def _generate_gradient_bmp(width, height): + """Generate a 24-bit BMP with HSL color gradient. + + Hue sweeps left to right, lightness goes from black (bottom) to white (top), + saturation is 1.0. + """ + row_size = width * 3 + row_padding = (4 - (row_size % 4)) % 4 + padded_row_size = row_size + row_padding + pixel_data_size = padded_row_size * height + file_size = 14 + 40 + pixel_data_size + + header = struct.pack( + "<2sIHHI", + b"BM", + file_size, + 0, + 0, + 14 + 40, + ) + info = struct.pack( + " Date: Sat, 21 Mar 2026 10:13:02 -0700 Subject: [PATCH 431/517] mtm_computer: Add DAC audio out module --- .../boards/mtm_computer/module/DACOut.c | 276 ++++++++++++++++++ .../boards/mtm_computer/module/DACOut.h | 38 +++ .../boards/mtm_computer/mpconfigboard.mk | 4 + 3 files changed, 318 insertions(+) create mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.c create mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.h diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c new file mode 100644 index 0000000000000..84f4296cb00cd --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c @@ -0,0 +1,276 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT +// +// MCP4822 dual-channel 12-bit SPI DAC driver for the MTM Workshop Computer. +// Uses PIO + DMA for non-blocking audio playback, mirroring audiobusio.I2SOut. + +#include +#include + +#include "mpconfigport.h" + +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "boards/mtm_computer/module/DACOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/audiocore/__init__.h" +#include "bindings/rp2pio/StateMachine.h" + +// ───────────────────────────────────────────────────────────────────────────── +// PIO program for MCP4822 SPI DAC +// ───────────────────────────────────────────────────────────────────────────── +// +// Pin assignment: +// OUT pin (1) = MOSI — serial data out +// SET pins (N) = MOSI through CS — for CS control & command-bit injection +// SIDE-SET pin (1) = SCK — serial clock +// +// On the MTM Workshop Computer: MOSI=GP19, CS=GP21, SCK=GP18. +// The SET group spans GP19..GP21 (3 pins). GP20 is unused and driven low. +// +// SET PINS bit mapping (bit0=MOSI/GP19, bit1=GP20, bit2=CS/GP21): +// 0 = CS low, MOSI low 1 = CS low, MOSI high 4 = CS high, MOSI low +// +// SIDE-SET (1 pin, SCK): side 0 = SCK low, side 1 = SCK high +// +// MCP4822 16-bit command word: +// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x) +// [12] output enable (1) [11:0] 12-bit data +// +// DMA feeds unsigned 16-bit audio samples. RP2040 narrow-write replication +// fills both halves of the 32-bit PIO FIFO entry with the same value, +// giving mono→stereo for free. +// +// The PIO pulls 32 bits, then sends two SPI transactions: +// Channel A: cmd nibble 0b0011, then all 16 sample bits from upper half-word +// Channel B: cmd nibble 0b1011, then all 16 sample bits from lower half-word +// The MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data), +// so only the top 12 of the 16 sample bits become DAC data. The bottom +// 4 sample bits clock out harmlessly after the DAC has latched. +// This gives correct 16-bit → 12-bit scaling (effectively sample >> 4). +// +// PIO instruction encoding with .side_set 1 (no opt): +// [15:13] opcode [12] side-set [11:8] delay [7:0] operands +// +// Total: 26 instructions, 86 PIO clocks per audio sample. +// ───────────────────────────────────────────────────────────────────────────── + +static const uint16_t mcp4822_pio_program[] = { + // side SCK + // 0: pull noblock side 0 ; Get 32 bits or keep X if FIFO empty + 0x8080, + // 1: mov x, osr side 0 ; Save for pull-noblock fallback + 0xA027, + + // ── Channel A: command nibble 0b0011 ────────────────────────────────── + // Send 4 cmd bits via SET, then all 16 sample bits via OUT. + // MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data); + // the extra 4 clocks shift out the LSBs which the DAC ignores. + // This gives correct 16→12 bit scaling (top 12 bits become DAC data). + // 2: set pins, 0 side 0 ; CS low, MOSI=0 (bit15=0: channel A) + 0xE000, + // 3: nop side 1 ; SCK high — latch bit 15 + 0xB042, + // 4: set pins, 0 side 0 ; MOSI=0 (bit14=0: don't care) + 0xE000, + // 5: nop side 1 ; SCK high + 0xB042, + // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + 0xE001, + // 7: nop side 1 ; SCK high + 0xB042, + // 8: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) + 0xE001, + // 9: nop side 1 ; SCK high + 0xB042, + // 10: set y, 15 side 0 ; Loop counter: 16 sample bits + 0xE04F, + // 11: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopA) + 0x6001, + // 12: jmp y--, 11 side 1 ; SCK high, loop back + 0x108B, + // 13: set pins, 4 side 0 ; CS high — DAC A latches + 0xE004, + + // ── Channel B: command nibble 0b1011 ────────────────────────────────── + // 14: set pins, 1 side 0 ; CS low, MOSI=1 (bit15=1: channel B) + 0xE001, + // 15: nop side 1 ; SCK high + 0xB042, + // 16: set pins, 0 side 0 ; MOSI=0 (bit14=0) + 0xE000, + // 17: nop side 1 ; SCK high + 0xB042, + // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + 0xE001, + // 19: nop side 1 ; SCK high + 0xB042, + // 20: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) + 0xE001, + // 21: nop side 1 ; SCK high + 0xB042, + // 22: set y, 15 side 0 ; Loop counter: 16 sample bits + 0xE04F, + // 23: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopB) + 0x6001, + // 24: jmp y--, 23 side 1 ; SCK high, loop back + 0x1097, + // 25: set pins, 4 side 0 ; CS high — DAC B latches + 0xE004, +}; + +// Clocks per sample: 2 (pull+mov) + 42 (chanA) + 42 (chanB) = 86 +// Per channel: 8(4 cmd bits × 2 clks) + 1(set y) + 32(16 bits × 2 clks) + 1(cs high) = 42 +#define MCP4822_CLOCKS_PER_SAMPLE 86 + + +void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs) { + + // SET pins span from MOSI to CS. MOSI must have a lower GPIO number + // than CS, with at most 4 pins between them (SET count max is 5). + if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { + mp_raise_ValueError( + MP_COMPRESSED_ROM_TEXT("cs pin must be 1-4 positions above mosi pin")); + } + + uint8_t set_count = cs->number - mosi->number + 1; + + // Initial SET pin state: CS high (bit at CS position), others low + uint32_t cs_bit_position = cs->number - mosi->number; + pio_pinmask32_t initial_set_state = PIO_PINMASK32_FROM_VALUE(1u << cs_bit_position); + pio_pinmask32_t initial_set_dir = PIO_PINMASK32_FROM_VALUE((1u << set_count) - 1); + + common_hal_rp2pio_statemachine_construct( + &self->state_machine, + mcp4822_pio_program, MP_ARRAY_SIZE(mcp4822_pio_program), + 44100 * MCP4822_CLOCKS_PER_SAMPLE, // Initial frequency; play() adjusts + NULL, 0, // No init program + NULL, 0, // No may_exec + mosi, 1, // OUT: MOSI, 1 pin + PIO_PINMASK32_NONE, PIO_PINMASK32_ALL, // OUT state=low, dir=output + NULL, 0, // IN: none + PIO_PINMASK32_NONE, PIO_PINMASK32_NONE, // IN pulls: none + mosi, set_count, // SET: MOSI..CS + initial_set_state, initial_set_dir, // SET state (CS high), dir=output + clock, 1, false, // SIDE-SET: SCK, 1 pin, not pindirs + PIO_PINMASK32_NONE, // SIDE-SET state: SCK low + PIO_PINMASK32_FROM_VALUE(0x1), // SIDE-SET dir: output + false, // No sideset enable + NULL, PULL_NONE, // No jump pin + PIO_PINMASK_NONE, // No wait GPIO + true, // Exclusive pin use + false, 32, false, // OUT shift: no autopull, 32-bit, shift left + false, // Don't wait for txstall + false, 32, false, // IN shift (unused) + false, // Not user-interruptible + 0, -1, // Wrap: whole program + PIO_ANY_OFFSET, + PIO_FIFO_TYPE_DEFAULT, + PIO_MOV_STATUS_DEFAULT, + PIO_MOV_N_DEFAULT + ); + + self->playing = false; + audio_dma_init(&self->dma); +} + +bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self) { + return common_hal_rp2pio_statemachine_deinited(&self->state_machine); +} + +void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self) { + if (common_hal_mtm_hardware_dacout_deinited(self)) { + return; + } + if (common_hal_mtm_hardware_dacout_get_playing(self)) { + common_hal_mtm_hardware_dacout_stop(self); + } + common_hal_rp2pio_statemachine_deinit(&self->state_machine); + audio_dma_deinit(&self->dma); +} + +void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, + mp_obj_t sample, bool loop) { + + if (common_hal_mtm_hardware_dacout_get_playing(self)) { + common_hal_mtm_hardware_dacout_stop(self); + } + + uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); + if (bits_per_sample < 16) { + bits_per_sample = 16; + } + + uint32_t sample_rate = audiosample_get_sample_rate(sample); + uint8_t channel_count = audiosample_get_channel_count(sample); + if (channel_count > 2) { + mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("Too many channels in sample.")); + } + + // PIO clock = sample_rate × clocks_per_sample + common_hal_rp2pio_statemachine_set_frequency( + &self->state_machine, + (uint32_t)sample_rate * MCP4822_CLOCKS_PER_SAMPLE); + common_hal_rp2pio_statemachine_restart(&self->state_machine); + + // DMA feeds unsigned 16-bit samples. The PIO discards the top 4 bits + // of each 16-bit half and uses the remaining 12 as DAC data. + // RP2040 narrow-write replication: 16-bit DMA write → same value in + // both 32-bit FIFO halves → mono-to-stereo for free. + audio_dma_result result = audio_dma_setup_playback( + &self->dma, + sample, + loop, + false, // single_channel_output + 0, // audio_channel + false, // output_signed = false (unsigned for MCP4822) + bits_per_sample, // output_resolution + (uint32_t)&self->state_machine.pio->txf[self->state_machine.state_machine], + self->state_machine.tx_dreq, + false); // swap_channel + + if (result == AUDIO_DMA_DMA_BUSY) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("No DMA channel found")); + } else if (result == AUDIO_DMA_MEMORY_ERROR) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Unable to allocate buffers for signed conversion")); + } else if (result == AUDIO_DMA_SOURCE_ERROR) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Audio source error")); + } + + self->playing = true; +} + +void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self) { + audio_dma_pause(&self->dma); +} + +void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self) { + audio_dma_resume(&self->dma); +} + +bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self) { + return audio_dma_get_paused(&self->dma); +} + +void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self) { + audio_dma_stop(&self->dma); + common_hal_rp2pio_statemachine_stop(&self->state_machine); + self->playing = false; +} + +bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self) { + bool playing = audio_dma_get_playing(&self->dma); + if (!playing && self->playing) { + common_hal_mtm_hardware_dacout_stop(self); + } + return playing; +} diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h new file mode 100644 index 0000000000000..f7c0c9eb8062c --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h @@ -0,0 +1,38 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/rp2pio/StateMachine.h" + +#include "audio_dma.h" +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + rp2pio_statemachine_obj_t state_machine; + audio_dma_t dma; + bool playing; +} mtm_hardware_dacout_obj_t; + +void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs); + +void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); + +void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, + mp_obj_t sample, bool loop); +void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self); + +void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self); +void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self); + +extern const mp_obj_type_t mtm_hardware_dacout_type; diff --git a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk index 718c393d1686f..74d9baac879b4 100644 --- a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk +++ b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk @@ -11,3 +11,7 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY_AUDIOEFFECTS = 1 CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_PICODVI = 0 + +SRC_C += \ + boards/$(BOARD)/module/mtm_hardware.c \ + boards/$(BOARD)/module/DACOut.c From 38b023338d43c8884d6abe568c0aa6b5e2d05ef4 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Sat, 21 Mar 2026 10:33:40 -0700 Subject: [PATCH 432/517] mtm_hardware.c added --- .../boards/mtm_computer/module/mtm_hardware.c | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c new file mode 100644 index 0000000000000..a3dafbcf9415a --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c @@ -0,0 +1,267 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT +// +// Python bindings for the mtm_hardware module. +// Provides DACOut: a non-blocking audio player for the MCP4822 SPI DAC. + +#include + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "boards/mtm_computer/module/DACOut.h" + +// ───────────────────────────────────────────────────────────────────────────── +// DACOut class +// ───────────────────────────────────────────────────────────────────────────── + +//| class DACOut: +//| """Output audio to the MCP4822 dual-channel 12-bit SPI DAC.""" +//| +//| def __init__( +//| self, +//| clock: microcontroller.Pin, +//| mosi: microcontroller.Pin, +//| cs: microcontroller.Pin, +//| ) -> None: +//| """Create a DACOut object associated with the given SPI pins. +//| +//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin +//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin +//| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| +//| Simple 8ksps 440 Hz sine wave:: +//| +//| import mtm_hardware +//| import audiocore +//| import board +//| import array +//| import time +//| import math +//| +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import mtm_hardware +//| +//| f = open("sound.wav", "rb") +//| wav = audiocore.WaveFile(f) +//| +//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(wav) +//| while dac.playing: +//| pass""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_clock, ARG_mosi, ARG_cs }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + + mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); + common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs); + + return MP_OBJ_FROM_PTR(self); +} + +static void check_for_deinit(mtm_hardware_dacout_obj_t *self) { + if (common_hal_mtm_hardware_dacout_deinited(self)) { + raise_deinited_error(); + } +} + +//| def deinit(self) -> None: +//| """Deinitialises the DACOut and releases any hardware resources for reuse.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_deinit(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_mtm_hardware_dacout_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_deinit_obj, mtm_hardware_dacout_deinit); + +//| def __enter__(self) -> DACOut: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +// Provided by context manager helper. + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 8 bit or 16 bit samples.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_mtm_hardware_dacout_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mtm_hardware_dacout_play_obj, 1, mtm_hardware_dacout_obj_play); + +//| def stop(self) -> None: +//| """Stops playback.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_stop(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_mtm_hardware_dacout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_stop_obj, mtm_hardware_dacout_obj_stop); + +//| playing: bool +//| """True when the audio sample is being output. (read-only)""" +//| +static mp_obj_t mtm_hardware_dacout_obj_get_playing(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_playing_obj, mtm_hardware_dacout_obj_get_playing); + +MP_PROPERTY_GETTER(mtm_hardware_dacout_playing_obj, + (mp_obj_t)&mtm_hardware_dacout_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_pause(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (!common_hal_mtm_hardware_dacout_get_playing(self)) { + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Not playing")); + } + common_hal_mtm_hardware_dacout_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_pause_obj, mtm_hardware_dacout_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_resume(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_mtm_hardware_dacout_get_paused(self)) { + common_hal_mtm_hardware_dacout_resume(self); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_resume_obj, mtm_hardware_dacout_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +static mp_obj_t mtm_hardware_dacout_obj_get_paused(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_paused_obj, mtm_hardware_dacout_obj_get_paused); + +MP_PROPERTY_GETTER(mtm_hardware_dacout_paused_obj, + (mp_obj_t)&mtm_hardware_dacout_get_paused_obj); + +// ── DACOut type definition ─────────────────────────────────────────────────── + +static const mp_rom_map_elem_t mtm_hardware_dacout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mtm_hardware_dacout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mtm_hardware_dacout_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mtm_hardware_dacout_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mtm_hardware_dacout_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mtm_hardware_dacout_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mtm_hardware_dacout_paused_obj) }, +}; +static MP_DEFINE_CONST_DICT(mtm_hardware_dacout_locals_dict, mtm_hardware_dacout_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mtm_hardware_dacout_type, + MP_QSTR_DACOut, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, mtm_hardware_dacout_make_new, + locals_dict, &mtm_hardware_dacout_locals_dict + ); + +// ───────────────────────────────────────────────────────────────────────────── +// mtm_hardware module definition +// ───────────────────────────────────────────────────────────────────────────── + +//| """Hardware interface to Music Thing Modular Workshop Computer peripherals. +//| +//| Provides the `DACOut` class for non-blocking audio output via the +//| MCP4822 dual-channel 12-bit SPI DAC. +//| """ + +static const mp_rom_map_elem_t mtm_hardware_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mtm_hardware) }, + { MP_ROM_QSTR(MP_QSTR_DACOut), MP_ROM_PTR(&mtm_hardware_dacout_type) }, +}; + +static MP_DEFINE_CONST_DICT(mtm_hardware_module_globals, mtm_hardware_module_globals_table); + +const mp_obj_module_t mtm_hardware_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mtm_hardware_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mtm_hardware, mtm_hardware_module); From a5f9f3b663fa4169c9ef4280aaff7b7f1e648494 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 12:58:15 -0700 Subject: [PATCH 433/517] mtm_hardware.dacout: add gain=1 or gain=2 argument --- .../boards/mtm_computer/module/DACOut.c | 21 +++++++++++++++++-- .../boards/mtm_computer/module/DACOut.h | 2 +- .../boards/mtm_computer/module/mtm_hardware.c | 13 ++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c index 84f4296cb00cd..fb4ce37d4d0ff 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c @@ -128,9 +128,19 @@ static const uint16_t mcp4822_pio_program[] = { #define MCP4822_CLOCKS_PER_SAMPLE 86 +// MCP4822 gain bit (bit 13) position in the PIO program: +// Instruction 6 = channel A gain bit +// Instruction 18 = channel B gain bit +// 1x gain: set pins, 1 (0xE001) — bit 13 = 1 +// 2x gain: set pins, 0 (0xE000) — bit 13 = 0 +#define MCP4822_PIO_GAIN_INSTR_A 6 +#define MCP4822_PIO_GAIN_INSTR_B 18 +#define MCP4822_PIO_GAIN_1X 0xE001 // set pins, 1 +#define MCP4822_PIO_GAIN_2X 0xE000 // set pins, 0 + void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs) { + const mcu_pin_obj_t *cs, uint8_t gain) { // SET pins span from MOSI to CS. MOSI must have a lower GPIO number // than CS, with at most 4 pins between them (SET count max is 5). @@ -141,6 +151,13 @@ void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, uint8_t set_count = cs->number - mosi->number + 1; + // Build a mutable copy of the PIO program and patch the gain bit + uint16_t program[MP_ARRAY_SIZE(mcp4822_pio_program)]; + memcpy(program, mcp4822_pio_program, sizeof(mcp4822_pio_program)); + uint16_t gain_instr = (gain == 2) ? MCP4822_PIO_GAIN_2X : MCP4822_PIO_GAIN_1X; + program[MCP4822_PIO_GAIN_INSTR_A] = gain_instr; + program[MCP4822_PIO_GAIN_INSTR_B] = gain_instr; + // Initial SET pin state: CS high (bit at CS position), others low uint32_t cs_bit_position = cs->number - mosi->number; pio_pinmask32_t initial_set_state = PIO_PINMASK32_FROM_VALUE(1u << cs_bit_position); @@ -148,7 +165,7 @@ void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, common_hal_rp2pio_statemachine_construct( &self->state_machine, - mcp4822_pio_program, MP_ARRAY_SIZE(mcp4822_pio_program), + program, MP_ARRAY_SIZE(mcp4822_pio_program), 44100 * MCP4822_CLOCKS_PER_SAMPLE, // Initial frequency; play() adjusts NULL, 0, // No init program NULL, 0, // No may_exec diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h index f7c0c9eb8062c..f9b5dd60108cf 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h @@ -21,7 +21,7 @@ typedef struct { void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs); + const mcu_pin_obj_t *cs, uint8_t gain); void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c index a3dafbcf9415a..8f875496f6745 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c +++ b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c @@ -29,12 +29,15 @@ //| clock: microcontroller.Pin, //| mosi: microcontroller.Pin, //| cs: microcontroller.Pin, +//| *, +//| gain: int = 1, //| ) -> None: //| """Create a DACOut object associated with the given SPI pins. //| //| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin //| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin //| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. //| //| Simple 8ksps 440 Hz sine wave:: //| @@ -72,11 +75,12 @@ //| ... //| static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_clock, ARG_mosi, ARG_cs }; + enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -85,8 +89,13 @@ static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + mp_int_t gain = args[ARG_gain].u_int; + if (gain != 1 && gain != 2) { + mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("gain must be 1 or 2")); + } + mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); - common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs); + common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs, (uint8_t)gain); return MP_OBJ_FROM_PTR(self); } From 02675cbe1c009a07f4d71d1536db8c85dab5e4a9 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 16:42:55 -0700 Subject: [PATCH 434/517] rework mcp4822 module from mtm_hardware.DACOut --- locale/circuitpython.pot | 4579 ----------------- ports/raspberrypi/boards/mtm_computer/board.c | 19 + .../boards/mtm_computer/module/DACOut.h | 38 - .../boards/mtm_computer/module/mtm_hardware.c | 276 - .../boards/mtm_computer/mpconfigboard.mk | 5 +- ports/raspberrypi/boards/mtm_computer/pins.c | 6 +- .../DACOut.c => common-hal/mcp4822/MCP4822.c} | 90 +- .../raspberrypi/common-hal/mcp4822/MCP4822.h | 22 + .../raspberrypi/common-hal/mcp4822/__init__.c | 7 + ports/raspberrypi/mpconfigport.mk | 1 + py/circuitpy_defns.mk | 5 + shared-bindings/mcp4822/MCP4822.c | 247 + shared-bindings/mcp4822/MCP4822.h | 25 + shared-bindings/mcp4822/__init__.c | 36 + shared-bindings/mcp4822/__init__.h | 7 + 15 files changed, 417 insertions(+), 4946 deletions(-) delete mode 100644 locale/circuitpython.pot delete mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.h delete mode 100644 ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c rename ports/raspberrypi/{boards/mtm_computer/module/DACOut.c => common-hal/mcp4822/MCP4822.c} (75%) create mode 100644 ports/raspberrypi/common-hal/mcp4822/MCP4822.h create mode 100644 ports/raspberrypi/common-hal/mcp4822/__init__.c create mode 100644 shared-bindings/mcp4822/MCP4822.c create mode 100644 shared-bindings/mcp4822/MCP4822.h create mode 100644 shared-bindings/mcp4822/__init__.c create mode 100644 shared-bindings/mcp4822/__init__.h diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot deleted file mode 100644 index bf4c5d110f673..0000000000000 --- a/locale/circuitpython.pot +++ /dev/null @@ -1,4579 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: main.c -msgid "" -"\n" -"Code done running.\n" -msgstr "" - -#: main.c -msgid "" -"\n" -"Code stopped by auto-reload. Reloading soon.\n" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "" -"\n" -"Please file an issue with your program at github.com/adafruit/circuitpython/" -"issues." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "" -"\n" -"Press reset to exit safe mode.\n" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "" -"\n" -"You are in safe mode because:\n" -msgstr "" - -#: py/obj.c -msgid " File \"%q\"" -msgstr "" - -#: py/obj.c -msgid " File \"%q\", line %d" -msgstr "" - -#: py/builtinhelp.c -msgid " is of type %q\n" -msgstr "" - -#: main.c -msgid " not found.\n" -msgstr "" - -#: main.c -msgid " output:\n" -msgstr "" - -#: py/objstr.c -#, c-format -msgid "%%c needs int or char" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "" -"%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" -msgstr "" - -#: shared-bindings/microcontroller/Pin.c -msgid "%q and %q contain duplicate pins" -msgstr "" - -#: shared-bindings/audioio/AudioOut.c -msgid "%q and %q must be different" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -msgid "%q and %q must share a clock unit" -msgstr "" - -#: ports/nordic/common-hal/watchdog/WatchDogTimer.c -msgid "%q cannot be changed once mode is set to %q" -msgstr "" - -#: shared-bindings/microcontroller/Pin.c -msgid "%q contains duplicate pins" -msgstr "" - -#: ports/atmel-samd/common-hal/sdioio/SDCard.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "%q failure: %d" -msgstr "" - -#: shared-module/audiodelays/MultiTapDelay.c -msgid "%q in %q must be of type %q or %q, not %q" -msgstr "" - -#: py/argcheck.c shared-module/audiofilters/Filter.c -msgid "%q in %q must be of type %q, not %q" -msgstr "" - -#: ports/espressif/common-hal/espulp/ULP.c -#: ports/espressif/common-hal/mipidsi/Bus.c -#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c -#: ports/mimxrt10xx/common-hal/usb_host/Port.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -#: ports/raspberrypi/common-hal/usb_host/Port.c -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c -#: shared-module/max3421e/Max3421E.c -msgid "%q in use" -msgstr "" - -#: py/objstr.c -msgid "%q index out of range" -msgstr "" - -#: py/obj.c -msgid "%q indices must be integers, not %s" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "%q init failed" -msgstr "" - -#: ports/espressif/bindings/espnow/Peer.c shared-bindings/dualbank/__init__.c -msgid "%q is %q" -msgstr "" - -#: ports/raspberrypi/common-hal/wifi/Radio.c -msgid "%q is read-only for this board" -msgstr "" - -#: py/argcheck.c shared-bindings/usb_hid/Device.c -msgid "%q length must be %d" -msgstr "" - -#: py/argcheck.c -msgid "%q length must be %d-%d" -msgstr "" - -#: py/argcheck.c -msgid "%q length must be <= %d" -msgstr "" - -#: py/argcheck.c -msgid "%q length must be >= %d" -msgstr "" - -#: py/argcheck.c -msgid "%q must be %d" -msgstr "" - -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -#: shared-bindings/is31fl3741/FrameBuffer.c -#: shared-bindings/rgbmatrix/RGBMatrix.c -msgid "%q must be %d-%d" -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -msgid "%q must be 1 when %q is True" -msgstr "" - -#: py/argcheck.c shared-bindings/gifio/GifWriter.c -#: shared-module/gifio/OnDiskGif.c -msgid "%q must be <= %d" -msgstr "" - -#: ports/espressif/common-hal/watchdog/WatchDogTimer.c -msgid "%q must be <= %u" -msgstr "" - -#: py/argcheck.c -msgid "%q must be >= %d" -msgstr "" - -#: shared-bindings/analogbufio/BufferedIn.c -msgid "%q must be a bytearray or array of type 'H' or 'B'" -msgstr "" - -#: shared-bindings/audiocore/RawSample.c -msgid "%q must be a bytearray or array of type 'h', 'H', 'b', or 'B'" -msgstr "" - -#: shared-bindings/warnings/__init__.c -msgid "%q must be a subclass of %q" -msgstr "" - -#: ports/espressif/common-hal/analogbufio/BufferedIn.c -msgid "%q must be array of type 'H'" -msgstr "" - -#: shared-module/synthio/__init__.c -msgid "%q must be array of type 'h'" -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "%q must be multiple of 8." -msgstr "" - -#: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c -#: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c -msgid "%q must be of type %q or %q, not %q" -msgstr "" - -#: shared-bindings/jpegio/JpegDecoder.c -msgid "%q must be of type %q, %q, or %q, not %q" -msgstr "" - -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "" - -#: ports/atmel-samd/common-hal/busio/UART.c -msgid "%q must be power of 2" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "%q object missing '%q' attribute" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "%q object missing '%q' method" -msgstr "" - -#: shared-bindings/wifi/Monitor.c -msgid "%q out of bounds" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -#: ports/cxd56/common-hal/pulseio/PulseIn.c -#: ports/nordic/common-hal/pulseio/PulseIn.c -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -#: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c -#: shared-bindings/time/__init__.c -msgid "%q out of range" -msgstr "" - -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - -#: py/objrange.c py/objslice.c shared-bindings/random/__init__.c -msgid "%q step cannot be zero" -msgstr "" - -#: shared-module/bitbangio/I2C.c -msgid "%q too long" -msgstr "" - -#: py/bc.c py/objnamedtuple.c -msgid "%q() takes %d positional arguments but %d were given" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "%q() without %q()" -msgstr "" - -#: shared-bindings/usb_hid/Device.c -msgid "%q, %q, and %q must all be the same length" -msgstr "" - -#: py/objint.c shared-bindings/_bleio/Connection.c -#: shared-bindings/storage/__init__.c -msgid "%q=%q" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "%q[%u] shifts in more bits than pin count" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "%q[%u] shifts out more bits than pin count" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "%q[%u] uses extra pin" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "%q[%u] waits on input outside of count" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -#, c-format -msgid "%s error 0x%x" -msgstr "" - -#: py/argcheck.c -msgid "'%q' argument required" -msgstr "" - -#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "'%q' object does not support '%q'" -msgstr "" - -#: py/runtime.c -msgid "'%q' object isn't an iterator" -msgstr "" - -#: py/objtype.c py/runtime.c shared-module/atexit/__init__.c -msgid "'%q' object isn't callable" -msgstr "" - -#: py/runtime.c -msgid "'%q' object isn't iterable" -msgstr "" - -#: py/emitinlinethumb.c py/emitinlinextensa.c -#, c-format -msgid "'%s' expects a label" -msgstr "" - -#: py/emitinlinethumb.c py/emitinlinextensa.c -#, c-format -msgid "'%s' expects a register" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects a special register" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects an FPU register" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects an address of the form [a, b]" -msgstr "" - -#: py/emitinlinethumb.c py/emitinlinextensa.c -#, c-format -msgid "'%s' expects an integer" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects at most r%d" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects {r0, r1, ...}" -msgstr "" - -#: py/emitinlinextensa.c -#, c-format -msgid "'%s' integer %d isn't within range %d..%d" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' integer 0x%x doesn't fit in mask 0x%x" -msgstr "" - -#: py/obj.c -#, c-format -msgid "'%s' object doesn't support item assignment" -msgstr "" - -#: py/obj.c -#, c-format -msgid "'%s' object doesn't support item deletion" -msgstr "" - -#: py/runtime.c -msgid "'%s' object has no attribute '%q'" -msgstr "" - -#: py/obj.c -#, c-format -msgid "'%s' object isn't subscriptable" -msgstr "" - -#: py/objstr.c -msgid "'=' alignment not allowed in string format specifier" -msgstr "" - -#: shared-module/struct/__init__.c -msgid "'S' and 'O' are not supported format types" -msgstr "" - -#: py/compile.c -msgid "'align' requires 1 argument" -msgstr "" - -#: py/compile.c -msgid "'await' outside function" -msgstr "" - -#: py/compile.c -msgid "'break'/'continue' outside loop" -msgstr "" - -#: py/compile.c -msgid "'data' requires at least 2 arguments" -msgstr "" - -#: py/compile.c -msgid "'data' requires integer arguments" -msgstr "" - -#: py/compile.c -msgid "'label' requires 1 argument" -msgstr "" - -#: py/emitnative.c -msgid "'not' not implemented" -msgstr "" - -#: py/compile.c -msgid "'return' outside function" -msgstr "" - -#: py/compile.c -msgid "'yield from' inside async function" -msgstr "" - -#: py/compile.c -msgid "'yield' outside function" -msgstr "" - -#: py/compile.c -msgid "* arg after **" -msgstr "" - -#: py/compile.c -msgid "*x must be assignment target" -msgstr "" - -#: py/obj.c -msgid ", in %q\n" -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -msgid ".show(x) removed. Use .root_group = x" -msgstr "" - -#: py/objcomplex.c -msgid "0.0 to a complex power" -msgstr "" - -#: py/modbuiltins.c -msgid "3-arg pow() not supported" -msgstr "" - -#: ports/raspberrypi/common-hal/wifi/Radio.c -msgid "AP could not be started" -msgstr "" - -#: shared-bindings/ipaddress/IPv4Address.c -#, c-format -msgid "Address must be %d bytes long" -msgstr "" - -#: ports/espressif/common-hal/memorymap/AddressRange.c -#: ports/nordic/common-hal/memorymap/AddressRange.c -#: ports/raspberrypi/common-hal/memorymap/AddressRange.c -msgid "Address range not allowed" -msgstr "" - -#: shared-bindings/memorymap/AddressRange.c -msgid "Address range wraps around" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -msgid "All CAN peripherals are in use" -msgstr "" - -#: ports/espressif/common-hal/busio/I2C.c -#: ports/espressif/common-hal/i2ctarget/I2CTarget.c -#: ports/nordic/common-hal/busio/I2C.c -msgid "All I2C peripherals are in use" -msgstr "" - -#: ports/atmel-samd/common-hal/canio/Listener.c -#: ports/espressif/common-hal/canio/Listener.c -#: ports/stm/common-hal/canio/Listener.c -msgid "All RX FIFOs in use" -msgstr "" - -#: ports/espressif/common-hal/busio/SPI.c ports/nordic/common-hal/busio/SPI.c -msgid "All SPI peripherals are in use" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c -#: ports/nordic/common-hal/busio/UART.c -msgid "All UART peripherals are in use" -msgstr "" - -#: ports/nordic/common-hal/countio/Counter.c -#: ports/nordic/common-hal/pulseio/PulseIn.c -#: ports/nordic/common-hal/rotaryio/IncrementalEncoder.c -msgid "All channels in use" -msgstr "" - -#: ports/raspberrypi/common-hal/usb_host/Port.c -msgid "All dma channels in use" -msgstr "" - -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -msgid "All event channels in use" -msgstr "" - -#: ports/raspberrypi/common-hal/floppyio/__init__.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -#: ports/raspberrypi/common-hal/usb_host/Port.c -msgid "All state machines in use" -msgstr "" - -#: ports/atmel-samd/audio_dma.c -msgid "All sync event channels in use" -msgstr "" - -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c -msgid "All timers for this pin are in use" -msgstr "" - -#: ports/atmel-samd/common-hal/_pew/PewPew.c -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -#: ports/atmel-samd/common-hal/pulseio/PulseOut.c -#: ports/cxd56/common-hal/pulseio/PulseOut.c -#: ports/nordic/common-hal/audiopwmio/PWMAudioOut.c -#: ports/nordic/common-hal/pulseio/PulseIn.c -#: ports/nordic/peripherals/nrf/timers.c -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "All timers in use" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Already advertising." -msgstr "" - -#: ports/atmel-samd/common-hal/canio/Listener.c -msgid "Already have all-matches listener" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -msgid "Already in progress" -msgstr "" - -#: ports/espressif/bindings/espnow/ESPNow.c -#: ports/espressif/common-hal/espulp/ULP.c -#: shared-module/memorymonitor/AllocationAlarm.c -#: shared-module/memorymonitor/AllocationSize.c -msgid "Already running" -msgstr "" - -#: ports/espressif/common-hal/wifi/Radio.c -#: ports/raspberrypi/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "Already scanning for wifi networks" -msgstr "" - -#: supervisor/shared/settings.c -#, c-format -msgid "An error occurred while retrieving '%s':\n" -msgstr "" - -#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c -msgid "Another PWMAudioOut is already active" -msgstr "" - -#: ports/atmel-samd/common-hal/pulseio/PulseOut.c -#: ports/cxd56/common-hal/pulseio/PulseOut.c -msgid "Another send is already active" -msgstr "" - -#: shared-bindings/pulseio/PulseOut.c -msgid "Array must contain halfwords (type 'H')" -msgstr "" - -#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c -#: shared-bindings/nvm/ByteArray.c -msgid "Array values should be single bytes." -msgstr "" - -#: ports/atmel-samd/common-hal/spitarget/SPITarget.c -msgid "Async SPI transfer in progress on this bus, keep awaiting." -msgstr "" - -#: shared-module/memorymonitor/AllocationAlarm.c -#, c-format -msgid "Attempt to allocate %d blocks" -msgstr "" - -#: ports/raspberrypi/audio_dma.c -msgid "Audio conversion not implemented" -msgstr "" - -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "Audio source error" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "AuthMode.OPEN is not used with password" -msgstr "" - -#: shared-bindings/wifi/Radio.c supervisor/shared/web_workflow/web_workflow.c -msgid "Authentication failure" -msgstr "" - -#: main.c -msgid "Auto-reload is off.\n" -msgstr "" - -#: main.c -msgid "" -"Auto-reload is on. Simply save files over USB to run them or enter REPL to " -"disable.\n" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -msgid "Baudrate not supported by peripheral" -msgstr "" - -#: shared-module/busdisplay/BusDisplay.c -#: shared-module/framebufferio/FramebufferDisplay.c -msgid "Below minimum frame rate" -msgstr "" - -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -msgid "Bit clock and word select must be sequential GPIO pins" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "Bitmap size and bits per value must match" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Boot device must be first (interface #0)." -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -#: ports/mimxrt10xx/common-hal/busio/UART.c -msgid "Both RX and TX required for flow control" -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -msgid "Brightness not adjustable" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "Buffer elements must be 4 bytes long or less" -msgstr "" - -#: shared-bindings/framebufferio/FramebufferDisplay.c -msgid "Buffer is not a bytearray." -msgstr "" - -#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c -#, c-format -msgid "Buffer length %d too big. It must be less than %d" -msgstr "" - -#: ports/atmel-samd/common-hal/sdioio/SDCard.c -#: ports/cxd56/common-hal/sdioio/SDCard.c -#: ports/espressif/common-hal/sdioio/SDCard.c -#: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c -#, c-format -msgid "Buffer must be a multiple of %d bytes" -msgstr "" - -#: shared-bindings/_bleio/PacketBuffer.c -#, c-format -msgid "Buffer too short by %d bytes" -msgstr "" - -#: ports/cxd56/common-hal/camera/Camera.c -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -#: shared-bindings/struct/__init__.c shared-module/struct/__init__.c -msgid "Buffer too small" -msgstr "" - -#: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/raspberrypi/common-hal/paralleldisplaybus/ParallelBus.c -#, c-format -msgid "Bus pin %d is already in use" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "CBC blocks must be multiples of 16 bytes" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "CIRCUITPY drive could not be found or created." -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "CRC or checksum was invalid" -msgstr "" - -#: py/objtype.c -msgid "Call super().__init__() before accessing native object." -msgstr "" - -#: ports/cxd56/common-hal/camera/Camera.c -msgid "Camera init" -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Can only alarm on RTC IO from deep sleep." -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Can only alarm on one low pin while others alarm high from deep sleep." -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Can only alarm on two low pins from deep sleep." -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Can't construct AudioOut because continuous channel already open" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -msgid "Can't set CCCD on local Characteristic" -msgstr "" - -#: shared-bindings/storage/__init__.c shared-bindings/usb_cdc/__init__.c -#: shared-bindings/usb_hid/__init__.c shared-bindings/usb_midi/__init__.c -#: shared-bindings/usb_video/__init__.c -msgid "Cannot change USB devices now" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Cannot create a new Adapter; use _bleio.adapter;" -msgstr "" - -#: shared-module/i2cioexpander/IOExpander.c -msgid "Cannot deinitialize board IOExpander" -msgstr "" - -#: shared-bindings/displayio/Bitmap.c -#: shared-bindings/memorymonitor/AllocationSize.c -#: shared-bindings/pulseio/PulseIn.c -msgid "Cannot delete values" -msgstr "" - -#: ports/atmel-samd/common-hal/digitalio/DigitalInOut.c -#: ports/mimxrt10xx/common-hal/digitalio/DigitalInOut.c -#: ports/nordic/common-hal/digitalio/DigitalInOut.c -#: ports/raspberrypi/common-hal/digitalio/DigitalInOut.c -msgid "Cannot get pull while in output mode" -msgstr "" - -#: ports/nordic/common-hal/microcontroller/Processor.c -msgid "Cannot get temperature" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Cannot have scan responses for extended, connectable advertisements." -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Cannot pull on input-only pin." -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "Cannot record to a file" -msgstr "" - -#: shared-module/storage/__init__.c -msgid "Cannot remount path when visible via USB." -msgstr "" - -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/i2cioexpander/IOPin.c -msgid "Cannot set value when direction is input." -msgstr "" - -#: ports/espressif/common-hal/busio/UART.c -#: ports/mimxrt10xx/common-hal/busio/UART.c -msgid "Cannot specify RTS or CTS in RS485 mode" -msgstr "" - -#: py/objslice.c -msgid "Cannot subclass slice" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Cannot use GPIO0..15 together with GPIO32..47" -msgstr "" - -#: ports/nordic/common-hal/alarm/pin/PinAlarm.c -msgid "Cannot wake on pin edge, only level" -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Cannot wake on pin edge. Only level." -msgstr "" - -#: shared-bindings/_bleio/CharacteristicBuffer.c -msgid "CharacteristicBuffer writing not provided" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "CircuitPython core code crashed hard. Whoops!\n" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -msgid "Clock unit in use" -msgstr "" - -#: shared-bindings/_bleio/Connection.c -msgid "" -"Connection has been disconnected and can no longer be used. Create a new " -"connection." -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "Coordinate arrays have different lengths" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "Coordinate arrays types have different sizes" -msgstr "" - -#: shared-module/usb/core/Device.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Could not allocate DMA capable buffer" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/Publisher.c -msgid "Could not publish to ROS topic" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Could not set address" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "Could not start interrupt, RX busy" -msgstr "" - -#: shared-module/audiomp3/MP3Decoder.c -msgid "Couldn't allocate decoder" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -#, c-format -msgid "Critical ROS failure during soft reboot, reset required: %d" -msgstr "" - -#: ports/stm/common-hal/analogio/AnalogOut.c -msgid "DAC Channel Init Error" -msgstr "" - -#: ports/stm/common-hal/analogio/AnalogOut.c -msgid "DAC Device Init Error" -msgstr "" - -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -msgid "DAC already in use" -msgstr "" - -#: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c -msgid "Data 0 pin must be byte aligned" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Data format error (may be broken data)" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Data not supported with directed advertising" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Data too large for advertisement packet" -msgstr "" - -#: ports/stm/common-hal/alarm/pin/PinAlarm.c -msgid "Deep sleep pins must use a rising edge with pulldown" -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "Destination capacity is smaller than destination_length." -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Device error or wrong termination of input stream" -msgstr "" - -#: ports/nordic/common-hal/audiobusio/I2SOut.c -msgid "Device in use" -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -msgid "Display must have a 16 bit colorspace." -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -#: shared-bindings/mipidsi/Display.c -msgid "Display rotation must be in 90 degree increments" -msgstr "" - -#: main.c -msgid "Done" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/i2cioexpander/IOPin.c -msgid "Drive mode not used when direction is input." -msgstr "" - -#: py/obj.c -msgid "During handling of the above exception, another exception occurred:" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "ECB only operates on 16 bytes at a time" -msgstr "" - -#: ports/espressif/common-hal/busio/SPI.c -#: ports/espressif/common-hal/canio/CAN.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "ESP-IDF memory allocation failed" -msgstr "" - -#: extmod/modre.c -msgid "Error in regex" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Error in safemode.py." -msgstr "" - -#: shared-bindings/alarm/__init__.c -msgid "Expected a kind of %q" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Extended advertisements with scan response not supported." -msgstr "" - -#: extmod/ulab/code/numpy/fft/fft_tools.c -msgid "FFT is defined for ndarrays only" -msgstr "" - -#: extmod/ulab/code/numpy/fft/fft_tools.c -msgid "FFT is implemented for linear arrays only" -msgstr "" - -#: shared-bindings/ps2io/Ps2.c -msgid "Failed sending command." -msgstr "" - -#: ports/nordic/sd_mutex.c -#, c-format -msgid "Failed to acquire mutex, err 0x%04x" -msgstr "" - -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "Failed to add service TXT record" -msgstr "" - -#: shared-bindings/mdns/Server.c -msgid "" -"Failed to add service TXT record; non-string or bytes found in txt_records" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c -msgid "Failed to allocate %q buffer" -msgstr "" - -#: ports/espressif/common-hal/wifi/__init__.c -msgid "Failed to allocate Wifi memory" -msgstr "" - -#: ports/espressif/common-hal/wifi/ScannedNetworks.c -msgid "Failed to allocate wifi scan memory" -msgstr "" - -#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c -msgid "Failed to buffer the sample" -msgstr "" - -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect: internal error" -msgstr "" - -#: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect: timeout" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to create continuous channels: invalid arg" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to create continuous channels: invalid state" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to create continuous channels: no mem" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to create continuous channels: not found" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to enable continuous" -msgstr "" - -#: shared-module/audiomp3/MP3Decoder.c -msgid "Failed to parse MP3 file" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to register continuous events callback" -msgstr "" - -#: ports/nordic/sd_mutex.c -#, c-format -msgid "Failed to release mutex, err 0x%04x" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -msgid "Failed to set SPI Clock Mode" -msgstr "" - -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "Failed to set hostname" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to start async audio" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Failed to write internal flash." -msgstr "" - -#: py/moderrno.c -msgid "File exists" -msgstr "" - -#: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -msgid "File not found" -msgstr "" - -#: ports/atmel-samd/common-hal/canio/Listener.c -#: ports/espressif/common-hal/canio/Listener.c -#: ports/mimxrt10xx/common-hal/canio/Listener.c -#: ports/stm/common-hal/canio/Listener.c -msgid "Filters too complex" -msgstr "" - -#: ports/espressif/common-hal/dualbank/__init__.c -msgid "Firmware is duplicate" -msgstr "" - -#: ports/espressif/common-hal/dualbank/__init__.c -msgid "Firmware is invalid" -msgstr "" - -#: ports/espressif/common-hal/dualbank/__init__.c -msgid "Firmware is too big" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "For L8 colorspace, input bitmap must have 8 bits per pixel" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "For RGB colorspaces, input bitmap must have 16 bits per pixel" -msgstr "" - -#: ports/cxd56/common-hal/camera/Camera.c shared-module/audiocore/WaveFile.c -msgid "Format not supported" -msgstr "" - -#: ports/mimxrt10xx/common-hal/microcontroller/Processor.c -msgid "" -"Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" -msgstr "" - -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "" - -#: ports/cxd56/common-hal/gnss/GNSS.c -msgid "GNSS init" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Generic Failure" -msgstr "" - -#: shared-bindings/framebufferio/FramebufferDisplay.c -#: shared-module/busdisplay/BusDisplay.c -#: shared-module/framebufferio/FramebufferDisplay.c -msgid "Group already used" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Hard fault: memory access or instruction error." -msgstr "" - -#: ports/mimxrt10xx/common-hal/busio/SPI.c -#: ports/mimxrt10xx/common-hal/busio/UART.c ports/stm/common-hal/busio/I2C.c -#: ports/stm/common-hal/busio/SPI.c ports/stm/common-hal/busio/UART.c -#: ports/stm/common-hal/canio/CAN.c ports/stm/common-hal/sdioio/SDCard.c -msgid "Hardware in use, try alternative pins" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Heap allocation when VM not running." -msgstr "" - -#: extmod/vfs_posix_file.c py/objstringio.c -msgid "I/O operation on closed file" -msgstr "" - -#: ports/stm/common-hal/busio/I2C.c -msgid "I2C init error" -msgstr "" - -#: ports/raspberrypi/common-hal/busio/I2C.c -#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c -msgid "I2C peripheral in use" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "In-buffer elements must be <= 4 bytes long" -msgstr "" - -#: shared-bindings/_pew/PewPew.c -msgid "Incorrect buffer size" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "Init program size invalid" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Initial set pin direction conflicts with initial out pin direction" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Initial set pin state conflicts with initial out pin state" -msgstr "" - -#: shared-bindings/bitops/__init__.c -#, c-format -msgid "Input buffer length (%d) must be a multiple of the strand count (%d)" -msgstr "" - -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -msgid "Input taking too long" -msgstr "" - -#: py/moderrno.c -msgid "Input/output error" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Insufficient authentication" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Insufficient encryption" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Insufficient memory pool for the image" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Insufficient stream input buffer" -msgstr "" - -#: ports/espressif/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "Interface must be started" -msgstr "" - -#: ports/atmel-samd/audio_dma.c ports/raspberrypi/audio_dma.c -msgid "Internal audio buffer too small" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "Internal define error" -msgstr "" - -#: ports/espressif/common-hal/qspibus/QSPIBus.c -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c -msgid "Internal error" -msgstr "" - -#: shared-module/rgbmatrix/RGBMatrix.c -#, c-format -msgid "Internal error #%d" -msgstr "" - -#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c -#: ports/atmel-samd/common-hal/countio/Counter.c -#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c -#: ports/atmel-samd/common-hal/max3421e/Max3421E.c -#: ports/atmel-samd/common-hal/ps2io/Ps2.c -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c -#: ports/cxd56/common-hal/pulseio/PulseIn.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c -#: shared-bindings/pwmio/PWMOut.c -msgid "Internal resource(s) in use" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Internal watchdog timer expired." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Interrupt error." -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Interrupted by output function" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -#: ports/analog/peripherals/max32690/max32_i2c.c -#: ports/analog/peripherals/max32690/max32_spi.c -#: ports/analog/peripherals/max32690/max32_uart.c -#: ports/espressif/common-hal/_bleio/Service.c -#: ports/espressif/common-hal/espulp/ULP.c -#: ports/espressif/common-hal/microcontroller/Processor.c -#: ports/espressif/common-hal/mipidsi/Display.c -#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c -#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c -#: ports/raspberrypi/bindings/picodvi/Framebuffer.c -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c -#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c -#: shared-module/aurora_epaper/aurora_framebuffer.c -#: shared-module/lvfontio/OnDiskFont.c -msgid "Invalid %q" -msgstr "" - -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c -#: shared-module/aurora_epaper/aurora_framebuffer.c -msgid "Invalid %q and %q" -msgstr "" - -#: ports/atmel-samd/common-hal/microcontroller/Pin.c -#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c -#: ports/mimxrt10xx/common-hal/microcontroller/Pin.c -#: shared-bindings/microcontroller/Pin.c -msgid "Invalid %q pin" -msgstr "" - -#: ports/stm/common-hal/analogio/AnalogIn.c -msgid "Invalid ADC Unit value" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Invalid BLE parameter" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "Invalid BSSID" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "Invalid MAC address" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -msgid "Invalid ROS domain ID" -msgstr "" - -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Invalid advertising data" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c -msgid "Invalid argument" -msgstr "" - -#: shared-module/displayio/Bitmap.c -msgid "Invalid bits per value" -msgstr "" - -#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c -#, c-format -msgid "Invalid data_pins[%d]" -msgstr "" - -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "" - -#: shared-module/audiocore/WaveFile.c -msgid "Invalid format chunk size" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "Invalid hex password" -msgstr "" - -#: ports/espressif/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "Invalid multicast MAC address" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Invalid size" -msgstr "" - -#: shared-module/ssl/SSLSocket.c -msgid "Invalid socket for TLS" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -#: ports/espressif/common-hal/espidf/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Invalid state" -msgstr "" - -#: supervisor/shared/settings.c -msgid "Invalid unicode escape" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "Key must be 16, 24, or 32 bytes long" -msgstr "" - -#: shared-module/is31fl3741/FrameBuffer.c -msgid "LED mappings must match display size" -msgstr "" - -#: py/compile.c -msgid "LHS of keyword arg must be an id" -msgstr "" - -#: shared-module/displayio/Group.c -msgid "Layer already in a group" -msgstr "" - -#: shared-module/displayio/Group.c -msgid "Layer must be a Group or TileGrid subclass" -msgstr "" - -#: shared-bindings/audiocore/RawSample.c -msgid "Length of %q must be an even multiple of channel_count * type_size" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "MAC address was invalid" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/espressif/common-hal/_bleio/Descriptor.c -msgid "MITM security not supported" -msgstr "" - -#: ports/stm/common-hal/sdioio/SDCard.c -#, c-format -msgid "MMC/SDIO Clock Error %x" -msgstr "" - -#: shared-bindings/is31fl3741/IS31FL3741.c -msgid "Mapping must be a tuple" -msgstr "" - -#: py/persistentcode.c -msgid "MicroPython .mpy file; use CircuitPython mpy-cross" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Mismatched data size" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Mismatched swap flag" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_in_pin. %q[%u] reads pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_in_pin. %q[%u] shifts in from pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_in_pin. %q[%u] waits based on pin" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_out_pin. %q[%u] shifts out to pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_out_pin. %q[%u] writes pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_set_pin. %q[%u] sets pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing jmp_pin. %q[%u] jumps on pin" -msgstr "" - -#: shared-module/storage/__init__.c -msgid "Mount point directory missing" -msgstr "" - -#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c -msgid "Must be a %q subclass." -msgstr "" - -#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c -msgid "Must provide 5/6/5 RGB pins" -msgstr "" - -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c -msgid "Must provide MISO or MOSI pin" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "Must use a multiple of 6 rgb pins, not %d" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "NLR jump failed. Likely memory corruption." -msgstr "" - -#: ports/espressif/common-hal/nvm/ByteArray.c -msgid "NVS Error" -msgstr "" - -#: shared-bindings/socketpool/SocketPool.c -msgid "Name or service not known" -msgstr "" - -#: shared-bindings/displayio/TileGrid.c -msgid "New bitmap must be same size as old bitmap" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -msgid "Nimble out of memory" -msgstr "" - -#: ports/atmel-samd/common-hal/busio/UART.c -#: ports/espressif/common-hal/busio/SPI.c -#: ports/espressif/common-hal/busio/UART.c -#: ports/mimxrt10xx/common-hal/busio/SPI.c -#: ports/mimxrt10xx/common-hal/busio/UART.c -#: ports/nordic/common-hal/busio/UART.c -#: ports/raspberrypi/common-hal/busio/UART.c ports/stm/common-hal/busio/SPI.c -#: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c -#: shared-bindings/i2cdisplaybus/I2CDisplayBus.c -#: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-bindings/qspibus/QSPIBus.c -#: shared-module/bitbangio/SPI.c -msgid "No %q pin" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -msgid "No CCCD for this Characteristic" -msgstr "" - -#: ports/atmel-samd/common-hal/analogio/AnalogOut.c -#: ports/stm/common-hal/analogio/AnalogOut.c -msgid "No DAC on chip" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "No DMA channel found" -msgstr "" - -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "No DMA pacing timer found" -msgstr "" - -#: shared-module/adafruit_bus_device/i2c_device/I2CDevice.c -#, c-format -msgid "No I2C device at address: 0x%x" -msgstr "" - -#: supervisor/shared/web_workflow/web_workflow.c -msgid "No IP" -msgstr "" - -#: ports/atmel-samd/common-hal/microcontroller/__init__.c -#: ports/cxd56/common-hal/microcontroller/__init__.c -#: ports/mimxrt10xx/common-hal/microcontroller/__init__.c -msgid "No bootloader present" -msgstr "" - -#: shared-module/usb/core/Device.c -msgid "No configuration set" -msgstr "" - -#: shared-bindings/_bleio/PacketBuffer.c -msgid "No connection: length cannot be determined" -msgstr "" - -#: shared-bindings/board/__init__.c -msgid "No default %q bus" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c -#: ports/atmel-samd/common-hal/touchio/TouchIn.c -msgid "No free GCLKs" -msgstr "" - -#: shared-bindings/os/__init__.c -msgid "No hardware random available" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "No in in program" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "No in or out in program" -msgstr "" - -#: py/objint.c shared-bindings/time/__init__.c -msgid "No long integer support" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "No network with that ssid" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "No out in program" -msgstr "" - -#: ports/atmel-samd/common-hal/busio/I2C.c -#: ports/espressif/common-hal/busio/I2C.c -#: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nordic/common-hal/busio/I2C.c -#: ports/raspberrypi/common-hal/busio/I2C.c -msgid "No pull up found on SDA or SCL; check your wiring" -msgstr "" - -#: shared-module/touchio/TouchIn.c -msgid "No pulldown on pin; 1Mohm recommended" -msgstr "" - -#: shared-module/touchio/TouchIn.c -msgid "No pullup on pin; 1Mohm recommended" -msgstr "" - -#: py/moderrno.c -msgid "No space left on device" -msgstr "" - -#: py/moderrno.c -msgid "No such device" -msgstr "" - -#: py/moderrno.c -msgid "No such file/directory" -msgstr "" - -#: shared-module/rgbmatrix/RGBMatrix.c -msgid "No timer available" -msgstr "" - -#: shared-module/usb/core/Device.c -msgid "No usb host port initialized" -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Nordic system firmware out of memory" -msgstr "" - -#: shared-bindings/ipaddress/IPv4Address.c shared-bindings/ipaddress/__init__.c -msgid "Not a valid IP string" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -#: shared-bindings/_bleio/CharacteristicBuffer.c -msgid "Not connected" -msgstr "" - -#: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c -msgid "Not playing" -msgstr "" - -#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/espressif/common-hal/sdioio/SDCard.c -#, c-format -msgid "Number of data_pins must be %d or %d, not %d" -msgstr "" - -#: shared-bindings/util.c -msgid "" -"Object has been deinitialized and can no longer be used. Create a new object." -msgstr "" - -#: ports/nordic/common-hal/busio/UART.c -msgid "Odd parity is not supported" -msgstr "" - -#: supervisor/shared/bluetooth/bluetooth.c -msgid "Off" -msgstr "" - -#: supervisor/shared/bluetooth/bluetooth.c -msgid "Ok" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c -#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c -#, c-format -msgid "Only 8 or 16 bit mono with %dx oversampling supported." -msgstr "" - -#: ports/espressif/common-hal/wifi/__init__.c -#: ports/raspberrypi/common-hal/wifi/__init__.c -msgid "Only IPv4 addresses supported" -msgstr "" - -#: ports/raspberrypi/common-hal/socketpool/Socket.c -msgid "Only IPv4 sockets supported" -msgstr "" - -#: shared-module/displayio/OnDiskBitmap.c -#, c-format -msgid "" -"Only Windows format, uncompressed BMP supported: given header size is %d" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Only connectable advertisements can be directed" -msgstr "" - -#: ports/stm/common-hal/alarm/pin/PinAlarm.c -msgid "Only edge detection is available on this hardware" -msgstr "" - -#: shared-bindings/ipaddress/__init__.c -msgid "Only int or string supported for ip" -msgstr "" - -#: ports/espressif/common-hal/alarm/touch/TouchAlarm.c -msgid "Only one %q can be set in deep sleep." -msgstr "" - -#: ports/espressif/common-hal/espulp/ULPAlarm.c -msgid "Only one %q can be set." -msgstr "" - -#: ports/espressif/common-hal/i2ctarget/I2CTarget.c -#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c -msgid "Only one address is allowed" -msgstr "" - -#: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c -#: ports/nordic/common-hal/alarm/time/TimeAlarm.c -#: ports/stm/common-hal/alarm/time/TimeAlarm.c -msgid "Only one alarm.time alarm can be set" -msgstr "" - -#: ports/espressif/common-hal/alarm/time/TimeAlarm.c -#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c -msgid "Only one alarm.time alarm can be set." -msgstr "" - -#: shared-module/displayio/ColorConverter.c -msgid "Only one color can be transparent at a time" -msgstr "" - -#: py/moderrno.c -msgid "Operation not permitted" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Operation or feature not supported" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Operation timed out" -msgstr "" - -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "Out of MDNS service slots" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Out of memory" -msgstr "" - -#: ports/espressif/common-hal/socketpool/Socket.c -#: ports/raspberrypi/common-hal/socketpool/Socket.c -#: ports/zephyr-cp/common-hal/socketpool/Socket.c -msgid "Out of sockets" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "Out-buffer elements must be <= 4 bytes long" -msgstr "" - -#: ports/stm/common-hal/pwmio/PWMOut.c -msgid "PWM restart" -msgstr "" - -#: ports/raspberrypi/common-hal/countio/Counter.c -msgid "PWM slice already in use" -msgstr "" - -#: ports/raspberrypi/common-hal/countio/Counter.c -msgid "PWM slice channel A already in use" -msgstr "" - -#: shared-bindings/spitarget/SPITarget.c -msgid "Packet buffers for an SPI transfer must have the same length." -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Parameter error" -msgstr "" - -#: ports/espressif/common-hal/audiobusio/__init__.c -msgid "Peripheral in use" -msgstr "" - -#: py/moderrno.c -msgid "Permission denied" -msgstr "" - -#: ports/stm/common-hal/alarm/pin/PinAlarm.c -msgid "Pin cannot wake from Deep Sleep" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Pin count too large" -msgstr "" - -#: ports/stm/common-hal/alarm/pin/PinAlarm.c -#: ports/stm/common-hal/pulseio/PulseIn.c -msgid "Pin interrupt already in use" -msgstr "" - -#: shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c -msgid "Pin is input only" -msgstr "" - -#: ports/raspberrypi/common-hal/countio/Counter.c -msgid "Pin must be on PWM Channel B" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "" -"Pinout uses %d bytes per element, which consumes more than the ideal %d " -"bytes. If this cannot be avoided, pass allow_inefficient=True to the " -"constructor" -msgstr "" - -#: ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c -msgid "Pins must be sequential" -msgstr "" - -#: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c -msgid "Pins must be sequential GPIO pins" -msgstr "" - -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "Pins must share PWM slice" -msgstr "" - -#: shared-module/usb/core/Device.c -msgid "Pipe error" -msgstr "" - -#: py/builtinhelp.c -msgid "Plus any modules on the filesystem\n" -msgstr "" - -#: shared-module/vectorio/Polygon.c -msgid "Polygon needs at least 3 points" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Power dipped. Make sure you are providing enough power." -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Prefix buffer must be on the heap" -msgstr "" - -#: main.c -msgid "Press any key to enter the REPL. Use CTRL-D to reload.\n" -msgstr "" - -#: main.c -msgid "Pretending to deep sleep until alarm, CTRL-C or file write.\n" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Program does IN without loading ISR" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Program does OUT without loading OSR" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "Program size invalid" -msgstr "" - -#: ports/espressif/common-hal/espulp/ULP.c -msgid "Program too long" -msgstr "" - -#: shared-bindings/rclcpy/Publisher.c -msgid "Publishers can only be created from a parent node" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/i2cioexpander/IOPin.c -msgid "Pull not used when direction is output." -msgstr "" - -#: ports/raspberrypi/common-hal/countio/Counter.c -msgid "RISE_AND_FALL not available on this chip" -msgstr "" - -#: shared-module/displayio/OnDiskBitmap.c -msgid "RLE-compressed BMP not supported" -msgstr "" - -#: ports/stm/common-hal/os/__init__.c -msgid "RNG DeInit Error" -msgstr "" - -#: ports/stm/common-hal/os/__init__.c -msgid "RNG Init Error" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -msgid "ROS failed to initialize. Is agent connected?" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -msgid "ROS internal setup failure" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -msgid "ROS memory allocator failure" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/Node.c -msgid "ROS node failed to initialize" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/Publisher.c -msgid "ROS topic failed to initialize" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -#: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c -#: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c -msgid "RS485" -msgstr "" - -#: ports/espressif/common-hal/busio/UART.c -#: ports/mimxrt10xx/common-hal/busio/UART.c -msgid "RS485 inversion specified when not in RS485 mode" -msgstr "" - -#: shared-bindings/alarm/time/TimeAlarm.c shared-bindings/time/__init__.c -msgid "RTC is not supported on this board" -msgstr "" - -#: ports/stm/common-hal/os/__init__.c -msgid "Random number generation error" -msgstr "" - -#: shared-bindings/_bleio/__init__.c -#: shared-bindings/memorymonitor/AllocationSize.c -#: shared-bindings/pulseio/PulseIn.c shared-module/bitmaptools/__init__.c -#: shared-module/displayio/Bitmap.c shared-module/displayio/Group.c -msgid "Read-only" -msgstr "" - -#: extmod/vfs_fat.c py/moderrno.c -msgid "Read-only filesystem" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Received response was invalid" -msgstr "" - -#: supervisor/shared/bluetooth/bluetooth.c -msgid "Reconnecting" -msgstr "" - -#: shared-bindings/epaperdisplay/EPaperDisplay.c -msgid "Refresh too soon" -msgstr "" - -#: shared-bindings/canio/RemoteTransmissionRequest.c -msgid "RemoteTransmissionRequests limited to 8 bytes" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "Requested AES mode is unsupported" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Requested resource not found" -msgstr "" - -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -msgid "Right channel unsupported" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Right format but not supported" -msgstr "" - -#: main.c -msgid "Running in safe mode! Not running saved code.\n" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "SD card CSD format not supported" -msgstr "" - -#: ports/cxd56/common-hal/sdioio/SDCard.c -msgid "SDCard init" -msgstr "" - -#: ports/stm/common-hal/sdioio/SDCard.c -#, c-format -msgid "SDIO GetCardInfo Error %d" -msgstr "" - -#: ports/espressif/common-hal/sdioio/SDCard.c -#: ports/stm/common-hal/sdioio/SDCard.c -#, c-format -msgid "SDIO Init Error %x" -msgstr "" - -#: ports/espressif/common-hal/busio/SPI.c -msgid "SPI configuration failed" -msgstr "" - -#: ports/stm/common-hal/busio/SPI.c -msgid "SPI init error" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -msgid "SPI needs MOSI, MISO, and SCK" -msgstr "" - -#: ports/raspberrypi/common-hal/busio/SPI.c -msgid "SPI peripheral in use" -msgstr "" - -#: ports/stm/common-hal/busio/SPI.c -msgid "SPI re-init" -msgstr "" - -#: shared-bindings/is31fl3741/FrameBuffer.c -msgid "Scale dimensions must divide by 3" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Scan already in progress. Stop with stop_scan." -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c -msgid "Serializer in use" -msgstr "" - -#: shared-bindings/ssl/SSLContext.c -msgid "Server side context cannot have hostname" -msgstr "" - -#: ports/cxd56/common-hal/camera/Camera.c -msgid "Size not supported" -msgstr "" - -#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c -#: shared-bindings/nvm/ByteArray.c -msgid "Slice and value different lengths." -msgstr "" - -#: shared-bindings/displayio/Bitmap.c shared-bindings/displayio/Group.c -#: shared-bindings/displayio/TileGrid.c -#: shared-bindings/memorymonitor/AllocationSize.c -#: shared-bindings/pulseio/PulseIn.c -#: shared-bindings/tilepalettemapper/TilePaletteMapper.c -msgid "Slices not supported" -msgstr "" - -#: ports/espressif/common-hal/socketpool/SocketPool.c -#: ports/raspberrypi/common-hal/socketpool/SocketPool.c -msgid "SocketPool can only be used with wifi.radio" -msgstr "" - -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c -msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "Source and destination buffers must be the same length" -msgstr "" - -#: shared-bindings/paralleldisplaybus/ParallelBus.c -msgid "Specify exactly one of data0 or data_pins" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Stack overflow. Increase stack size." -msgstr "" - -#: shared-bindings/alarm/time/TimeAlarm.c -msgid "Supply one of monotonic_time or epoch_time" -msgstr "" - -#: shared-bindings/gnss/GNSS.c -msgid "System entry must be gnss.SatelliteSystem" -msgstr "" - -#: ports/stm/common-hal/microcontroller/Processor.c -msgid "Temperature read timed out" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "The `microcontroller` module was used to boot into safe mode." -msgstr "" - -#: py/obj.c -msgid "The above exception was the direct cause of the following exception:" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -msgid "The length of rgb_pins must be 6, 12, 18, 24, or 30" -msgstr "" - -#: shared-module/audiocore/__init__.c -msgid "The sample's %q does not match" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Third-party firmware fatal error." -msgstr "" - -#: shared-module/imagecapture/ParallelImageCapture.c -msgid "This microcontroller does not support continuous capture." -msgstr "" - -#: shared-module/paralleldisplaybus/ParallelBus.c -msgid "" -"This microcontroller only supports data0=, not data_pins=, because it " -"requires contiguous pins." -msgstr "" - -#: shared-bindings/displayio/TileGrid.c -msgid "Tile height must exactly divide bitmap height" -msgstr "" - -#: shared-bindings/displayio/TileGrid.c -#: shared-bindings/tilepalettemapper/TilePaletteMapper.c -#: shared-module/displayio/TileGrid.c -msgid "Tile index out of bounds" -msgstr "" - -#: shared-bindings/displayio/TileGrid.c -msgid "Tile width must exactly divide bitmap width" -msgstr "" - -#: shared-module/tilepalettemapper/TilePaletteMapper.c -msgid "TilePaletteMapper may only be bound to a TileGrid once" -msgstr "" - -#: shared-bindings/alarm/time/TimeAlarm.c -msgid "Time is in the past." -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -#, c-format -msgid "Timeout is too long: Maximum timeout length is %d seconds" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "Timeout must be < 100 seconds" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -msgid "Too many channels in sample" -msgstr "" - -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -msgid "Too many channels in sample." -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -msgid "Too many descriptors" -msgstr "" - -#: shared-module/displayio/__init__.c -msgid "Too many display busses; forgot displayio.release_displays() ?" -msgstr "" - -#: shared-module/displayio/__init__.c -msgid "Too many displays" -msgstr "" - -#: ports/espressif/common-hal/_bleio/PacketBuffer.c -#: ports/nordic/common-hal/_bleio/PacketBuffer.c -msgid "Total data to write is larger than %q" -msgstr "" - -#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c -#: ports/stm/common-hal/alarm/touch/TouchAlarm.c -msgid "Touch alarms not available" -msgstr "" - -#: py/obj.c -msgid "Traceback (most recent call last):\n" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "UART de-init" -msgstr "" - -#: ports/cxd56/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c -#: ports/stm/common-hal/busio/UART.c -msgid "UART init" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "UART needs TX & RX" -msgstr "" - -#: ports/raspberrypi/common-hal/busio/UART.c -msgid "UART peripheral in use" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "UART re-init" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "UART read error" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "UART transaction timeout" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "UART write" -msgstr "" - -#: main.c -msgid "UID:" -msgstr "" - -#: shared-module/usb_hid/Device.c -msgid "USB busy" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "USB devices need more endpoints than are available." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "USB devices specify too many interface names." -msgstr "" - -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "" - -#: shared-bindings/_bleio/UUID.c -msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" -msgstr "" - -#: shared-bindings/_bleio/UUID.c -msgid "UUID value is not str, int or byte buffer" -msgstr "" - -#: ports/raspberrypi/common-hal/memorymap/AddressRange.c -msgid "Unable to access unaligned IO register" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "Unable to allocate buffers for signed conversion" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Unable to allocate to the heap." -msgstr "" - -#: ports/espressif/common-hal/busio/I2C.c -#: ports/espressif/common-hal/busio/SPI.c -msgid "Unable to create lock" -msgstr "" - -#: shared-module/i2cdisplaybus/I2CDisplayBus.c -#: shared-module/is31fl3741/IS31FL3741.c -#, c-format -msgid "Unable to find I2C Display at %x" -msgstr "" - -#: py/parse.c -msgid "Unable to init parser" -msgstr "" - -#: shared-module/displayio/OnDiskBitmap.c -msgid "Unable to read color palette data" -msgstr "" - -#: ports/mimxrt10xx/common-hal/canio/CAN.c -msgid "Unable to send CAN Message: all Tx message buffers are busy" -msgstr "" - -#: ports/espressif/common-hal/mdns/Server.c -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "Unable to start mDNS query" -msgstr "" - -#: shared-bindings/nvm/ByteArray.c -msgid "Unable to write to nvm." -msgstr "" - -#: ports/raspberrypi/common-hal/memorymap/AddressRange.c -msgid "Unable to write to read-only memory" -msgstr "" - -#: shared-bindings/alarm/SleepMemory.c -msgid "Unable to write to sleep_memory." -msgstr "" - -#: ports/nordic/common-hal/_bleio/UUID.c -msgid "Unexpected nrfx uuid type" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown BLE error at %s:%d: %d" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown BLE error: %d" -msgstr "" - -#: ports/espressif/common-hal/max3421e/Max3421E.c -#: ports/raspberrypi/common-hal/wifi/__init__.c -#, c-format -msgid "Unknown error code %d" -msgstr "" - -#: shared-bindings/wifi/Radio.c -#, c-format -msgid "Unknown failure %d" -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown gatt error: 0x%04x" -msgstr "" - -#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c -#: supervisor/shared/safe_mode.c -msgid "Unknown reason." -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown security error: 0x%04x" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown system firmware error at %s:%d: %d" -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown system firmware error: %04x" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown system firmware error: %d" -msgstr "" - -#: shared-bindings/adafruit_pixelbuf/PixelBuf.c -#: shared-module/_pixelmap/PixelMap.c -#, c-format -msgid "Unmatched number of items on RHS (expected %d, got %d)." -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " -"declined or ignored." -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Unsupported JPEG (may be progressive)" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "Unsupported colorspace" -msgstr "" - -#: shared-module/displayio/bus_core.c -msgid "Unsupported display bus type" -msgstr "" - -#: shared-bindings/hashlib/__init__.c -msgid "Unsupported hash algorithm" -msgstr "" - -#: ports/espressif/common-hal/socketpool/Socket.c -#: ports/zephyr-cp/common-hal/socketpool/Socket.c -msgid "Unsupported socket type" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/espressif/common-hal/dualbank/__init__.c -msgid "Update failed" -msgstr "" - -#: ports/zephyr-cp/common-hal/busio/I2C.c -#: ports/zephyr-cp/common-hal/busio/SPI.c -#: ports/zephyr-cp/common-hal/busio/UART.c -msgid "Use device tree to define %q devices" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Descriptor.c -msgid "Value length != required fixed length" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Descriptor.c -msgid "Value length > max_length" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Version was invalid" -msgstr "" - -#: ports/stm/common-hal/microcontroller/Processor.c -msgid "Voltage read timed out" -msgstr "" - -#: main.c -msgid "WARNING: Your code filename has two extensions\n" -msgstr "" - -#: ports/nordic/common-hal/watchdog/WatchDogTimer.c -msgid "WatchDogTimer cannot be deinitialized once mode is set to RESET" -msgstr "" - -#: py/builtinhelp.c -#, c-format -msgid "" -"Welcome to Adafruit CircuitPython %s!\n" -"\n" -"Visit circuitpython.org for more information.\n" -"\n" -"To list built-in modules type `help(\"modules\")`.\n" -msgstr "" - -#: supervisor/shared/web_workflow/web_workflow.c -msgid "Wi-Fi: " -msgstr "" - -#: ports/espressif/common-hal/wifi/Radio.c -#: ports/raspberrypi/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "WiFi is not enabled" -msgstr "" - -#: main.c -msgid "Woken up by alarm.\n" -msgstr "" - -#: ports/espressif/common-hal/_bleio/PacketBuffer.c -#: ports/nordic/common-hal/_bleio/PacketBuffer.c -msgid "Writes not supported on Characteristic" -msgstr "" - -#: ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h -#: ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h -#: ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.h -#: ports/atmel-samd/boards/meowmeow/mpconfigboard.h -msgid "You pressed both buttons at start up." -msgstr "" - -#: ports/espressif/boards/m5stack_core_basic/mpconfigboard.h -#: ports/espressif/boards/m5stack_core_fire/mpconfigboard.h -#: ports/espressif/boards/m5stack_stick_c/mpconfigboard.h -#: ports/espressif/boards/m5stack_stick_c_plus/mpconfigboard.h -#: ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h -msgid "You pressed button A at start up." -msgstr "" - -#: ports/espressif/boards/m5stack_m5paper/mpconfigboard.h -msgid "You pressed button DOWN at start up." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "You pressed the BOOT button at start up" -msgstr "" - -#: ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.h -#: ports/espressif/boards/adafruit_itsybitsy_esp32/mpconfigboard.h -#: ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/mpconfigboard.h -msgid "You pressed the BOOT button at start up." -msgstr "" - -#: ports/espressif/boards/adafruit_huzzah32_breakout/mpconfigboard.h -msgid "You pressed the GPIO0 button at start up." -msgstr "" - -#: ports/espressif/boards/espressif_esp32_lyrat/mpconfigboard.h -msgid "You pressed the Rec button at start up." -msgstr "" - -#: ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h -msgid "You pressed the SW38 button at start up." -msgstr "" - -#: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h -#: ports/espressif/boards/vidi_x/mpconfigboard.h -msgid "You pressed the VOLUME button at start up." -msgstr "" - -#: ports/espressif/boards/m5stack_atom_echo/mpconfigboard.h -#: ports/espressif/boards/m5stack_atom_lite/mpconfigboard.h -#: ports/espressif/boards/m5stack_atom_matrix/mpconfigboard.h -#: ports/espressif/boards/m5stack_atom_u/mpconfigboard.h -msgid "You pressed the central button at start up." -msgstr "" - -#: ports/nordic/boards/aramcon2_badge/mpconfigboard.h -msgid "You pressed the left button at start up." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "You pressed the reset button during boot." -msgstr "" - -#: supervisor/shared/micropython.c -msgid "[truncated due to length]" -msgstr "" - -#: py/objtype.c -msgid "__init__() should return None" -msgstr "" - -#: py/objtype.c -#, c-format -msgid "__init__() should return None, not '%s'" -msgstr "" - -#: py/objobject.c -msgid "__new__ arg must be a user-type" -msgstr "" - -#: extmod/modbinascii.c extmod/modhashlib.c py/objarray.c -msgid "a bytes-like object is required" -msgstr "" - -#: shared-bindings/i2cioexpander/IOExpander.c -msgid "address out of range" -msgstr "" - -#: shared-bindings/i2ctarget/I2CTarget.c -msgid "addresses is empty" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "already playing" -msgstr "" - -#: py/compile.c -msgid "annotation must be an identifier" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "arange: cannot compute length" -msgstr "" - -#: py/modbuiltins.c -msgid "arg is an empty sequence" -msgstr "" - -#: py/objobject.c -msgid "arg must be user-type" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "argsort argument must be an ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "argsort is not implemented for flattened arrays" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "argument must be None, an integer or a tuple of integers" -msgstr "" - -#: py/compile.c -msgid "argument name reused" -msgstr "" - -#: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c -msgid "argument num/types mismatch" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/numpy/transform.c -msgid "arguments must be ndarrays" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "array and index length must be equal" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "array has too many dimensions" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "array is too big" -msgstr "" - -#: py/objarray.c shared-bindings/alarm/SleepMemory.c -#: shared-bindings/memorymap/AddressRange.c shared-bindings/nvm/ByteArray.c -msgid "array/bytes required on right side" -msgstr "" - -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - -#: py/compile.c -msgid "async for/with outside async function" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "attempt to get (arg)min/(arg)max of empty sequence" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "attempt to get argmin/argmax of an empty sequence" -msgstr "" - -#: py/objstr.c -msgid "attributes not supported" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "audio format not supported" -msgstr "" - -#: extmod/ulab/code/ulab_tools.c -msgid "axis is out of bounds" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c -msgid "axis must be None, or an integer" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "axis too long" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "background value out of range of target" -msgstr "" - -#: py/builtinevex.c -msgid "bad compile mode" -msgstr "" - -#: py/objstr.c -msgid "bad conversion specifier" -msgstr "" - -#: py/objstr.c -msgid "bad format string" -msgstr "" - -#: py/binary.c py/objarray.c -msgid "bad typecode" -msgstr "" - -#: py/emitnative.c -msgid "binary op %q not implemented" -msgstr "" - -#: ports/espressif/common-hal/audiobusio/PDMIn.c -msgid "bit_depth must be 8, 16, 24, or 32." -msgstr "" - -#: shared-module/bitmapfilter/__init__.c -msgid "bitmap size and depth must match" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "bitmap sizes must match" -msgstr "" - -#: extmod/modrandom.c -msgid "bits must be 32 or less" -msgstr "" - -#: shared-bindings/audiofreeverb/Freeverb.c -msgid "bits_per_sample must be 16" -msgstr "" - -#: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c -#: shared-bindings/audiodelays/MultiTapDelay.c -#: shared-bindings/audiodelays/PitchShift.c -#: shared-bindings/audiofilters/Distortion.c -#: shared-bindings/audiofilters/Filter.c shared-bindings/audiofilters/Phaser.c -#: shared-bindings/audiomixer/Mixer.c -msgid "bits_per_sample must be 8 or 16" -msgstr "" - -#: py/emitinlinethumb.c -msgid "branch not in range" -msgstr "" - -#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c -msgid "buffer is smaller than requested size" -msgstr "" - -#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c -msgid "buffer size must be a multiple of element size" -msgstr "" - -#: shared-module/struct/__init__.c -msgid "buffer size must match format" -msgstr "" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "" - -#: py/modstruct.c shared-module/struct/__init__.c -msgid "buffer too small" -msgstr "" - -#: shared-bindings/socketpool/Socket.c shared-bindings/ssl/SSLSocket.c -msgid "buffer too small for requested bytes" -msgstr "" - -#: py/emitbc.c -msgid "bytecode overflow" -msgstr "" - -#: py/objarray.c -msgid "bytes length not a multiple of item size" -msgstr "" - -#: py/objstr.c -msgid "bytes value out of range" -msgstr "" - -#: ports/atmel-samd/bindings/samd/Clock.c -msgid "calibration is out of range" -msgstr "" - -#: ports/atmel-samd/bindings/samd/Clock.c -msgid "calibration is read only" -msgstr "" - -#: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c -#: shared-module/vectorio/Rectangle.c -msgid "can only have one parent" -msgstr "" - -#: py/emitinlinerv32.c -msgid "can only have up to 4 parameters for RV32 assembly" -msgstr "" - -#: py/emitinlinethumb.c -msgid "can only have up to 4 parameters to Thumb assembly" -msgstr "" - -#: py/emitinlinextensa.c -msgid "can only have up to 4 parameters to Xtensa assembly" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "can only specify one unknown dimension" -msgstr "" - -#: py/objtype.c -msgid "can't add special method to already-subclassed class" -msgstr "" - -#: py/compile.c -msgid "can't assign to expression" -msgstr "" - -#: extmod/modasyncio.c -msgid "can't cancel self" -msgstr "" - -#: py/obj.c shared-module/adafruit_pixelbuf/PixelBuf.c -msgid "can't convert %q to %q" -msgstr "" - -#: py/obj.c -#, c-format -msgid "can't convert %s to complex" -msgstr "" - -#: py/obj.c -#, c-format -msgid "can't convert %s to float" -msgstr "" - -#: py/objint.c py/runtime.c -#, c-format -msgid "can't convert %s to int" -msgstr "" - -#: py/objstr.c -msgid "can't convert '%q' object to %q implicitly" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "can't convert complex to float" -msgstr "" - -#: py/obj.c -msgid "can't convert to complex" -msgstr "" - -#: py/obj.c -msgid "can't convert to float" -msgstr "" - -#: py/runtime.c -msgid "can't convert to int" -msgstr "" - -#: py/objstr.c -msgid "can't convert to str implicitly" -msgstr "" - -#: py/objtype.c -msgid "can't create '%q' instances" -msgstr "" - -#: py/compile.c -msgid "can't declare nonlocal in outer code" -msgstr "" - -#: py/compile.c -msgid "can't delete expression" -msgstr "" - -#: py/emitnative.c -msgid "can't do binary op between '%q' and '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't do unary op of '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't implicitly convert '%q' to 'bool'" -msgstr "" - -#: py/runtime.c -msgid "can't import name %q" -msgstr "" - -#: py/emitnative.c -msgid "can't load from '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't load with '%q' index" -msgstr "" - -#: py/builtinimport.c -msgid "can't perform relative import" -msgstr "" - -#: py/objgenerator.c -msgid "can't send non-None value to a just-started generator" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "can't set 512 block size" -msgstr "" - -#: py/objexcept.c py/objnamedtuple.c -msgid "can't set attribute" -msgstr "" - -#: py/runtime.c -msgid "can't set attribute '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't store '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't store to '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't store with '%q' index" -msgstr "" - -#: py/objstr.c -msgid "" -"can't switch from automatic field numbering to manual field specification" -msgstr "" - -#: py/objstr.c -msgid "" -"can't switch from manual field specification to automatic field numbering" -msgstr "" - -#: py/objcomplex.c -msgid "can't truncate-divide a complex number" -msgstr "" - -#: extmod/modasyncio.c -msgid "can't wait" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot assign new shape" -msgstr "" - -#: extmod/ulab/code/ndarray_operators.c -msgid "cannot cast output with casting rule" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot convert complex to dtype" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot convert complex type" -msgstr "" - -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot delete array elements" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot reshape array" -msgstr "" - -#: py/emitnative.c -msgid "casting" -msgstr "" - -#: ports/stm/common-hal/pwmio/PWMOut.c -msgid "channel re-init" -msgstr "" - -#: shared-bindings/_stage/Text.c -msgid "chars buffer too small" -msgstr "" - -#: py/modbuiltins.c -msgid "chr() arg not in range(0x110000)" -msgstr "" - -#: py/modbuiltins.c -msgid "chr() arg not in range(256)" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "clip point must be (x,y) tuple" -msgstr "" - -#: shared-bindings/msgpack/ExtType.c -msgid "code outside range 0~127" -msgstr "" - -#: shared-bindings/displayio/Palette.c -msgid "color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)" -msgstr "" - -#: shared-bindings/displayio/Palette.c -msgid "color buffer must be a buffer, tuple, list, or int" -msgstr "" - -#: shared-bindings/displayio/Palette.c -msgid "color buffer must be a bytearray or array of type 'b' or 'B'" -msgstr "" - -#: shared-bindings/displayio/Palette.c -msgid "color must be between 0x000000 and 0xffffff" -msgstr "" - -#: py/emitnative.c -msgid "comparison of int and uint" -msgstr "" - -#: py/objcomplex.c -msgid "complex divide by zero" -msgstr "" - -#: py/objfloat.c py/parsenum.c -msgid "complex values not supported" -msgstr "" - -#: extmod/modzlib.c -msgid "compression header" -msgstr "" - -#: py/emitnative.c -msgid "conversion to object" -msgstr "" - -#: extmod/ulab/code/numpy/filter.c -msgid "convolve arguments must be linear arrays" -msgstr "" - -#: extmod/ulab/code/numpy/filter.c -msgid "convolve arguments must be ndarrays" -msgstr "" - -#: extmod/ulab/code/numpy/filter.c -msgid "convolve arguments must not be empty" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "corrupted file" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "could not invert Vandermonde matrix" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "couldn't determine SD card version" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "cross is defined for 1D arrays of length 3" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "data must be iterable" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "data must be of equal length" -msgstr "" - -#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c -#, c-format -msgid "data pin #%d in use" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "data type not understood" -msgstr "" - -#: py/parsenum.c -msgid "decimal numbers not supported" -msgstr "" - -#: py/compile.c -msgid "default 'except' must be last" -msgstr "" - -#: shared-bindings/msgpack/__init__.c -msgid "default is not a function" -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "" -"destination buffer must be a bytearray or array of type 'B' for bit_depth = 8" -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "destination buffer must be an array of type 'H' for bit_depth = 16" -msgstr "" - -#: py/objdict.c -msgid "dict update sequence has wrong length" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "diff argument must be an ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "differentiation order out of range" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c -msgid "dimensions do not match" -msgstr "" - -#: py/emitnative.c -msgid "div/mod not implemented for uint" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "divide by zero" -msgstr "" - -#: py/runtime.c -msgid "division by zero" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "dtype must be float, or complex" -msgstr "" - -#: extmod/ulab/code/ndarray_operators.c -msgid "dtype of int32 is not supported" -msgstr "" - -#: py/objdeque.c -msgid "empty" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "empty file" -msgstr "" - -#: extmod/modasyncio.c extmod/modheapq.c -msgid "empty heap" -msgstr "" - -#: py/objstr.c -msgid "empty separator" -msgstr "" - -#: shared-bindings/random/__init__.c -msgid "empty sequence" -msgstr "" - -#: py/objstr.c -msgid "end of format while looking for conversion specifier" -msgstr "" - -#: shared-bindings/alarm/time/TimeAlarm.c -msgid "epoch_time not supported on this board" -msgstr "" - -#: ports/nordic/common-hal/busio/UART.c -#, c-format -msgid "error = 0x%08lX" -msgstr "" - -#: py/runtime.c -msgid "exceptions must derive from BaseException" -msgstr "" - -#: py/objstr.c -msgid "expected ':' after format specifier" -msgstr "" - -#: py/obj.c -msgid "expected tuple/list" -msgstr "" - -#: py/modthread.c -msgid "expecting a dict for keyword args" -msgstr "" - -#: py/compile.c -msgid "expecting an assembler instruction" -msgstr "" - -#: py/compile.c -msgid "expecting just a value for set" -msgstr "" - -#: py/compile.c -msgid "expecting key:value for dict" -msgstr "" - -#: shared-bindings/msgpack/__init__.c -msgid "ext_hook is not a function" -msgstr "" - -#: py/argcheck.c -msgid "extra keyword arguments given" -msgstr "" - -#: py/argcheck.c -msgid "extra positional arguments given" -msgstr "" - -#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c -#: shared-bindings/displayio/OnDiskBitmap.c shared-bindings/gifio/OnDiskGif.c -#: shared-bindings/synthio/__init__.c shared-module/gifio/GifWriter.c -msgid "file must be a file opened in byte mode" -msgstr "" - -#: shared-bindings/traceback/__init__.c -msgid "file write is not available" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "first argument must be a callable" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "first argument must be a function" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "first argument must be a tuple of ndarrays" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c extmod/ulab/code/numpy/vector.c -msgid "first argument must be an ndarray" -msgstr "" - -#: py/objtype.c -msgid "first argument to super() must be type" -msgstr "" - -#: extmod/ulab/code/scipy/linalg/linalg.c -msgid "first two arguments must be ndarrays" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "flattening order must be either 'C', or 'F'" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "flip argument must be an ndarray" -msgstr "" - -#: py/objint.c -msgid "float too big" -msgstr "" - -#: py/nativeglue.c -msgid "float unsupported" -msgstr "" - -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - -#: extmod/moddeflate.c -msgid "format" -msgstr "" - -#: py/objstr.c -msgid "format needs a dict" -msgstr "" - -#: py/objstr.c -msgid "format string didn't convert all arguments" -msgstr "" - -#: py/objstr.c -msgid "format string needs more arguments" -msgstr "" - -#: py/objdeque.c -msgid "full" -msgstr "" - -#: py/argcheck.c -msgid "function doesn't take keyword arguments" -msgstr "" - -#: py/argcheck.c -#, c-format -msgid "function expected at most %d arguments, got %d" -msgstr "" - -#: py/bc.c py/objnamedtuple.c -msgid "function got multiple values for argument '%q'" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "function has the same sign at the ends of interval" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "function is defined for ndarrays only" -msgstr "" - -#: extmod/ulab/code/numpy/carray/carray.c -msgid "function is implemented for ndarrays only" -msgstr "" - -#: py/argcheck.c -#, c-format -msgid "function missing %d required positional arguments" -msgstr "" - -#: py/bc.c -msgid "function missing keyword-only argument" -msgstr "" - -#: py/bc.c -msgid "function missing required keyword argument '%q'" -msgstr "" - -#: py/bc.c -#, c-format -msgid "function missing required positional argument #%d" -msgstr "" - -#: py/argcheck.c py/bc.c py/objnamedtuple.c shared-bindings/_eve/__init__.c -#: shared-bindings/time/__init__.c -#, c-format -msgid "function takes %d positional arguments but %d were given" -msgstr "" - -#: py/objgenerator.c -msgid "generator already executing" -msgstr "" - -#: py/objgenerator.c -msgid "generator ignored GeneratorExit" -msgstr "" - -#: py/objgenerator.c py/runtime.c -msgid "generator raised StopIteration" -msgstr "" - -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - -#: extmod/modhashlib.c -msgid "hash is final" -msgstr "" - -#: extmod/modheapq.c -msgid "heap must be a list" -msgstr "" - -#: py/compile.c -msgid "identifier redefined as global" -msgstr "" - -#: py/compile.c -msgid "identifier redefined as nonlocal" -msgstr "" - -#: py/compile.c -msgid "import * not at module level" -msgstr "" - -#: py/persistentcode.c -msgid "incompatible .mpy arch" -msgstr "" - -#: py/persistentcode.c -msgid "incompatible .mpy file" -msgstr "" - -#: py/objstr.c -msgid "incomplete format" -msgstr "" - -#: py/objstr.c -msgid "incomplete format key" -msgstr "" - -#: extmod/modbinascii.c -msgid "incorrect padding" -msgstr "" - -#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/transform.c -msgid "index is out of bounds" -msgstr "" - -#: shared-bindings/_pixelmap/PixelMap.c -msgid "index must be tuple or int" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c -#: ports/espressif/common-hal/pulseio/PulseIn.c -#: shared-bindings/bitmaptools/__init__.c -msgid "index out of range" -msgstr "" - -#: py/obj.c -msgid "indices must be integers" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "indices must be integers, slices, or Boolean lists" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "initial values must be iterable" -msgstr "" - -#: py/compile.c -msgid "inline assembler must be a function" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "input and output dimensions differ" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "input and output shapes differ" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "input argument must be an integer, a tuple, or a list" -msgstr "" - -#: extmod/ulab/code/numpy/fft/fft_tools.c -msgid "input array length must be power of 2" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "input arrays are not compatible" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "input data must be an iterable" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "input dtype must be float or complex" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "input is not iterable" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "input matrix is asymmetric" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -#: extmod/ulab/code/scipy/linalg/linalg.c -msgid "input matrix is singular" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "input must be 1- or 2-d" -msgstr "" - -#: extmod/ulab/code/numpy/carray/carray.c -msgid "input must be a 1D ndarray" -msgstr "" - -#: extmod/ulab/code/scipy/linalg/linalg.c extmod/ulab/code/user/user.c -msgid "input must be a dense ndarray" -msgstr "" - -#: extmod/ulab/code/user/user.c shared-bindings/_eve/__init__.c -msgid "input must be an ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/carray/carray.c -msgid "input must be an ndarray, or a scalar" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "input must be one-dimensional" -msgstr "" - -#: extmod/ulab/code/ulab_tools.c -msgid "input must be square matrix" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "input must be tuple, list, range, or ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "input vectors must be of equal length" -msgstr "" - -#: extmod/ulab/code/numpy/approx.c -msgid "interp is defined for 1D iterables of equal length" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -#, c-format -msgid "interval must be in range %s-%s" -msgstr "" - -#: py/compile.c -msgid "invalid arch" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -#, c-format -msgid "invalid bits_per_pixel %d, must be, 1, 2, 4, 8, 16, 24, or 32" -msgstr "" - -#: shared-module/ssl/SSLSocket.c -msgid "invalid cert" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -#, c-format -msgid "invalid element size %d for bits_per_pixel %d\n" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -#, c-format -msgid "invalid element_size %d, must be, 1, 2, or 4" -msgstr "" - -#: shared-bindings/traceback/__init__.c -msgid "invalid exception" -msgstr "" - -#: py/objstr.c -msgid "invalid format specifier" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "invalid hostname" -msgstr "" - -#: shared-module/ssl/SSLSocket.c -msgid "invalid key" -msgstr "" - -#: py/compile.c -msgid "invalid micropython decorator" -msgstr "" - -#: ports/espressif/common-hal/espcamera/Camera.c -msgid "invalid setting" -msgstr "" - -#: shared-bindings/random/__init__.c -msgid "invalid step" -msgstr "" - -#: py/compile.c py/parse.c -msgid "invalid syntax" -msgstr "" - -#: py/parsenum.c -msgid "invalid syntax for integer" -msgstr "" - -#: py/parsenum.c -#, c-format -msgid "invalid syntax for integer with base %d" -msgstr "" - -#: py/parsenum.c -msgid "invalid syntax for number" -msgstr "" - -#: py/objtype.c -msgid "issubclass() arg 1 must be a class" -msgstr "" - -#: py/objtype.c -msgid "issubclass() arg 2 must be a class or a tuple of classes" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "iterations did not converge" -msgstr "" - -#: py/objstr.c -msgid "join expects a list of str/bytes objects consistent with self object" -msgstr "" - -#: py/argcheck.c -msgid "keyword argument(s) not implemented - use normal args instead" -msgstr "" - -#: py/emitinlinethumb.c py/emitinlinextensa.c -msgid "label '%q' not defined" -msgstr "" - -#: py/compile.c -msgid "label redefined" -msgstr "" - -#: py/objarray.c -msgid "lhs and rhs should be compatible" -msgstr "" - -#: py/emitnative.c -msgid "local '%q' has type '%q' but source is '%q'" -msgstr "" - -#: py/emitnative.c -msgid "local '%q' used before type known" -msgstr "" - -#: py/vm.c -msgid "local variable referenced before assignment" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -msgid "loopback + silent mode not supported by peripheral" -msgstr "" - -#: ports/espressif/common-hal/mdns/Server.c -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "mDNS already initialized" -msgstr "" - -#: ports/espressif/common-hal/mdns/Server.c -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "mDNS only works with built-in WiFi" -msgstr "" - -#: py/parse.c -msgid "malformed f-string" -msgstr "" - -#: shared-bindings/_stage/Layer.c -msgid "map buffer too small" -msgstr "" - -#: py/modmath.c shared-bindings/math/__init__.c -msgid "math domain error" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "matrix is not positive definite" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Descriptor.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Descriptor.c -#, c-format -msgid "max_length must be 0-%d when fixed_length is %s" -msgstr "" - -#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/random/random.c -msgid "maximum number of dimensions is " -msgstr "" - -#: py/runtime.c -msgid "maximum recursion depth exceeded" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "maxiter must be > 0" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "maxiter should be > 0" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "median argument must be an ndarray" -msgstr "" - -#: py/runtime.c -#, c-format -msgid "memory allocation failed, allocating %u bytes" -msgstr "" - -#: py/runtime.c -msgid "memory allocation failed, heap is locked" -msgstr "" - -#: py/objarray.c -msgid "memoryview offset too large" -msgstr "" - -#: py/objarray.c -msgid "memoryview: length is not a multiple of itemsize" -msgstr "" - -#: extmod/modtime.c -msgid "mktime needs a tuple of length 8 or 9" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "mode must be complete, or reduced" -msgstr "" - -#: py/builtinimport.c -msgid "module not found" -msgstr "" - -#: ports/espressif/common-hal/wifi/Monitor.c -msgid "monitor init failed" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "more degrees of freedom than data points" -msgstr "" - -#: py/compile.c -msgid "multiple *x in assignment" -msgstr "" - -#: py/objtype.c -msgid "multiple bases have instance lay-out conflict" -msgstr "" - -#: py/objtype.c -msgid "multiple inheritance not supported" -msgstr "" - -#: py/emitnative.c -msgid "must raise an object" -msgstr "" - -#: py/modbuiltins.c -msgid "must use keyword argument for key function" -msgstr "" - -#: py/runtime.c -msgid "name '%q' isn't defined" -msgstr "" - -#: py/runtime.c -msgid "name not defined" -msgstr "" - -#: py/qstr.c -msgid "name too long" -msgstr "" - -#: py/persistentcode.c -msgid "native code in .mpy unsupported" -msgstr "" - -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - -#: py/emitnative.c -msgid "native yield" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "ndarray length overflows" -msgstr "" - -#: py/runtime.c -#, c-format -msgid "need more than %d values to unpack" -msgstr "" - -#: py/modmath.c -msgid "negative factorial" -msgstr "" - -#: py/objint_longlong.c py/objint_mpz.c py/runtime.c -msgid "negative power with no float support" -msgstr "" - -#: py/objint_mpz.c py/runtime.c -msgid "negative shift count" -msgstr "" - -#: shared-bindings/_pixelmap/PixelMap.c -msgid "nested index must be int" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "no SD card" -msgstr "" - -#: py/vm.c -msgid "no active exception to reraise" -msgstr "" - -#: py/compile.c -msgid "no binding for nonlocal found" -msgstr "" - -#: shared-module/msgpack/__init__.c -msgid "no default packer" -msgstr "" - -#: extmod/modrandom.c extmod/ulab/code/numpy/random/random.c -msgid "no default seed" -msgstr "" - -#: py/builtinimport.c -msgid "no module named '%q'" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "no response from SD card" -msgstr "" - -#: ports/espressif/common-hal/espcamera/Camera.c py/objobject.c py/runtime.c -msgid "no such attribute" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Connection.c -#: ports/nordic/common-hal/_bleio/Connection.c -msgid "non-UUID found in service_uuids_whitelist" -msgstr "" - -#: py/compile.c -msgid "non-default argument follows default argument" -msgstr "" - -#: py/objstr.c -msgid "non-hex digit" -msgstr "" - -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "non-zero timeout must be > 0.01" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "non-zero timeout must be >= interval" -msgstr "" - -#: shared-bindings/_bleio/UUID.c -msgid "not a 128-bit UUID" -msgstr "" - -#: py/parse.c -msgid "not a constant" -msgstr "" - -#: extmod/ulab/code/numpy/carray/carray_tools.c -msgid "not implemented for complex dtype" -msgstr "" - -#: extmod/ulab/code/numpy/bitwise.c -msgid "not supported for input types" -msgstr "" - -#: shared-bindings/i2cioexpander/IOExpander.c -msgid "num_pins must be 8 or 16" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "number of points must be at least 2" -msgstr "" - -#: py/builtinhelp.c -msgid "object " -msgstr "" - -#: py/obj.c -#, c-format -msgid "object '%s' isn't a tuple or list" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "object does not support DigitalInOut protocol" -msgstr "" - -#: py/obj.c -msgid "object doesn't support item assignment" -msgstr "" - -#: py/obj.c -msgid "object doesn't support item deletion" -msgstr "" - -#: py/obj.c -msgid "object has no len" -msgstr "" - -#: py/obj.c -msgid "object isn't subscriptable" -msgstr "" - -#: py/runtime.c -msgid "object not an iterator" -msgstr "" - -#: py/objtype.c py/runtime.c -msgid "object not callable" -msgstr "" - -#: py/sequence.c shared-bindings/displayio/Group.c -msgid "object not in sequence" -msgstr "" - -#: py/runtime.c -msgid "object not iterable" -msgstr "" - -#: py/obj.c -#, c-format -msgid "object of type '%s' has no len()" -msgstr "" - -#: py/obj.c -msgid "object with buffer protocol required" -msgstr "" - -#: supervisor/shared/web_workflow/web_workflow.c -msgid "off" -msgstr "" - -#: extmod/ulab/code/utils/utils.c -msgid "offset is too large" -msgstr "" - -#: shared-bindings/dualbank/__init__.c -msgid "offset must be >= 0" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "offset must be non-negative and no greater than buffer length" -msgstr "" - -#: ports/nordic/common-hal/audiobusio/PDMIn.c -#: ports/stm/common-hal/audiobusio/PDMIn.c -msgid "only bit_depth=16 is supported" -msgstr "" - -#: ports/stm/common-hal/audiobusio/PDMIn.c -msgid "only mono is supported" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "only ndarrays can be concatenated" -msgstr "" - -#: ports/stm/common-hal/audiobusio/PDMIn.c -msgid "only oversample=64 is supported" -msgstr "" - -#: ports/nordic/common-hal/audiobusio/PDMIn.c -#: ports/stm/common-hal/audiobusio/PDMIn.c -msgid "only sample_rate=16000 is supported" -msgstr "" - -#: py/objarray.c py/objstr.c py/objstrunicode.c py/objtuple.c -#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c -#: shared-bindings/nvm/ByteArray.c -msgid "only slices with step=1 (aka None) are supported" -msgstr "" - -#: py/vm.c -msgid "opcode" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: expecting %q" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: must not be zero" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: out of range" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: undefined label '%q'" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: unknown register" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q': expecting %d arguments" -msgstr "" - -#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/bitwise.c -#: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/vector.c -msgid "operands could not be broadcast together" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "operation is defined for 2D arrays only" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "operation is defined for ndarrays only" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "operation is implemented for 1D Boolean arrays only" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "operation is not implemented on ndarrays" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "operation is not supported for given type" -msgstr "" - -#: extmod/ulab/code/ndarray_operators.c -msgid "operation not supported for the input types" -msgstr "" - -#: py/modbuiltins.c -msgid "ord expects a character" -msgstr "" - -#: py/modbuiltins.c -#, c-format -msgid "ord() expected a character, but string of length %d found" -msgstr "" - -#: extmod/ulab/code/utils/utils.c -msgid "out array is too small" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "out has wrong type" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "out keyword is not supported for complex dtype" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "out keyword is not supported for function" -msgstr "" - -#: extmod/ulab/code/utils/utils.c -msgid "out must be a float dense array" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "out must be an ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "out must be of float dtype" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "out of range of target" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "output array has wrong type" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "output array must be contiguous" -msgstr "" - -#: py/objint_mpz.c -msgid "overflow converting long int to machine word" -msgstr "" - -#: py/modstruct.c -#, c-format -msgid "pack expected %d items for packing (got %d)" -msgstr "" - -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - -#: py/emitinlinerv32.c -msgid "parameters must be registers in sequence a0 to a3" -msgstr "" - -#: py/emitinlinextensa.c -msgid "parameters must be registers in sequence a2 to a5" -msgstr "" - -#: py/emitinlinethumb.c -msgid "parameters must be registers in sequence r0 to r3" -msgstr "" - -#: extmod/vfs_posix_file.c -msgid "poll on file not available on win32" -msgstr "" - -#: ports/espressif/common-hal/pulseio/PulseIn.c -msgid "pop from an empty PulseIn" -msgstr "" - -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -#: ports/cxd56/common-hal/pulseio/PulseIn.c -#: ports/nordic/common-hal/pulseio/PulseIn.c -#: ports/raspberrypi/common-hal/pulseio/PulseIn.c -#: ports/stm/common-hal/pulseio/PulseIn.c py/objdict.c py/objlist.c py/objset.c -#: shared-bindings/ps2io/Ps2.c -msgid "pop from empty %q" -msgstr "" - -#: shared-bindings/socketpool/Socket.c -msgid "port must be >= 0" -msgstr "" - -#: py/compile.c -msgid "positional arg after **" -msgstr "" - -#: py/compile.c -msgid "positional arg after keyword arg" -msgstr "" - -#: py/objint_mpz.c -msgid "pow() 3rd argument cannot be 0" -msgstr "" - -#: py/objint_mpz.c -msgid "pow() with 3 arguments requires integers" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "pull masks conflict with direction masks" -msgstr "" - -#: extmod/ulab/code/numpy/fft/fft_tools.c -msgid "real and imaginary parts must be of equal length" -msgstr "" - -#: py/builtinimport.c -msgid "relative import" -msgstr "" - -#: py/obj.c -#, c-format -msgid "requested length %d but object has length %d" -msgstr "" - -#: extmod/ulab/code/ndarray_operators.c -msgid "results cannot be cast to specified type" -msgstr "" - -#: py/compile.c -msgid "return annotation must be an identifier" -msgstr "" - -#: py/emitnative.c -msgid "return expected '%q' but got '%q'" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "rgb_pins[%d] duplicates another pin assignment" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "rgb_pins[%d] is not on the same port as clock" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "roll argument must be an ndarray" -msgstr "" - -#: py/objstr.c -msgid "rsplit(None,n)" -msgstr "" - -#: shared-bindings/audiofreeverb/Freeverb.c -msgid "samples_signed must be true" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c -#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c -msgid "sampling rate out of range" -msgstr "" - -#: py/modmicropython.c -msgid "schedule queue full" -msgstr "" - -#: py/builtinimport.c -msgid "script compilation not supported" -msgstr "" - -#: py/nativeglue.c -msgid "set unsupported" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "shape must be None, and integer or a tuple of integers" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "shape must be integer or tuple of integers" -msgstr "" - -#: shared-module/msgpack/__init__.c -msgid "short read" -msgstr "" - -#: py/objstr.c -msgid "sign not allowed in string format specifier" -msgstr "" - -#: py/objstr.c -msgid "sign not allowed with integer format specifier 'c'" -msgstr "" - -#: extmod/ulab/code/ulab_tools.c -msgid "size is defined for ndarrays only" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "size must match out.shape when used together" -msgstr "" - -#: py/nativeglue.c -msgid "slice unsupported" -msgstr "" - -#: py/objint.c py/sequence.c -msgid "small int overflow" -msgstr "" - -#: main.c -msgid "soft reboot\n" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "sort argument must be an ndarray" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "sos array must be of shape (n_section, 6)" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "sos[:, 3] should be all ones" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "sosfilt requires iterable arguments" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "source palette too large" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "source_bitmap must have value_count of 2 or 65536" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "source_bitmap must have value_count of 65536" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "source_bitmap must have value_count of 8" -msgstr "" - -#: extmod/modre.c -msgid "splitting with sub-captures" -msgstr "" - -#: shared-bindings/random/__init__.c -msgid "stop not reachable from start" -msgstr "" - -#: py/stream.c shared-bindings/getpass/__init__.c -msgid "stream operation not supported" -msgstr "" - -#: py/objarray.c py/objstr.c -msgid "string argument without an encoding" -msgstr "" - -#: py/objstrunicode.c -msgid "string index out of range" -msgstr "" - -#: py/objstrunicode.c -#, c-format -msgid "string indices must be integers, not %s" -msgstr "" - -#: py/objarray.c py/objstr.c -msgid "substring not found" -msgstr "" - -#: py/compile.c -msgid "super() can't find self" -msgstr "" - -#: extmod/modjson.c -msgid "syntax error in JSON" -msgstr "" - -#: extmod/modtime.c -msgid "ticks interval overflow" -msgstr "" - -#: ports/nordic/common-hal/watchdog/WatchDogTimer.c -msgid "timeout duration exceeded the maximum supported value" -msgstr "" - -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "timeout must be < 655.35 secs" -msgstr "" - -#: ports/raspberrypi/common-hal/floppyio/__init__.c -msgid "timeout waiting for flux" -msgstr "" - -#: ports/raspberrypi/common-hal/floppyio/__init__.c -#: shared-module/floppyio/__init__.c -msgid "timeout waiting for index pulse" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "timeout waiting for v1 card" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "timeout waiting for v2 card" -msgstr "" - -#: ports/stm/common-hal/pwmio/PWMOut.c -msgid "timer re-init" -msgstr "" - -#: shared-bindings/time/__init__.c -msgid "timestamp out of range for platform time_t" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "tobytes can be invoked for dense arrays only" -msgstr "" - -#: py/compile.c -msgid "too many args" -msgstr "" - -#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/create.c -msgid "too many dimensions" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "too many indices" -msgstr "" - -#: py/asmthumb.c -msgid "too many locals for native method" -msgstr "" - -#: py/runtime.c -#, c-format -msgid "too many values to unpack (expected %d)" -msgstr "" - -#: extmod/ulab/code/numpy/approx.c -msgid "trapz is defined for 1D arrays of equal length" -msgstr "" - -#: extmod/ulab/code/numpy/approx.c -msgid "trapz is defined for 1D iterables" -msgstr "" - -#: py/obj.c -msgid "tuple/list has wrong length" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -#, c-format -msgid "twai_driver_install returned esp-idf error #%d" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -#, c-format -msgid "twai_start returned esp-idf error #%d" -msgstr "" - -#: shared-bindings/busio/UART.c shared-bindings/canio/CAN.c -msgid "tx and rx cannot both be None" -msgstr "" - -#: py/objtype.c -msgid "type '%q' isn't an acceptable base type" -msgstr "" - -#: py/objtype.c -msgid "type isn't an acceptable base type" -msgstr "" - -#: py/runtime.c -msgid "type object '%q' has no attribute '%q'" -msgstr "" - -#: py/objtype.c -msgid "type takes 1 or 3 arguments" -msgstr "" - -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - -#: py/parse.c -msgid "unexpected indent" -msgstr "" - -#: py/bc.c -msgid "unexpected keyword argument" -msgstr "" - -#: py/argcheck.c py/bc.c py/objnamedtuple.c -#: shared-bindings/traceback/__init__.c -msgid "unexpected keyword argument '%q'" -msgstr "" - -#: py/lexer.c -msgid "unicode name escapes" -msgstr "" - -#: py/parse.c -msgid "unindent doesn't match any outer indent level" -msgstr "" - -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - -#: py/objstr.c -#, c-format -msgid "unknown conversion specifier %c" -msgstr "" - -#: py/objstr.c -msgid "unknown format code '%c' for object of type '%q'" -msgstr "" - -#: py/compile.c -msgid "unknown type" -msgstr "" - -#: py/compile.c -msgid "unknown type '%q'" -msgstr "" - -#: py/objstr.c -#, c-format -msgid "unmatched '%c' in format" -msgstr "" - -#: py/objtype.c py/runtime.c -msgid "unreadable attribute" -msgstr "" - -#: shared-bindings/displayio/TileGrid.c shared-bindings/terminalio/Terminal.c -#: shared-bindings/tilepalettemapper/TilePaletteMapper.c -#: shared-bindings/vectorio/VectorShape.c -msgid "unsupported %q type" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "unsupported Thumb instruction '%s' with %d arguments" -msgstr "" - -#: py/emitinlinextensa.c -#, c-format -msgid "unsupported Xtensa instruction '%s' with %d arguments" -msgstr "" - -#: shared-module/bitmapfilter/__init__.c -msgid "unsupported bitmap depth" -msgstr "" - -#: shared-module/gifio/GifWriter.c -msgid "unsupported colorspace for GifWriter" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "unsupported colorspace for dither" -msgstr "" - -#: py/objstr.c -#, c-format -msgid "unsupported format character '%c' (0x%x) at index %d" -msgstr "" - -#: py/runtime.c -msgid "unsupported type for %q: '%s'" -msgstr "" - -#: py/runtime.c -msgid "unsupported type for operator" -msgstr "" - -#: py/runtime.c -msgid "unsupported types for %q: '%q', '%q'" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "usecols is too high" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "usecols keyword must be specified" -msgstr "" - -#: py/objint.c -#, c-format -msgid "value must fit in %d byte(s)" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "value out of range of target" -msgstr "" - -#: extmod/moddeflate.c -msgid "wbits" -msgstr "" - -#: shared-bindings/bitmapfilter/__init__.c -msgid "" -"weights must be a sequence with an odd square number of elements (usually 9 " -"or 25)" -msgstr "" - -#: shared-bindings/bitmapfilter/__init__.c -msgid "weights must be an object of type %q, %q, %q, or %q, not %q " -msgstr "" - -#: shared-bindings/is31fl3741/FrameBuffer.c -msgid "width must be greater than zero" -msgstr "" - -#: ports/raspberrypi/common-hal/wifi/Monitor.c -msgid "wifi.Monitor not available" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "window must be <= interval" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "wrong axis index" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "wrong axis specified" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "wrong dtype" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c -msgid "wrong index type" -msgstr "" - -#: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/create.c -#: extmod/ulab/code/numpy/io/io.c extmod/ulab/code/numpy/transform.c -#: extmod/ulab/code/numpy/vector.c -msgid "wrong input type" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c -msgid "wrong length of condition array" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c -msgid "wrong length of index array" -msgstr "" - -#: extmod/ulab/code/numpy/create.c py/objarray.c py/objstr.c -msgid "wrong number of arguments" -msgstr "" - -#: py/runtime.c -msgid "wrong number of values to unpack" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "wrong output type" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "zi must be an ndarray" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "zi must be of float type" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "zi must be of shape (n_section, 2)" -msgstr "" diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index e6a868ab21226..9065ffebcf831 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -5,5 +5,24 @@ // SPDX-License-Identifier: MIT #include "supervisor/board.h" +#include "shared-bindings/mcp4822/MCP4822.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "peripherals/pins.h" +#include "py/runtime.h" + +// board.DAC() — factory function that constructs an mcp4822.MCP4822 with +// the MTM Workshop Computer's DAC pins (GP18=SCK, GP19=SDI, GP21=CS). +static mp_obj_t board_dac_factory(void) { + mcp4822_mcp4822_obj_t *dac = mp_obj_malloc_with_finaliser( + mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); + common_hal_mcp4822_mcp4822_construct( + dac, + &pin_GPIO18, // clock (SCK) + &pin_GPIO19, // mosi (SDI) + &pin_GPIO21, // cs + 1); // gain 1x + return MP_OBJ_FROM_PTR(dac); +} +MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h deleted file mode 100644 index f9b5dd60108cf..0000000000000 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "common-hal/microcontroller/Pin.h" -#include "common-hal/rp2pio/StateMachine.h" - -#include "audio_dma.h" -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; - rp2pio_statemachine_obj_t state_machine; - audio_dma_t dma; - bool playing; -} mtm_hardware_dacout_obj_t; - -void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, - const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs, uint8_t gain); - -void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); - -void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, - mp_obj_t sample, bool loop); -void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self); - -void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self); -void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self); - -extern const mp_obj_type_t mtm_hardware_dacout_type; diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c deleted file mode 100644 index 8f875496f6745..0000000000000 --- a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c +++ /dev/null @@ -1,276 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT -// -// Python bindings for the mtm_hardware module. -// Provides DACOut: a non-blocking audio player for the MCP4822 SPI DAC. - -#include - -#include "shared/runtime/context_manager_helpers.h" -#include "py/binary.h" -#include "py/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/util.h" -#include "boards/mtm_computer/module/DACOut.h" - -// ───────────────────────────────────────────────────────────────────────────── -// DACOut class -// ───────────────────────────────────────────────────────────────────────────── - -//| class DACOut: -//| """Output audio to the MCP4822 dual-channel 12-bit SPI DAC.""" -//| -//| def __init__( -//| self, -//| clock: microcontroller.Pin, -//| mosi: microcontroller.Pin, -//| cs: microcontroller.Pin, -//| *, -//| gain: int = 1, -//| ) -> None: -//| """Create a DACOut object associated with the given SPI pins. -//| -//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin -//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin -//| :param ~microcontroller.Pin cs: The chip select (CS) pin -//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. -//| -//| Simple 8ksps 440 Hz sine wave:: -//| -//| import mtm_hardware -//| import audiocore -//| import board -//| import array -//| import time -//| import math -//| -//| length = 8000 // 440 -//| sine_wave = array.array("H", [0] * length) -//| for i in range(length): -//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) -//| -//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) -//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) -//| dac.play(sine_wave, loop=True) -//| time.sleep(1) -//| dac.stop() -//| -//| Playing a wave file from flash:: -//| -//| import board -//| import audiocore -//| import mtm_hardware -//| -//| f = open("sound.wav", "rb") -//| wav = audiocore.WaveFile(f) -//| -//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) -//| dac.play(wav) -//| while dac.playing: -//| pass""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, - }; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); - const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); - const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); - - mp_int_t gain = args[ARG_gain].u_int; - if (gain != 1 && gain != 2) { - mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("gain must be 1 or 2")); - } - - mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); - common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs, (uint8_t)gain); - - return MP_OBJ_FROM_PTR(self); -} - -static void check_for_deinit(mtm_hardware_dacout_obj_t *self) { - if (common_hal_mtm_hardware_dacout_deinited(self)) { - raise_deinited_error(); - } -} - -//| def deinit(self) -> None: -//| """Deinitialises the DACOut and releases any hardware resources for reuse.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_deinit(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_mtm_hardware_dacout_deinit(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_deinit_obj, mtm_hardware_dacout_deinit); - -//| def __enter__(self) -> DACOut: -//| """No-op used by Context Managers.""" -//| ... -//| -// Provided by context manager helper. - -//| def __exit__(self) -> None: -//| """Automatically deinitializes the hardware when exiting a context. See -//| :ref:`lifetime-and-contextmanagers` for more info.""" -//| ... -//| -// Provided by context manager helper. - -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: -//| """Plays the sample once when loop=False and continuously when loop=True. -//| Does not block. Use `playing` to block. -//| -//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. -//| -//| The sample itself should consist of 8 bit or 16 bit samples.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sample, ARG_loop }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, - }; - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - check_for_deinit(self); - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_obj_t sample = args[ARG_sample].u_obj; - common_hal_mtm_hardware_dacout_play(self, sample, args[ARG_loop].u_bool); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(mtm_hardware_dacout_play_obj, 1, mtm_hardware_dacout_obj_play); - -//| def stop(self) -> None: -//| """Stops playback.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_stop(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - common_hal_mtm_hardware_dacout_stop(self); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_stop_obj, mtm_hardware_dacout_obj_stop); - -//| playing: bool -//| """True when the audio sample is being output. (read-only)""" -//| -static mp_obj_t mtm_hardware_dacout_obj_get_playing(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_playing(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_playing_obj, mtm_hardware_dacout_obj_get_playing); - -MP_PROPERTY_GETTER(mtm_hardware_dacout_playing_obj, - (mp_obj_t)&mtm_hardware_dacout_get_playing_obj); - -//| def pause(self) -> None: -//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_pause(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - if (!common_hal_mtm_hardware_dacout_get_playing(self)) { - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Not playing")); - } - common_hal_mtm_hardware_dacout_pause(self); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_pause_obj, mtm_hardware_dacout_obj_pause); - -//| def resume(self) -> None: -//| """Resumes sample playback after :py:func:`pause`.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_resume(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - if (common_hal_mtm_hardware_dacout_get_paused(self)) { - common_hal_mtm_hardware_dacout_resume(self); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_resume_obj, mtm_hardware_dacout_obj_resume); - -//| paused: bool -//| """True when playback is paused. (read-only)""" -//| -static mp_obj_t mtm_hardware_dacout_obj_get_paused(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_paused(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_paused_obj, mtm_hardware_dacout_obj_get_paused); - -MP_PROPERTY_GETTER(mtm_hardware_dacout_paused_obj, - (mp_obj_t)&mtm_hardware_dacout_get_paused_obj); - -// ── DACOut type definition ─────────────────────────────────────────────────── - -static const mp_rom_map_elem_t mtm_hardware_dacout_locals_dict_table[] = { - // Methods - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, - { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mtm_hardware_dacout_play_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mtm_hardware_dacout_stop_obj) }, - { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mtm_hardware_dacout_pause_obj) }, - { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mtm_hardware_dacout_resume_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mtm_hardware_dacout_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mtm_hardware_dacout_paused_obj) }, -}; -static MP_DEFINE_CONST_DICT(mtm_hardware_dacout_locals_dict, mtm_hardware_dacout_locals_dict_table); - -MP_DEFINE_CONST_OBJ_TYPE( - mtm_hardware_dacout_type, - MP_QSTR_DACOut, - MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, - make_new, mtm_hardware_dacout_make_new, - locals_dict, &mtm_hardware_dacout_locals_dict - ); - -// ───────────────────────────────────────────────────────────────────────────── -// mtm_hardware module definition -// ───────────────────────────────────────────────────────────────────────────── - -//| """Hardware interface to Music Thing Modular Workshop Computer peripherals. -//| -//| Provides the `DACOut` class for non-blocking audio output via the -//| MCP4822 dual-channel 12-bit SPI DAC. -//| """ - -static const mp_rom_map_elem_t mtm_hardware_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mtm_hardware) }, - { MP_ROM_QSTR(MP_QSTR_DACOut), MP_ROM_PTR(&mtm_hardware_dacout_type) }, -}; - -static MP_DEFINE_CONST_DICT(mtm_hardware_module_globals, mtm_hardware_module_globals_table); - -const mp_obj_module_t mtm_hardware_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&mtm_hardware_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_mtm_hardware, mtm_hardware_module); diff --git a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk index 74d9baac879b4..45c99711d72a3 100644 --- a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk +++ b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk @@ -11,7 +11,4 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY_AUDIOEFFECTS = 1 CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_PICODVI = 0 - -SRC_C += \ - boards/$(BOARD)/module/mtm_hardware.c \ - boards/$(BOARD)/module/DACOut.c +CIRCUITPY_MCP4822 = 1 diff --git a/ports/raspberrypi/boards/mtm_computer/pins.c b/ports/raspberrypi/boards/mtm_computer/pins.c index 6987818233102..ffdf2687702c6 100644 --- a/ports/raspberrypi/boards/mtm_computer/pins.c +++ b/ports/raspberrypi/boards/mtm_computer/pins.c @@ -6,6 +6,8 @@ #include "shared-bindings/board/__init__.h" +extern const mp_obj_fun_builtin_fixed_t board_dac_obj; + static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS @@ -21,7 +23,6 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_PULSE_2_IN), MP_ROM_PTR(&pin_GPIO3) }, { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_NORM_PROBE), MP_ROM_PTR(&pin_GPIO4) }, { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, @@ -105,6 +106,9 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, + // Factory function: dac = board.DAC() returns a configured mcp4822.MCP4822 + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&board_dac_obj) }, + // { MP_ROM_QSTR(MP_QSTR_EEPROM_I2C), MP_ROM_PTR(&board_i2c_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c similarity index 75% rename from ports/raspberrypi/boards/mtm_computer/module/DACOut.c rename to ports/raspberrypi/common-hal/mcp4822/MCP4822.c index fb4ce37d4d0ff..cb6cddb8343ed 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c +++ b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// MCP4822 dual-channel 12-bit SPI DAC driver for the MTM Workshop Computer. +// MCP4822 dual-channel 12-bit SPI DAC audio output. // Uses PIO + DMA for non-blocking audio playback, mirroring audiobusio.I2SOut. #include @@ -15,7 +15,8 @@ #include "py/gc.h" #include "py/mperrno.h" #include "py/runtime.h" -#include "boards/mtm_computer/module/DACOut.h" +#include "common-hal/mcp4822/MCP4822.h" +#include "shared-bindings/mcp4822/MCP4822.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/audiocore/__init__.h" #include "bindings/rp2pio/StateMachine.h" @@ -29,16 +30,14 @@ // SET pins (N) = MOSI through CS — for CS control & command-bit injection // SIDE-SET pin (1) = SCK — serial clock // -// On the MTM Workshop Computer: MOSI=GP19, CS=GP21, SCK=GP18. -// The SET group spans GP19..GP21 (3 pins). GP20 is unused and driven low. -// -// SET PINS bit mapping (bit0=MOSI/GP19, bit1=GP20, bit2=CS/GP21): -// 0 = CS low, MOSI low 1 = CS low, MOSI high 4 = CS high, MOSI low +// SET PINS bit mapping (bit0=MOSI, ..., bit N=CS): +// 0 = CS low, MOSI low 1 = CS low, MOSI high +// (1 << cs_bit_pos) = CS high, MOSI low // // SIDE-SET (1 pin, SCK): side 0 = SCK low, side 1 = SCK high // // MCP4822 16-bit command word: -// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x) +// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x, 0=2x) // [12] output enable (1) [11:0] 12-bit data // // DMA feeds unsigned 16-bit audio samples. RP2040 narrow-write replication @@ -46,8 +45,8 @@ // giving mono→stereo for free. // // The PIO pulls 32 bits, then sends two SPI transactions: -// Channel A: cmd nibble 0b0011, then all 16 sample bits from upper half-word -// Channel B: cmd nibble 0b1011, then all 16 sample bits from lower half-word +// Channel A: cmd nibble, then all 16 sample bits from upper half-word +// Channel B: cmd nibble, then all 16 sample bits from lower half-word // The MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data), // so only the top 12 of the 16 sample bits become DAC data. The bottom // 4 sample bits clock out harmlessly after the DAC has latched. @@ -66,11 +65,7 @@ static const uint16_t mcp4822_pio_program[] = { // 1: mov x, osr side 0 ; Save for pull-noblock fallback 0xA027, - // ── Channel A: command nibble 0b0011 ────────────────────────────────── - // Send 4 cmd bits via SET, then all 16 sample bits via OUT. - // MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data); - // the extra 4 clocks shift out the LSBs which the DAC ignores. - // This gives correct 16→12 bit scaling (top 12 bits become DAC data). + // ── Channel A: command nibble 0b0011 (1x gain) ──────────────────────── // 2: set pins, 0 side 0 ; CS low, MOSI=0 (bit15=0: channel A) 0xE000, // 3: nop side 1 ; SCK high — latch bit 15 @@ -79,7 +74,7 @@ static const uint16_t mcp4822_pio_program[] = { 0xE000, // 5: nop side 1 ; SCK high 0xB042, - // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) [patched for 2x] 0xE001, // 7: nop side 1 ; SCK high 0xB042, @@ -96,7 +91,7 @@ static const uint16_t mcp4822_pio_program[] = { // 13: set pins, 4 side 0 ; CS high — DAC A latches 0xE004, - // ── Channel B: command nibble 0b1011 ────────────────────────────────── + // ── Channel B: command nibble 0b1011 (1x gain) ──────────────────────── // 14: set pins, 1 side 0 ; CS low, MOSI=1 (bit15=1: channel B) 0xE001, // 15: nop side 1 ; SCK high @@ -105,7 +100,7 @@ static const uint16_t mcp4822_pio_program[] = { 0xE000, // 17: nop side 1 ; SCK high 0xB042, - // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) [patched for 2x] 0xE001, // 19: nop side 1 ; SCK high 0xB042, @@ -127,7 +122,6 @@ static const uint16_t mcp4822_pio_program[] = { // Per channel: 8(4 cmd bits × 2 clks) + 1(set y) + 32(16 bits × 2 clks) + 1(cs high) = 42 #define MCP4822_CLOCKS_PER_SAMPLE 86 - // MCP4822 gain bit (bit 13) position in the PIO program: // Instruction 6 = channel A gain bit // Instruction 18 = channel B gain bit @@ -138,15 +132,19 @@ static const uint16_t mcp4822_pio_program[] = { #define MCP4822_PIO_GAIN_1X 0xE001 // set pins, 1 #define MCP4822_PIO_GAIN_2X 0xE000 // set pins, 0 -void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, +void mcp4822_reset(void) { +} + +// Caller validates that pins are free. +void common_hal_mcp4822_mcp4822_construct(mcp4822_mcp4822_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *cs, uint8_t gain) { - // SET pins span from MOSI to CS. MOSI must have a lower GPIO number - // than CS, with at most 4 pins between them (SET count max is 5). + // The SET pin group spans from MOSI to CS. + // MOSI must have a lower GPIO number than CS, gap at most 4. if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { mp_raise_ValueError( - MP_COMPRESSED_ROM_TEXT("cs pin must be 1-4 positions above mosi pin")); + MP_ERROR_TEXT("cs pin must be 1-4 positions above mosi pin")); } uint8_t set_count = cs->number - mosi->number + 1; @@ -197,26 +195,26 @@ void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, audio_dma_init(&self->dma); } -bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self) { +bool common_hal_mcp4822_mcp4822_deinited(mcp4822_mcp4822_obj_t *self) { return common_hal_rp2pio_statemachine_deinited(&self->state_machine); } -void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self) { - if (common_hal_mtm_hardware_dacout_deinited(self)) { +void common_hal_mcp4822_mcp4822_deinit(mcp4822_mcp4822_obj_t *self) { + if (common_hal_mcp4822_mcp4822_deinited(self)) { return; } - if (common_hal_mtm_hardware_dacout_get_playing(self)) { - common_hal_mtm_hardware_dacout_stop(self); + if (common_hal_mcp4822_mcp4822_get_playing(self)) { + common_hal_mcp4822_mcp4822_stop(self); } common_hal_rp2pio_statemachine_deinit(&self->state_machine); audio_dma_deinit(&self->dma); } -void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, +void common_hal_mcp4822_mcp4822_play(mcp4822_mcp4822_obj_t *self, mp_obj_t sample, bool loop) { - if (common_hal_mtm_hardware_dacout_get_playing(self)) { - common_hal_mtm_hardware_dacout_stop(self); + if (common_hal_mcp4822_mcp4822_get_playing(self)) { + common_hal_mcp4822_mcp4822_stop(self); } uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); @@ -227,7 +225,7 @@ void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, uint32_t sample_rate = audiosample_get_sample_rate(sample); uint8_t channel_count = audiosample_get_channel_count(sample); if (channel_count > 2) { - mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("Too many channels in sample.")); + mp_raise_ValueError(MP_ERROR_TEXT("Too many channels in sample.")); } // PIO clock = sample_rate × clocks_per_sample @@ -236,10 +234,6 @@ void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, (uint32_t)sample_rate * MCP4822_CLOCKS_PER_SAMPLE); common_hal_rp2pio_statemachine_restart(&self->state_machine); - // DMA feeds unsigned 16-bit samples. The PIO discards the top 4 bits - // of each 16-bit half and uses the remaining 12 as DAC data. - // RP2040 narrow-write replication: 16-bit DMA write → same value in - // both 32-bit FIFO halves → mono-to-stereo for free. audio_dma_result result = audio_dma_setup_playback( &self->dma, sample, @@ -253,41 +247,41 @@ void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, false); // swap_channel if (result == AUDIO_DMA_DMA_BUSY) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("No DMA channel found")); + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("No DMA channel found")); } else if (result == AUDIO_DMA_MEMORY_ERROR) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Unable to allocate buffers for signed conversion")); + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("Unable to allocate buffers for signed conversion")); } else if (result == AUDIO_DMA_SOURCE_ERROR) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Audio source error")); + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("Audio source error")); } self->playing = true; } -void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self) { +void common_hal_mcp4822_mcp4822_pause(mcp4822_mcp4822_obj_t *self) { audio_dma_pause(&self->dma); } -void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self) { +void common_hal_mcp4822_mcp4822_resume(mcp4822_mcp4822_obj_t *self) { audio_dma_resume(&self->dma); } -bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self) { +bool common_hal_mcp4822_mcp4822_get_paused(mcp4822_mcp4822_obj_t *self) { return audio_dma_get_paused(&self->dma); } -void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self) { +void common_hal_mcp4822_mcp4822_stop(mcp4822_mcp4822_obj_t *self) { audio_dma_stop(&self->dma); common_hal_rp2pio_statemachine_stop(&self->state_machine); self->playing = false; } -bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self) { +bool common_hal_mcp4822_mcp4822_get_playing(mcp4822_mcp4822_obj_t *self) { bool playing = audio_dma_get_playing(&self->dma); if (!playing && self->playing) { - common_hal_mtm_hardware_dacout_stop(self); + common_hal_mcp4822_mcp4822_stop(self); } return playing; } diff --git a/ports/raspberrypi/common-hal/mcp4822/MCP4822.h b/ports/raspberrypi/common-hal/mcp4822/MCP4822.h new file mode 100644 index 0000000000000..53c8e862b63c5 --- /dev/null +++ b/ports/raspberrypi/common-hal/mcp4822/MCP4822.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/rp2pio/StateMachine.h" + +#include "audio_dma.h" +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + rp2pio_statemachine_obj_t state_machine; + audio_dma_t dma; + bool playing; +} mcp4822_mcp4822_obj_t; + +void mcp4822_reset(void); diff --git a/ports/raspberrypi/common-hal/mcp4822/__init__.c b/ports/raspberrypi/common-hal/mcp4822/__init__.c new file mode 100644 index 0000000000000..48981fc88a713 --- /dev/null +++ b/ports/raspberrypi/common-hal/mcp4822/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +// No module-level init needed. diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 8401c5d75453a..30fc75c106b54 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -16,6 +16,7 @@ CIRCUITPY_HASHLIB ?= 1 CIRCUITPY_HASHLIB_MBEDTLS ?= 1 CIRCUITPY_IMAGECAPTURE ?= 1 CIRCUITPY_MAX3421E ?= 0 +CIRCUITPY_MCP4822 ?= 0 CIRCUITPY_MEMORYMAP ?= 1 CIRCUITPY_PWMIO ?= 1 CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_DISPLAYIO) diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 886ba96f3e5fa..14c8c52802e6e 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -288,6 +288,9 @@ endif ifeq ($(CIRCUITPY_MAX3421E),1) SRC_PATTERNS += max3421e/% endif +ifeq ($(CIRCUITPY_MCP4822),1) +SRC_PATTERNS += mcp4822/% +endif ifeq ($(CIRCUITPY_MDNS),1) SRC_PATTERNS += mdns/% endif @@ -532,6 +535,8 @@ SRC_COMMON_HAL_ALL = \ i2ctarget/I2CTarget.c \ i2ctarget/__init__.c \ max3421e/Max3421E.c \ + mcp4822/__init__.c \ + mcp4822/MCP4822.c \ memorymap/__init__.c \ memorymap/AddressRange.c \ microcontroller/__init__.c \ diff --git a/shared-bindings/mcp4822/MCP4822.c b/shared-bindings/mcp4822/MCP4822.c new file mode 100644 index 0000000000000..c48f5426e6690 --- /dev/null +++ b/shared-bindings/mcp4822/MCP4822.c @@ -0,0 +1,247 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/mcp4822/MCP4822.h" +#include "shared-bindings/util.h" + +//| class MCP4822: +//| """Output audio to an MCP4822 dual-channel 12-bit SPI DAC.""" +//| +//| def __init__( +//| self, +//| clock: microcontroller.Pin, +//| mosi: microcontroller.Pin, +//| cs: microcontroller.Pin, +//| *, +//| gain: int = 1, +//| ) -> None: +//| """Create an MCP4822 object associated with the given SPI pins. +//| +//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin +//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin +//| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. +//| +//| Simple 8ksps 440 Hz sine wave:: +//| +//| import mcp4822 +//| import audiocore +//| import board +//| import array +//| import time +//| import math +//| +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac = mcp4822.MCP4822(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import mcp4822 +//| +//| f = open("sound.wav", "rb") +//| wav = audiocore.WaveFile(f) +//| +//| dac = mcp4822.MCP4822(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(wav) +//| while dac.playing: +//| pass""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + + mp_int_t gain = args[ARG_gain].u_int; + if (gain != 1 && gain != 2) { + mp_raise_ValueError(MP_ERROR_TEXT("gain must be 1 or 2")); + } + + mcp4822_mcp4822_obj_t *self = mp_obj_malloc_with_finaliser(mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); + common_hal_mcp4822_mcp4822_construct(self, clock, mosi, cs, (uint8_t)gain); + + return MP_OBJ_FROM_PTR(self); +} + +static void check_for_deinit(mcp4822_mcp4822_obj_t *self) { + if (common_hal_mcp4822_mcp4822_deinited(self)) { + raise_deinited_error(); + } +} + +//| def deinit(self) -> None: +//| """Deinitialises the MCP4822 and releases any hardware resources for reuse.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_deinit(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_mcp4822_mcp4822_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_deinit_obj, mcp4822_mcp4822_deinit); + +//| def __enter__(self) -> MCP4822: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +// Provided by context manager helper. + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 8 bit or 16 bit samples.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_mcp4822_mcp4822_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mcp4822_mcp4822_play_obj, 1, mcp4822_mcp4822_obj_play); + +//| def stop(self) -> None: +//| """Stops playback.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_stop(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_mcp4822_mcp4822_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_stop_obj, mcp4822_mcp4822_obj_stop); + +//| playing: bool +//| """True when the audio sample is being output. (read-only)""" +//| +static mp_obj_t mcp4822_mcp4822_obj_get_playing(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mcp4822_mcp4822_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_get_playing_obj, mcp4822_mcp4822_obj_get_playing); + +MP_PROPERTY_GETTER(mcp4822_mcp4822_playing_obj, + (mp_obj_t)&mcp4822_mcp4822_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_pause(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (!common_hal_mcp4822_mcp4822_get_playing(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Not playing")); + } + common_hal_mcp4822_mcp4822_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_pause_obj, mcp4822_mcp4822_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_resume(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (common_hal_mcp4822_mcp4822_get_paused(self)) { + common_hal_mcp4822_mcp4822_resume(self); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_resume_obj, mcp4822_mcp4822_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +//| +static mp_obj_t mcp4822_mcp4822_obj_get_paused(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mcp4822_mcp4822_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_get_paused_obj, mcp4822_mcp4822_obj_get_paused); + +MP_PROPERTY_GETTER(mcp4822_mcp4822_paused_obj, + (mp_obj_t)&mcp4822_mcp4822_get_paused_obj); + +static const mp_rom_map_elem_t mcp4822_mcp4822_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mcp4822_mcp4822_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mcp4822_mcp4822_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mcp4822_mcp4822_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mcp4822_mcp4822_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mcp4822_mcp4822_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mcp4822_mcp4822_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mcp4822_mcp4822_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mcp4822_mcp4822_paused_obj) }, +}; +static MP_DEFINE_CONST_DICT(mcp4822_mcp4822_locals_dict, mcp4822_mcp4822_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mcp4822_mcp4822_type, + MP_QSTR_MCP4822, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, mcp4822_mcp4822_make_new, + locals_dict, &mcp4822_mcp4822_locals_dict + ); diff --git a/shared-bindings/mcp4822/MCP4822.h b/shared-bindings/mcp4822/MCP4822.h new file mode 100644 index 0000000000000..b129aec306124 --- /dev/null +++ b/shared-bindings/mcp4822/MCP4822.h @@ -0,0 +1,25 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/mcp4822/MCP4822.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t mcp4822_mcp4822_type; + +void common_hal_mcp4822_mcp4822_construct(mcp4822_mcp4822_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs, uint8_t gain); + +void common_hal_mcp4822_mcp4822_deinit(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_deinited(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_play(mcp4822_mcp4822_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_mcp4822_mcp4822_stop(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_get_playing(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_pause(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_resume(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_get_paused(mcp4822_mcp4822_obj_t *self); diff --git a/shared-bindings/mcp4822/__init__.c b/shared-bindings/mcp4822/__init__.c new file mode 100644 index 0000000000000..bac2136d9e7c0 --- /dev/null +++ b/shared-bindings/mcp4822/__init__.c @@ -0,0 +1,36 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/mcp4822/__init__.h" +#include "shared-bindings/mcp4822/MCP4822.h" + +//| """Audio output via MCP4822 dual-channel 12-bit SPI DAC. +//| +//| The `mcp4822` module provides the `MCP4822` class for non-blocking +//| audio playback through the Microchip MCP4822 SPI DAC using PIO and DMA. +//| +//| All classes change hardware state and should be deinitialized when they +//| are no longer needed. To do so, either call :py:meth:`!deinit` or use a +//| context manager.""" + +static const mp_rom_map_elem_t mcp4822_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mcp4822) }, + { MP_ROM_QSTR(MP_QSTR_MCP4822), MP_ROM_PTR(&mcp4822_mcp4822_type) }, +}; + +static MP_DEFINE_CONST_DICT(mcp4822_module_globals, mcp4822_module_globals_table); + +const mp_obj_module_t mcp4822_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mcp4822_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mcp4822, mcp4822_module); diff --git a/shared-bindings/mcp4822/__init__.h b/shared-bindings/mcp4822/__init__.h new file mode 100644 index 0000000000000..c4a52e5819d12 --- /dev/null +++ b/shared-bindings/mcp4822/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once From 3b9eaf9f2788ecbbc12d0c0cdbe6f2ee7eb04cc0 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Sat, 21 Mar 2026 10:13:02 -0700 Subject: [PATCH 435/517] mtm_computer: Add DAC audio out module --- .../boards/mtm_computer/module/DACOut.c | 276 ++++++++++++++++++ .../boards/mtm_computer/module/DACOut.h | 38 +++ .../boards/mtm_computer/mpconfigboard.mk | 4 + 3 files changed, 318 insertions(+) create mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.c create mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.h diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c new file mode 100644 index 0000000000000..84f4296cb00cd --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c @@ -0,0 +1,276 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT +// +// MCP4822 dual-channel 12-bit SPI DAC driver for the MTM Workshop Computer. +// Uses PIO + DMA for non-blocking audio playback, mirroring audiobusio.I2SOut. + +#include +#include + +#include "mpconfigport.h" + +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "boards/mtm_computer/module/DACOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/audiocore/__init__.h" +#include "bindings/rp2pio/StateMachine.h" + +// ───────────────────────────────────────────────────────────────────────────── +// PIO program for MCP4822 SPI DAC +// ───────────────────────────────────────────────────────────────────────────── +// +// Pin assignment: +// OUT pin (1) = MOSI — serial data out +// SET pins (N) = MOSI through CS — for CS control & command-bit injection +// SIDE-SET pin (1) = SCK — serial clock +// +// On the MTM Workshop Computer: MOSI=GP19, CS=GP21, SCK=GP18. +// The SET group spans GP19..GP21 (3 pins). GP20 is unused and driven low. +// +// SET PINS bit mapping (bit0=MOSI/GP19, bit1=GP20, bit2=CS/GP21): +// 0 = CS low, MOSI low 1 = CS low, MOSI high 4 = CS high, MOSI low +// +// SIDE-SET (1 pin, SCK): side 0 = SCK low, side 1 = SCK high +// +// MCP4822 16-bit command word: +// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x) +// [12] output enable (1) [11:0] 12-bit data +// +// DMA feeds unsigned 16-bit audio samples. RP2040 narrow-write replication +// fills both halves of the 32-bit PIO FIFO entry with the same value, +// giving mono→stereo for free. +// +// The PIO pulls 32 bits, then sends two SPI transactions: +// Channel A: cmd nibble 0b0011, then all 16 sample bits from upper half-word +// Channel B: cmd nibble 0b1011, then all 16 sample bits from lower half-word +// The MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data), +// so only the top 12 of the 16 sample bits become DAC data. The bottom +// 4 sample bits clock out harmlessly after the DAC has latched. +// This gives correct 16-bit → 12-bit scaling (effectively sample >> 4). +// +// PIO instruction encoding with .side_set 1 (no opt): +// [15:13] opcode [12] side-set [11:8] delay [7:0] operands +// +// Total: 26 instructions, 86 PIO clocks per audio sample. +// ───────────────────────────────────────────────────────────────────────────── + +static const uint16_t mcp4822_pio_program[] = { + // side SCK + // 0: pull noblock side 0 ; Get 32 bits or keep X if FIFO empty + 0x8080, + // 1: mov x, osr side 0 ; Save for pull-noblock fallback + 0xA027, + + // ── Channel A: command nibble 0b0011 ────────────────────────────────── + // Send 4 cmd bits via SET, then all 16 sample bits via OUT. + // MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data); + // the extra 4 clocks shift out the LSBs which the DAC ignores. + // This gives correct 16→12 bit scaling (top 12 bits become DAC data). + // 2: set pins, 0 side 0 ; CS low, MOSI=0 (bit15=0: channel A) + 0xE000, + // 3: nop side 1 ; SCK high — latch bit 15 + 0xB042, + // 4: set pins, 0 side 0 ; MOSI=0 (bit14=0: don't care) + 0xE000, + // 5: nop side 1 ; SCK high + 0xB042, + // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + 0xE001, + // 7: nop side 1 ; SCK high + 0xB042, + // 8: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) + 0xE001, + // 9: nop side 1 ; SCK high + 0xB042, + // 10: set y, 15 side 0 ; Loop counter: 16 sample bits + 0xE04F, + // 11: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopA) + 0x6001, + // 12: jmp y--, 11 side 1 ; SCK high, loop back + 0x108B, + // 13: set pins, 4 side 0 ; CS high — DAC A latches + 0xE004, + + // ── Channel B: command nibble 0b1011 ────────────────────────────────── + // 14: set pins, 1 side 0 ; CS low, MOSI=1 (bit15=1: channel B) + 0xE001, + // 15: nop side 1 ; SCK high + 0xB042, + // 16: set pins, 0 side 0 ; MOSI=0 (bit14=0) + 0xE000, + // 17: nop side 1 ; SCK high + 0xB042, + // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + 0xE001, + // 19: nop side 1 ; SCK high + 0xB042, + // 20: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) + 0xE001, + // 21: nop side 1 ; SCK high + 0xB042, + // 22: set y, 15 side 0 ; Loop counter: 16 sample bits + 0xE04F, + // 23: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopB) + 0x6001, + // 24: jmp y--, 23 side 1 ; SCK high, loop back + 0x1097, + // 25: set pins, 4 side 0 ; CS high — DAC B latches + 0xE004, +}; + +// Clocks per sample: 2 (pull+mov) + 42 (chanA) + 42 (chanB) = 86 +// Per channel: 8(4 cmd bits × 2 clks) + 1(set y) + 32(16 bits × 2 clks) + 1(cs high) = 42 +#define MCP4822_CLOCKS_PER_SAMPLE 86 + + +void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs) { + + // SET pins span from MOSI to CS. MOSI must have a lower GPIO number + // than CS, with at most 4 pins between them (SET count max is 5). + if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { + mp_raise_ValueError( + MP_COMPRESSED_ROM_TEXT("cs pin must be 1-4 positions above mosi pin")); + } + + uint8_t set_count = cs->number - mosi->number + 1; + + // Initial SET pin state: CS high (bit at CS position), others low + uint32_t cs_bit_position = cs->number - mosi->number; + pio_pinmask32_t initial_set_state = PIO_PINMASK32_FROM_VALUE(1u << cs_bit_position); + pio_pinmask32_t initial_set_dir = PIO_PINMASK32_FROM_VALUE((1u << set_count) - 1); + + common_hal_rp2pio_statemachine_construct( + &self->state_machine, + mcp4822_pio_program, MP_ARRAY_SIZE(mcp4822_pio_program), + 44100 * MCP4822_CLOCKS_PER_SAMPLE, // Initial frequency; play() adjusts + NULL, 0, // No init program + NULL, 0, // No may_exec + mosi, 1, // OUT: MOSI, 1 pin + PIO_PINMASK32_NONE, PIO_PINMASK32_ALL, // OUT state=low, dir=output + NULL, 0, // IN: none + PIO_PINMASK32_NONE, PIO_PINMASK32_NONE, // IN pulls: none + mosi, set_count, // SET: MOSI..CS + initial_set_state, initial_set_dir, // SET state (CS high), dir=output + clock, 1, false, // SIDE-SET: SCK, 1 pin, not pindirs + PIO_PINMASK32_NONE, // SIDE-SET state: SCK low + PIO_PINMASK32_FROM_VALUE(0x1), // SIDE-SET dir: output + false, // No sideset enable + NULL, PULL_NONE, // No jump pin + PIO_PINMASK_NONE, // No wait GPIO + true, // Exclusive pin use + false, 32, false, // OUT shift: no autopull, 32-bit, shift left + false, // Don't wait for txstall + false, 32, false, // IN shift (unused) + false, // Not user-interruptible + 0, -1, // Wrap: whole program + PIO_ANY_OFFSET, + PIO_FIFO_TYPE_DEFAULT, + PIO_MOV_STATUS_DEFAULT, + PIO_MOV_N_DEFAULT + ); + + self->playing = false; + audio_dma_init(&self->dma); +} + +bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self) { + return common_hal_rp2pio_statemachine_deinited(&self->state_machine); +} + +void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self) { + if (common_hal_mtm_hardware_dacout_deinited(self)) { + return; + } + if (common_hal_mtm_hardware_dacout_get_playing(self)) { + common_hal_mtm_hardware_dacout_stop(self); + } + common_hal_rp2pio_statemachine_deinit(&self->state_machine); + audio_dma_deinit(&self->dma); +} + +void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, + mp_obj_t sample, bool loop) { + + if (common_hal_mtm_hardware_dacout_get_playing(self)) { + common_hal_mtm_hardware_dacout_stop(self); + } + + uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); + if (bits_per_sample < 16) { + bits_per_sample = 16; + } + + uint32_t sample_rate = audiosample_get_sample_rate(sample); + uint8_t channel_count = audiosample_get_channel_count(sample); + if (channel_count > 2) { + mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("Too many channels in sample.")); + } + + // PIO clock = sample_rate × clocks_per_sample + common_hal_rp2pio_statemachine_set_frequency( + &self->state_machine, + (uint32_t)sample_rate * MCP4822_CLOCKS_PER_SAMPLE); + common_hal_rp2pio_statemachine_restart(&self->state_machine); + + // DMA feeds unsigned 16-bit samples. The PIO discards the top 4 bits + // of each 16-bit half and uses the remaining 12 as DAC data. + // RP2040 narrow-write replication: 16-bit DMA write → same value in + // both 32-bit FIFO halves → mono-to-stereo for free. + audio_dma_result result = audio_dma_setup_playback( + &self->dma, + sample, + loop, + false, // single_channel_output + 0, // audio_channel + false, // output_signed = false (unsigned for MCP4822) + bits_per_sample, // output_resolution + (uint32_t)&self->state_machine.pio->txf[self->state_machine.state_machine], + self->state_machine.tx_dreq, + false); // swap_channel + + if (result == AUDIO_DMA_DMA_BUSY) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("No DMA channel found")); + } else if (result == AUDIO_DMA_MEMORY_ERROR) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Unable to allocate buffers for signed conversion")); + } else if (result == AUDIO_DMA_SOURCE_ERROR) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Audio source error")); + } + + self->playing = true; +} + +void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self) { + audio_dma_pause(&self->dma); +} + +void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self) { + audio_dma_resume(&self->dma); +} + +bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self) { + return audio_dma_get_paused(&self->dma); +} + +void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self) { + audio_dma_stop(&self->dma); + common_hal_rp2pio_statemachine_stop(&self->state_machine); + self->playing = false; +} + +bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self) { + bool playing = audio_dma_get_playing(&self->dma); + if (!playing && self->playing) { + common_hal_mtm_hardware_dacout_stop(self); + } + return playing; +} diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h new file mode 100644 index 0000000000000..f7c0c9eb8062c --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h @@ -0,0 +1,38 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/rp2pio/StateMachine.h" + +#include "audio_dma.h" +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + rp2pio_statemachine_obj_t state_machine; + audio_dma_t dma; + bool playing; +} mtm_hardware_dacout_obj_t; + +void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs); + +void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); + +void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, + mp_obj_t sample, bool loop); +void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self); + +void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self); +void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self); + +extern const mp_obj_type_t mtm_hardware_dacout_type; diff --git a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk index 718c393d1686f..74d9baac879b4 100644 --- a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk +++ b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk @@ -11,3 +11,7 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY_AUDIOEFFECTS = 1 CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_PICODVI = 0 + +SRC_C += \ + boards/$(BOARD)/module/mtm_hardware.c \ + boards/$(BOARD)/module/DACOut.c From f63ea5c6fdfb31bd2be26b4a202ce189a16df37e Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Sat, 21 Mar 2026 10:33:40 -0700 Subject: [PATCH 436/517] mtm_hardware.c added --- .../boards/mtm_computer/module/mtm_hardware.c | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c new file mode 100644 index 0000000000000..a3dafbcf9415a --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c @@ -0,0 +1,267 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT +// +// Python bindings for the mtm_hardware module. +// Provides DACOut: a non-blocking audio player for the MCP4822 SPI DAC. + +#include + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "boards/mtm_computer/module/DACOut.h" + +// ───────────────────────────────────────────────────────────────────────────── +// DACOut class +// ───────────────────────────────────────────────────────────────────────────── + +//| class DACOut: +//| """Output audio to the MCP4822 dual-channel 12-bit SPI DAC.""" +//| +//| def __init__( +//| self, +//| clock: microcontroller.Pin, +//| mosi: microcontroller.Pin, +//| cs: microcontroller.Pin, +//| ) -> None: +//| """Create a DACOut object associated with the given SPI pins. +//| +//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin +//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin +//| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| +//| Simple 8ksps 440 Hz sine wave:: +//| +//| import mtm_hardware +//| import audiocore +//| import board +//| import array +//| import time +//| import math +//| +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import mtm_hardware +//| +//| f = open("sound.wav", "rb") +//| wav = audiocore.WaveFile(f) +//| +//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(wav) +//| while dac.playing: +//| pass""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_clock, ARG_mosi, ARG_cs }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + + mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); + common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs); + + return MP_OBJ_FROM_PTR(self); +} + +static void check_for_deinit(mtm_hardware_dacout_obj_t *self) { + if (common_hal_mtm_hardware_dacout_deinited(self)) { + raise_deinited_error(); + } +} + +//| def deinit(self) -> None: +//| """Deinitialises the DACOut and releases any hardware resources for reuse.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_deinit(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_mtm_hardware_dacout_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_deinit_obj, mtm_hardware_dacout_deinit); + +//| def __enter__(self) -> DACOut: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +// Provided by context manager helper. + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 8 bit or 16 bit samples.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_mtm_hardware_dacout_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mtm_hardware_dacout_play_obj, 1, mtm_hardware_dacout_obj_play); + +//| def stop(self) -> None: +//| """Stops playback.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_stop(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_mtm_hardware_dacout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_stop_obj, mtm_hardware_dacout_obj_stop); + +//| playing: bool +//| """True when the audio sample is being output. (read-only)""" +//| +static mp_obj_t mtm_hardware_dacout_obj_get_playing(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_playing_obj, mtm_hardware_dacout_obj_get_playing); + +MP_PROPERTY_GETTER(mtm_hardware_dacout_playing_obj, + (mp_obj_t)&mtm_hardware_dacout_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_pause(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (!common_hal_mtm_hardware_dacout_get_playing(self)) { + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Not playing")); + } + common_hal_mtm_hardware_dacout_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_pause_obj, mtm_hardware_dacout_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_resume(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_mtm_hardware_dacout_get_paused(self)) { + common_hal_mtm_hardware_dacout_resume(self); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_resume_obj, mtm_hardware_dacout_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +static mp_obj_t mtm_hardware_dacout_obj_get_paused(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_paused_obj, mtm_hardware_dacout_obj_get_paused); + +MP_PROPERTY_GETTER(mtm_hardware_dacout_paused_obj, + (mp_obj_t)&mtm_hardware_dacout_get_paused_obj); + +// ── DACOut type definition ─────────────────────────────────────────────────── + +static const mp_rom_map_elem_t mtm_hardware_dacout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mtm_hardware_dacout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mtm_hardware_dacout_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mtm_hardware_dacout_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mtm_hardware_dacout_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mtm_hardware_dacout_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mtm_hardware_dacout_paused_obj) }, +}; +static MP_DEFINE_CONST_DICT(mtm_hardware_dacout_locals_dict, mtm_hardware_dacout_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mtm_hardware_dacout_type, + MP_QSTR_DACOut, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, mtm_hardware_dacout_make_new, + locals_dict, &mtm_hardware_dacout_locals_dict + ); + +// ───────────────────────────────────────────────────────────────────────────── +// mtm_hardware module definition +// ───────────────────────────────────────────────────────────────────────────── + +//| """Hardware interface to Music Thing Modular Workshop Computer peripherals. +//| +//| Provides the `DACOut` class for non-blocking audio output via the +//| MCP4822 dual-channel 12-bit SPI DAC. +//| """ + +static const mp_rom_map_elem_t mtm_hardware_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mtm_hardware) }, + { MP_ROM_QSTR(MP_QSTR_DACOut), MP_ROM_PTR(&mtm_hardware_dacout_type) }, +}; + +static MP_DEFINE_CONST_DICT(mtm_hardware_module_globals, mtm_hardware_module_globals_table); + +const mp_obj_module_t mtm_hardware_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mtm_hardware_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mtm_hardware, mtm_hardware_module); From c7fc0c07a78ad14a213fd307036682d92926756d Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 12:58:15 -0700 Subject: [PATCH 437/517] mtm_hardware.dacout: add gain=1 or gain=2 argument --- .../boards/mtm_computer/module/DACOut.c | 21 +++++++++++++++++-- .../boards/mtm_computer/module/DACOut.h | 2 +- .../boards/mtm_computer/module/mtm_hardware.c | 13 ++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c index 84f4296cb00cd..fb4ce37d4d0ff 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c @@ -128,9 +128,19 @@ static const uint16_t mcp4822_pio_program[] = { #define MCP4822_CLOCKS_PER_SAMPLE 86 +// MCP4822 gain bit (bit 13) position in the PIO program: +// Instruction 6 = channel A gain bit +// Instruction 18 = channel B gain bit +// 1x gain: set pins, 1 (0xE001) — bit 13 = 1 +// 2x gain: set pins, 0 (0xE000) — bit 13 = 0 +#define MCP4822_PIO_GAIN_INSTR_A 6 +#define MCP4822_PIO_GAIN_INSTR_B 18 +#define MCP4822_PIO_GAIN_1X 0xE001 // set pins, 1 +#define MCP4822_PIO_GAIN_2X 0xE000 // set pins, 0 + void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs) { + const mcu_pin_obj_t *cs, uint8_t gain) { // SET pins span from MOSI to CS. MOSI must have a lower GPIO number // than CS, with at most 4 pins between them (SET count max is 5). @@ -141,6 +151,13 @@ void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, uint8_t set_count = cs->number - mosi->number + 1; + // Build a mutable copy of the PIO program and patch the gain bit + uint16_t program[MP_ARRAY_SIZE(mcp4822_pio_program)]; + memcpy(program, mcp4822_pio_program, sizeof(mcp4822_pio_program)); + uint16_t gain_instr = (gain == 2) ? MCP4822_PIO_GAIN_2X : MCP4822_PIO_GAIN_1X; + program[MCP4822_PIO_GAIN_INSTR_A] = gain_instr; + program[MCP4822_PIO_GAIN_INSTR_B] = gain_instr; + // Initial SET pin state: CS high (bit at CS position), others low uint32_t cs_bit_position = cs->number - mosi->number; pio_pinmask32_t initial_set_state = PIO_PINMASK32_FROM_VALUE(1u << cs_bit_position); @@ -148,7 +165,7 @@ void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, common_hal_rp2pio_statemachine_construct( &self->state_machine, - mcp4822_pio_program, MP_ARRAY_SIZE(mcp4822_pio_program), + program, MP_ARRAY_SIZE(mcp4822_pio_program), 44100 * MCP4822_CLOCKS_PER_SAMPLE, // Initial frequency; play() adjusts NULL, 0, // No init program NULL, 0, // No may_exec diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h index f7c0c9eb8062c..f9b5dd60108cf 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h @@ -21,7 +21,7 @@ typedef struct { void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs); + const mcu_pin_obj_t *cs, uint8_t gain); void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c index a3dafbcf9415a..8f875496f6745 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c +++ b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c @@ -29,12 +29,15 @@ //| clock: microcontroller.Pin, //| mosi: microcontroller.Pin, //| cs: microcontroller.Pin, +//| *, +//| gain: int = 1, //| ) -> None: //| """Create a DACOut object associated with the given SPI pins. //| //| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin //| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin //| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. //| //| Simple 8ksps 440 Hz sine wave:: //| @@ -72,11 +75,12 @@ //| ... //| static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_clock, ARG_mosi, ARG_cs }; + enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -85,8 +89,13 @@ static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + mp_int_t gain = args[ARG_gain].u_int; + if (gain != 1 && gain != 2) { + mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("gain must be 1 or 2")); + } + mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); - common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs); + common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs, (uint8_t)gain); return MP_OBJ_FROM_PTR(self); } From 2e4fc89120625dfeb6acabd229eba7e36664c4a5 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 16:42:55 -0700 Subject: [PATCH 438/517] rework mcp4822 module from mtm_hardware.DACOut --- locale/circuitpython.pot | 4579 ----------------- ports/raspberrypi/boards/mtm_computer/board.c | 19 + .../boards/mtm_computer/module/DACOut.h | 38 - .../boards/mtm_computer/module/mtm_hardware.c | 276 - .../boards/mtm_computer/mpconfigboard.mk | 5 +- ports/raspberrypi/boards/mtm_computer/pins.c | 6 +- .../DACOut.c => common-hal/mcp4822/MCP4822.c} | 90 +- .../raspberrypi/common-hal/mcp4822/MCP4822.h | 22 + .../raspberrypi/common-hal/mcp4822/__init__.c | 7 + ports/raspberrypi/mpconfigport.mk | 1 + py/circuitpy_defns.mk | 5 + shared-bindings/mcp4822/MCP4822.c | 247 + shared-bindings/mcp4822/MCP4822.h | 25 + shared-bindings/mcp4822/__init__.c | 36 + shared-bindings/mcp4822/__init__.h | 7 + 15 files changed, 417 insertions(+), 4946 deletions(-) delete mode 100644 locale/circuitpython.pot delete mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.h delete mode 100644 ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c rename ports/raspberrypi/{boards/mtm_computer/module/DACOut.c => common-hal/mcp4822/MCP4822.c} (75%) create mode 100644 ports/raspberrypi/common-hal/mcp4822/MCP4822.h create mode 100644 ports/raspberrypi/common-hal/mcp4822/__init__.c create mode 100644 shared-bindings/mcp4822/MCP4822.c create mode 100644 shared-bindings/mcp4822/MCP4822.h create mode 100644 shared-bindings/mcp4822/__init__.c create mode 100644 shared-bindings/mcp4822/__init__.h diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot deleted file mode 100644 index bf4c5d110f673..0000000000000 --- a/locale/circuitpython.pot +++ /dev/null @@ -1,4579 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: main.c -msgid "" -"\n" -"Code done running.\n" -msgstr "" - -#: main.c -msgid "" -"\n" -"Code stopped by auto-reload. Reloading soon.\n" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "" -"\n" -"Please file an issue with your program at github.com/adafruit/circuitpython/" -"issues." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "" -"\n" -"Press reset to exit safe mode.\n" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "" -"\n" -"You are in safe mode because:\n" -msgstr "" - -#: py/obj.c -msgid " File \"%q\"" -msgstr "" - -#: py/obj.c -msgid " File \"%q\", line %d" -msgstr "" - -#: py/builtinhelp.c -msgid " is of type %q\n" -msgstr "" - -#: main.c -msgid " not found.\n" -msgstr "" - -#: main.c -msgid " output:\n" -msgstr "" - -#: py/objstr.c -#, c-format -msgid "%%c needs int or char" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "" -"%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" -msgstr "" - -#: shared-bindings/microcontroller/Pin.c -msgid "%q and %q contain duplicate pins" -msgstr "" - -#: shared-bindings/audioio/AudioOut.c -msgid "%q and %q must be different" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -msgid "%q and %q must share a clock unit" -msgstr "" - -#: ports/nordic/common-hal/watchdog/WatchDogTimer.c -msgid "%q cannot be changed once mode is set to %q" -msgstr "" - -#: shared-bindings/microcontroller/Pin.c -msgid "%q contains duplicate pins" -msgstr "" - -#: ports/atmel-samd/common-hal/sdioio/SDCard.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "%q failure: %d" -msgstr "" - -#: shared-module/audiodelays/MultiTapDelay.c -msgid "%q in %q must be of type %q or %q, not %q" -msgstr "" - -#: py/argcheck.c shared-module/audiofilters/Filter.c -msgid "%q in %q must be of type %q, not %q" -msgstr "" - -#: ports/espressif/common-hal/espulp/ULP.c -#: ports/espressif/common-hal/mipidsi/Bus.c -#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c -#: ports/mimxrt10xx/common-hal/usb_host/Port.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -#: ports/raspberrypi/common-hal/usb_host/Port.c -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c -#: shared-module/max3421e/Max3421E.c -msgid "%q in use" -msgstr "" - -#: py/objstr.c -msgid "%q index out of range" -msgstr "" - -#: py/obj.c -msgid "%q indices must be integers, not %s" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c -#: shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "%q init failed" -msgstr "" - -#: ports/espressif/bindings/espnow/Peer.c shared-bindings/dualbank/__init__.c -msgid "%q is %q" -msgstr "" - -#: ports/raspberrypi/common-hal/wifi/Radio.c -msgid "%q is read-only for this board" -msgstr "" - -#: py/argcheck.c shared-bindings/usb_hid/Device.c -msgid "%q length must be %d" -msgstr "" - -#: py/argcheck.c -msgid "%q length must be %d-%d" -msgstr "" - -#: py/argcheck.c -msgid "%q length must be <= %d" -msgstr "" - -#: py/argcheck.c -msgid "%q length must be >= %d" -msgstr "" - -#: py/argcheck.c -msgid "%q must be %d" -msgstr "" - -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -#: shared-bindings/is31fl3741/FrameBuffer.c -#: shared-bindings/rgbmatrix/RGBMatrix.c -msgid "%q must be %d-%d" -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -msgid "%q must be 1 when %q is True" -msgstr "" - -#: py/argcheck.c shared-bindings/gifio/GifWriter.c -#: shared-module/gifio/OnDiskGif.c -msgid "%q must be <= %d" -msgstr "" - -#: ports/espressif/common-hal/watchdog/WatchDogTimer.c -msgid "%q must be <= %u" -msgstr "" - -#: py/argcheck.c -msgid "%q must be >= %d" -msgstr "" - -#: shared-bindings/analogbufio/BufferedIn.c -msgid "%q must be a bytearray or array of type 'H' or 'B'" -msgstr "" - -#: shared-bindings/audiocore/RawSample.c -msgid "%q must be a bytearray or array of type 'h', 'H', 'b', or 'B'" -msgstr "" - -#: shared-bindings/warnings/__init__.c -msgid "%q must be a subclass of %q" -msgstr "" - -#: ports/espressif/common-hal/analogbufio/BufferedIn.c -msgid "%q must be array of type 'H'" -msgstr "" - -#: shared-module/synthio/__init__.c -msgid "%q must be array of type 'h'" -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "%q must be multiple of 8." -msgstr "" - -#: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c -#: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c -#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c -#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c -#: shared-module/synthio/Synthesizer.c -msgid "%q must be of type %q or %q, not %q" -msgstr "" - -#: shared-bindings/jpegio/JpegDecoder.c -msgid "%q must be of type %q, %q, or %q, not %q" -msgstr "" - -#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c -#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c -#: shared-module/synthio/__init__.c -msgid "%q must be of type %q, not %q" -msgstr "" - -#: ports/atmel-samd/common-hal/busio/UART.c -msgid "%q must be power of 2" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "%q object missing '%q' attribute" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "%q object missing '%q' method" -msgstr "" - -#: shared-bindings/wifi/Monitor.c -msgid "%q out of bounds" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -#: ports/cxd56/common-hal/pulseio/PulseIn.c -#: ports/nordic/common-hal/pulseio/PulseIn.c -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -#: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c -#: shared-bindings/time/__init__.c -msgid "%q out of range" -msgstr "" - -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - -#: py/objrange.c py/objslice.c shared-bindings/random/__init__.c -msgid "%q step cannot be zero" -msgstr "" - -#: shared-module/bitbangio/I2C.c -msgid "%q too long" -msgstr "" - -#: py/bc.c py/objnamedtuple.c -msgid "%q() takes %d positional arguments but %d were given" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "%q() without %q()" -msgstr "" - -#: shared-bindings/usb_hid/Device.c -msgid "%q, %q, and %q must all be the same length" -msgstr "" - -#: py/objint.c shared-bindings/_bleio/Connection.c -#: shared-bindings/storage/__init__.c -msgid "%q=%q" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "%q[%u] shifts in more bits than pin count" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "%q[%u] shifts out more bits than pin count" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "%q[%u] uses extra pin" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "%q[%u] waits on input outside of count" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -#, c-format -msgid "%s error 0x%x" -msgstr "" - -#: py/argcheck.c -msgid "'%q' argument required" -msgstr "" - -#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "'%q' object does not support '%q'" -msgstr "" - -#: py/runtime.c -msgid "'%q' object isn't an iterator" -msgstr "" - -#: py/objtype.c py/runtime.c shared-module/atexit/__init__.c -msgid "'%q' object isn't callable" -msgstr "" - -#: py/runtime.c -msgid "'%q' object isn't iterable" -msgstr "" - -#: py/emitinlinethumb.c py/emitinlinextensa.c -#, c-format -msgid "'%s' expects a label" -msgstr "" - -#: py/emitinlinethumb.c py/emitinlinextensa.c -#, c-format -msgid "'%s' expects a register" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects a special register" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects an FPU register" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects an address of the form [a, b]" -msgstr "" - -#: py/emitinlinethumb.c py/emitinlinextensa.c -#, c-format -msgid "'%s' expects an integer" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects at most r%d" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' expects {r0, r1, ...}" -msgstr "" - -#: py/emitinlinextensa.c -#, c-format -msgid "'%s' integer %d isn't within range %d..%d" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "'%s' integer 0x%x doesn't fit in mask 0x%x" -msgstr "" - -#: py/obj.c -#, c-format -msgid "'%s' object doesn't support item assignment" -msgstr "" - -#: py/obj.c -#, c-format -msgid "'%s' object doesn't support item deletion" -msgstr "" - -#: py/runtime.c -msgid "'%s' object has no attribute '%q'" -msgstr "" - -#: py/obj.c -#, c-format -msgid "'%s' object isn't subscriptable" -msgstr "" - -#: py/objstr.c -msgid "'=' alignment not allowed in string format specifier" -msgstr "" - -#: shared-module/struct/__init__.c -msgid "'S' and 'O' are not supported format types" -msgstr "" - -#: py/compile.c -msgid "'align' requires 1 argument" -msgstr "" - -#: py/compile.c -msgid "'await' outside function" -msgstr "" - -#: py/compile.c -msgid "'break'/'continue' outside loop" -msgstr "" - -#: py/compile.c -msgid "'data' requires at least 2 arguments" -msgstr "" - -#: py/compile.c -msgid "'data' requires integer arguments" -msgstr "" - -#: py/compile.c -msgid "'label' requires 1 argument" -msgstr "" - -#: py/emitnative.c -msgid "'not' not implemented" -msgstr "" - -#: py/compile.c -msgid "'return' outside function" -msgstr "" - -#: py/compile.c -msgid "'yield from' inside async function" -msgstr "" - -#: py/compile.c -msgid "'yield' outside function" -msgstr "" - -#: py/compile.c -msgid "* arg after **" -msgstr "" - -#: py/compile.c -msgid "*x must be assignment target" -msgstr "" - -#: py/obj.c -msgid ", in %q\n" -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -msgid ".show(x) removed. Use .root_group = x" -msgstr "" - -#: py/objcomplex.c -msgid "0.0 to a complex power" -msgstr "" - -#: py/modbuiltins.c -msgid "3-arg pow() not supported" -msgstr "" - -#: ports/raspberrypi/common-hal/wifi/Radio.c -msgid "AP could not be started" -msgstr "" - -#: shared-bindings/ipaddress/IPv4Address.c -#, c-format -msgid "Address must be %d bytes long" -msgstr "" - -#: ports/espressif/common-hal/memorymap/AddressRange.c -#: ports/nordic/common-hal/memorymap/AddressRange.c -#: ports/raspberrypi/common-hal/memorymap/AddressRange.c -msgid "Address range not allowed" -msgstr "" - -#: shared-bindings/memorymap/AddressRange.c -msgid "Address range wraps around" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -msgid "All CAN peripherals are in use" -msgstr "" - -#: ports/espressif/common-hal/busio/I2C.c -#: ports/espressif/common-hal/i2ctarget/I2CTarget.c -#: ports/nordic/common-hal/busio/I2C.c -msgid "All I2C peripherals are in use" -msgstr "" - -#: ports/atmel-samd/common-hal/canio/Listener.c -#: ports/espressif/common-hal/canio/Listener.c -#: ports/stm/common-hal/canio/Listener.c -msgid "All RX FIFOs in use" -msgstr "" - -#: ports/espressif/common-hal/busio/SPI.c ports/nordic/common-hal/busio/SPI.c -msgid "All SPI peripherals are in use" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c -#: ports/nordic/common-hal/busio/UART.c -msgid "All UART peripherals are in use" -msgstr "" - -#: ports/nordic/common-hal/countio/Counter.c -#: ports/nordic/common-hal/pulseio/PulseIn.c -#: ports/nordic/common-hal/rotaryio/IncrementalEncoder.c -msgid "All channels in use" -msgstr "" - -#: ports/raspberrypi/common-hal/usb_host/Port.c -msgid "All dma channels in use" -msgstr "" - -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -msgid "All event channels in use" -msgstr "" - -#: ports/raspberrypi/common-hal/floppyio/__init__.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -#: ports/raspberrypi/common-hal/usb_host/Port.c -msgid "All state machines in use" -msgstr "" - -#: ports/atmel-samd/audio_dma.c -msgid "All sync event channels in use" -msgstr "" - -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c -msgid "All timers for this pin are in use" -msgstr "" - -#: ports/atmel-samd/common-hal/_pew/PewPew.c -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -#: ports/atmel-samd/common-hal/pulseio/PulseOut.c -#: ports/cxd56/common-hal/pulseio/PulseOut.c -#: ports/nordic/common-hal/audiopwmio/PWMAudioOut.c -#: ports/nordic/common-hal/pulseio/PulseIn.c -#: ports/nordic/peripherals/nrf/timers.c -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "All timers in use" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Already advertising." -msgstr "" - -#: ports/atmel-samd/common-hal/canio/Listener.c -msgid "Already have all-matches listener" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -msgid "Already in progress" -msgstr "" - -#: ports/espressif/bindings/espnow/ESPNow.c -#: ports/espressif/common-hal/espulp/ULP.c -#: shared-module/memorymonitor/AllocationAlarm.c -#: shared-module/memorymonitor/AllocationSize.c -msgid "Already running" -msgstr "" - -#: ports/espressif/common-hal/wifi/Radio.c -#: ports/raspberrypi/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "Already scanning for wifi networks" -msgstr "" - -#: supervisor/shared/settings.c -#, c-format -msgid "An error occurred while retrieving '%s':\n" -msgstr "" - -#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c -msgid "Another PWMAudioOut is already active" -msgstr "" - -#: ports/atmel-samd/common-hal/pulseio/PulseOut.c -#: ports/cxd56/common-hal/pulseio/PulseOut.c -msgid "Another send is already active" -msgstr "" - -#: shared-bindings/pulseio/PulseOut.c -msgid "Array must contain halfwords (type 'H')" -msgstr "" - -#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c -#: shared-bindings/nvm/ByteArray.c -msgid "Array values should be single bytes." -msgstr "" - -#: ports/atmel-samd/common-hal/spitarget/SPITarget.c -msgid "Async SPI transfer in progress on this bus, keep awaiting." -msgstr "" - -#: shared-module/memorymonitor/AllocationAlarm.c -#, c-format -msgid "Attempt to allocate %d blocks" -msgstr "" - -#: ports/raspberrypi/audio_dma.c -msgid "Audio conversion not implemented" -msgstr "" - -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "Audio source error" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "AuthMode.OPEN is not used with password" -msgstr "" - -#: shared-bindings/wifi/Radio.c supervisor/shared/web_workflow/web_workflow.c -msgid "Authentication failure" -msgstr "" - -#: main.c -msgid "Auto-reload is off.\n" -msgstr "" - -#: main.c -msgid "" -"Auto-reload is on. Simply save files over USB to run them or enter REPL to " -"disable.\n" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -msgid "Baudrate not supported by peripheral" -msgstr "" - -#: shared-module/busdisplay/BusDisplay.c -#: shared-module/framebufferio/FramebufferDisplay.c -msgid "Below minimum frame rate" -msgstr "" - -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -msgid "Bit clock and word select must be sequential GPIO pins" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "Bitmap size and bits per value must match" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Boot device must be first (interface #0)." -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -#: ports/mimxrt10xx/common-hal/busio/UART.c -msgid "Both RX and TX required for flow control" -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -msgid "Brightness not adjustable" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "Buffer elements must be 4 bytes long or less" -msgstr "" - -#: shared-bindings/framebufferio/FramebufferDisplay.c -msgid "Buffer is not a bytearray." -msgstr "" - -#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c -#, c-format -msgid "Buffer length %d too big. It must be less than %d" -msgstr "" - -#: ports/atmel-samd/common-hal/sdioio/SDCard.c -#: ports/cxd56/common-hal/sdioio/SDCard.c -#: ports/espressif/common-hal/sdioio/SDCard.c -#: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c -#: shared-module/sdcardio/SDCard.c -#, c-format -msgid "Buffer must be a multiple of %d bytes" -msgstr "" - -#: shared-bindings/_bleio/PacketBuffer.c -#, c-format -msgid "Buffer too short by %d bytes" -msgstr "" - -#: ports/cxd56/common-hal/camera/Camera.c -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -#: shared-bindings/struct/__init__.c shared-module/struct/__init__.c -msgid "Buffer too small" -msgstr "" - -#: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/raspberrypi/common-hal/paralleldisplaybus/ParallelBus.c -#, c-format -msgid "Bus pin %d is already in use" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "CBC blocks must be multiples of 16 bytes" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "CIRCUITPY drive could not be found or created." -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "CRC or checksum was invalid" -msgstr "" - -#: py/objtype.c -msgid "Call super().__init__() before accessing native object." -msgstr "" - -#: ports/cxd56/common-hal/camera/Camera.c -msgid "Camera init" -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Can only alarm on RTC IO from deep sleep." -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Can only alarm on one low pin while others alarm high from deep sleep." -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Can only alarm on two low pins from deep sleep." -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Can't construct AudioOut because continuous channel already open" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -msgid "Can't set CCCD on local Characteristic" -msgstr "" - -#: shared-bindings/storage/__init__.c shared-bindings/usb_cdc/__init__.c -#: shared-bindings/usb_hid/__init__.c shared-bindings/usb_midi/__init__.c -#: shared-bindings/usb_video/__init__.c -msgid "Cannot change USB devices now" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Cannot create a new Adapter; use _bleio.adapter;" -msgstr "" - -#: shared-module/i2cioexpander/IOExpander.c -msgid "Cannot deinitialize board IOExpander" -msgstr "" - -#: shared-bindings/displayio/Bitmap.c -#: shared-bindings/memorymonitor/AllocationSize.c -#: shared-bindings/pulseio/PulseIn.c -msgid "Cannot delete values" -msgstr "" - -#: ports/atmel-samd/common-hal/digitalio/DigitalInOut.c -#: ports/mimxrt10xx/common-hal/digitalio/DigitalInOut.c -#: ports/nordic/common-hal/digitalio/DigitalInOut.c -#: ports/raspberrypi/common-hal/digitalio/DigitalInOut.c -msgid "Cannot get pull while in output mode" -msgstr "" - -#: ports/nordic/common-hal/microcontroller/Processor.c -msgid "Cannot get temperature" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Cannot have scan responses for extended, connectable advertisements." -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Cannot pull on input-only pin." -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "Cannot record to a file" -msgstr "" - -#: shared-module/storage/__init__.c -msgid "Cannot remount path when visible via USB." -msgstr "" - -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/i2cioexpander/IOPin.c -msgid "Cannot set value when direction is input." -msgstr "" - -#: ports/espressif/common-hal/busio/UART.c -#: ports/mimxrt10xx/common-hal/busio/UART.c -msgid "Cannot specify RTS or CTS in RS485 mode" -msgstr "" - -#: py/objslice.c -msgid "Cannot subclass slice" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Cannot use GPIO0..15 together with GPIO32..47" -msgstr "" - -#: ports/nordic/common-hal/alarm/pin/PinAlarm.c -msgid "Cannot wake on pin edge, only level" -msgstr "" - -#: ports/espressif/common-hal/alarm/pin/PinAlarm.c -msgid "Cannot wake on pin edge. Only level." -msgstr "" - -#: shared-bindings/_bleio/CharacteristicBuffer.c -msgid "CharacteristicBuffer writing not provided" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "CircuitPython core code crashed hard. Whoops!\n" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -msgid "Clock unit in use" -msgstr "" - -#: shared-bindings/_bleio/Connection.c -msgid "" -"Connection has been disconnected and can no longer be used. Create a new " -"connection." -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "Coordinate arrays have different lengths" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "Coordinate arrays types have different sizes" -msgstr "" - -#: shared-module/usb/core/Device.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Could not allocate DMA capable buffer" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/Publisher.c -msgid "Could not publish to ROS topic" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Could not set address" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "Could not start interrupt, RX busy" -msgstr "" - -#: shared-module/audiomp3/MP3Decoder.c -msgid "Couldn't allocate decoder" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -#, c-format -msgid "Critical ROS failure during soft reboot, reset required: %d" -msgstr "" - -#: ports/stm/common-hal/analogio/AnalogOut.c -msgid "DAC Channel Init Error" -msgstr "" - -#: ports/stm/common-hal/analogio/AnalogOut.c -msgid "DAC Device Init Error" -msgstr "" - -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -msgid "DAC already in use" -msgstr "" - -#: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c -msgid "Data 0 pin must be byte aligned" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Data format error (may be broken data)" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Data not supported with directed advertising" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Data too large for advertisement packet" -msgstr "" - -#: ports/stm/common-hal/alarm/pin/PinAlarm.c -msgid "Deep sleep pins must use a rising edge with pulldown" -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "Destination capacity is smaller than destination_length." -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Device error or wrong termination of input stream" -msgstr "" - -#: ports/nordic/common-hal/audiobusio/I2SOut.c -msgid "Device in use" -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -msgid "Display must have a 16 bit colorspace." -msgstr "" - -#: shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/framebufferio/FramebufferDisplay.c -#: shared-bindings/mipidsi/Display.c -msgid "Display rotation must be in 90 degree increments" -msgstr "" - -#: main.c -msgid "Done" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/i2cioexpander/IOPin.c -msgid "Drive mode not used when direction is input." -msgstr "" - -#: py/obj.c -msgid "During handling of the above exception, another exception occurred:" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "ECB only operates on 16 bytes at a time" -msgstr "" - -#: ports/espressif/common-hal/busio/SPI.c -#: ports/espressif/common-hal/canio/CAN.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "ESP-IDF memory allocation failed" -msgstr "" - -#: extmod/modre.c -msgid "Error in regex" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Error in safemode.py." -msgstr "" - -#: shared-bindings/alarm/__init__.c -msgid "Expected a kind of %q" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Extended advertisements with scan response not supported." -msgstr "" - -#: extmod/ulab/code/numpy/fft/fft_tools.c -msgid "FFT is defined for ndarrays only" -msgstr "" - -#: extmod/ulab/code/numpy/fft/fft_tools.c -msgid "FFT is implemented for linear arrays only" -msgstr "" - -#: shared-bindings/ps2io/Ps2.c -msgid "Failed sending command." -msgstr "" - -#: ports/nordic/sd_mutex.c -#, c-format -msgid "Failed to acquire mutex, err 0x%04x" -msgstr "" - -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "Failed to add service TXT record" -msgstr "" - -#: shared-bindings/mdns/Server.c -msgid "" -"Failed to add service TXT record; non-string or bytes found in txt_records" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c -msgid "Failed to allocate %q buffer" -msgstr "" - -#: ports/espressif/common-hal/wifi/__init__.c -msgid "Failed to allocate Wifi memory" -msgstr "" - -#: ports/espressif/common-hal/wifi/ScannedNetworks.c -msgid "Failed to allocate wifi scan memory" -msgstr "" - -#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c -msgid "Failed to buffer the sample" -msgstr "" - -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect: internal error" -msgstr "" - -#: ports/nordic/common-hal/_bleio/Adapter.c -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect: timeout" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to create continuous channels: invalid arg" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to create continuous channels: invalid state" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to create continuous channels: no mem" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to create continuous channels: not found" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to enable continuous" -msgstr "" - -#: shared-module/audiomp3/MP3Decoder.c -msgid "Failed to parse MP3 file" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to register continuous events callback" -msgstr "" - -#: ports/nordic/sd_mutex.c -#, c-format -msgid "Failed to release mutex, err 0x%04x" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -msgid "Failed to set SPI Clock Mode" -msgstr "" - -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "Failed to set hostname" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "Failed to start async audio" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Failed to write internal flash." -msgstr "" - -#: py/moderrno.c -msgid "File exists" -msgstr "" - -#: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -msgid "File not found" -msgstr "" - -#: ports/atmel-samd/common-hal/canio/Listener.c -#: ports/espressif/common-hal/canio/Listener.c -#: ports/mimxrt10xx/common-hal/canio/Listener.c -#: ports/stm/common-hal/canio/Listener.c -msgid "Filters too complex" -msgstr "" - -#: ports/espressif/common-hal/dualbank/__init__.c -msgid "Firmware is duplicate" -msgstr "" - -#: ports/espressif/common-hal/dualbank/__init__.c -msgid "Firmware is invalid" -msgstr "" - -#: ports/espressif/common-hal/dualbank/__init__.c -msgid "Firmware is too big" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "For L8 colorspace, input bitmap must have 8 bits per pixel" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "For RGB colorspaces, input bitmap must have 16 bits per pixel" -msgstr "" - -#: ports/cxd56/common-hal/camera/Camera.c shared-module/audiocore/WaveFile.c -msgid "Format not supported" -msgstr "" - -#: ports/mimxrt10xx/common-hal/microcontroller/Processor.c -msgid "" -"Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" -msgstr "" - -#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c -#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c -msgid "Function requires lock" -msgstr "" - -#: ports/cxd56/common-hal/gnss/GNSS.c -msgid "GNSS init" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Generic Failure" -msgstr "" - -#: shared-bindings/framebufferio/FramebufferDisplay.c -#: shared-module/busdisplay/BusDisplay.c -#: shared-module/framebufferio/FramebufferDisplay.c -msgid "Group already used" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Hard fault: memory access or instruction error." -msgstr "" - -#: ports/mimxrt10xx/common-hal/busio/SPI.c -#: ports/mimxrt10xx/common-hal/busio/UART.c ports/stm/common-hal/busio/I2C.c -#: ports/stm/common-hal/busio/SPI.c ports/stm/common-hal/busio/UART.c -#: ports/stm/common-hal/canio/CAN.c ports/stm/common-hal/sdioio/SDCard.c -msgid "Hardware in use, try alternative pins" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Heap allocation when VM not running." -msgstr "" - -#: extmod/vfs_posix_file.c py/objstringio.c -msgid "I/O operation on closed file" -msgstr "" - -#: ports/stm/common-hal/busio/I2C.c -msgid "I2C init error" -msgstr "" - -#: ports/raspberrypi/common-hal/busio/I2C.c -#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c -msgid "I2C peripheral in use" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "In-buffer elements must be <= 4 bytes long" -msgstr "" - -#: shared-bindings/_pew/PewPew.c -msgid "Incorrect buffer size" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "Init program size invalid" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Initial set pin direction conflicts with initial out pin direction" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Initial set pin state conflicts with initial out pin state" -msgstr "" - -#: shared-bindings/bitops/__init__.c -#, c-format -msgid "Input buffer length (%d) must be a multiple of the strand count (%d)" -msgstr "" - -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -msgid "Input taking too long" -msgstr "" - -#: py/moderrno.c -msgid "Input/output error" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Insufficient authentication" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Insufficient encryption" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Insufficient memory pool for the image" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Insufficient stream input buffer" -msgstr "" - -#: ports/espressif/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "Interface must be started" -msgstr "" - -#: ports/atmel-samd/audio_dma.c ports/raspberrypi/audio_dma.c -msgid "Internal audio buffer too small" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "Internal define error" -msgstr "" - -#: ports/espressif/common-hal/qspibus/QSPIBus.c -#: shared-bindings/pwmio/PWMOut.c supervisor/shared/settings.c -msgid "Internal error" -msgstr "" - -#: shared-module/rgbmatrix/RGBMatrix.c -#, c-format -msgid "Internal error #%d" -msgstr "" - -#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c -#: ports/atmel-samd/common-hal/countio/Counter.c -#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c -#: ports/atmel-samd/common-hal/max3421e/Max3421E.c -#: ports/atmel-samd/common-hal/ps2io/Ps2.c -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c -#: ports/cxd56/common-hal/pulseio/PulseIn.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c -#: shared-bindings/pwmio/PWMOut.c -msgid "Internal resource(s) in use" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Internal watchdog timer expired." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Interrupt error." -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Interrupted by output function" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -#: ports/analog/peripherals/max32690/max32_i2c.c -#: ports/analog/peripherals/max32690/max32_spi.c -#: ports/analog/peripherals/max32690/max32_uart.c -#: ports/espressif/common-hal/_bleio/Service.c -#: ports/espressif/common-hal/espulp/ULP.c -#: ports/espressif/common-hal/microcontroller/Processor.c -#: ports/espressif/common-hal/mipidsi/Display.c -#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c -#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c -#: ports/raspberrypi/bindings/picodvi/Framebuffer.c -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c -#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c -#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c -#: shared-module/aurora_epaper/aurora_framebuffer.c -#: shared-module/lvfontio/OnDiskFont.c -msgid "Invalid %q" -msgstr "" - -#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c -#: shared-module/aurora_epaper/aurora_framebuffer.c -msgid "Invalid %q and %q" -msgstr "" - -#: ports/atmel-samd/common-hal/microcontroller/Pin.c -#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c -#: ports/mimxrt10xx/common-hal/microcontroller/Pin.c -#: shared-bindings/microcontroller/Pin.c -msgid "Invalid %q pin" -msgstr "" - -#: ports/stm/common-hal/analogio/AnalogIn.c -msgid "Invalid ADC Unit value" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Invalid BLE parameter" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "Invalid BSSID" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "Invalid MAC address" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -msgid "Invalid ROS domain ID" -msgstr "" - -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Invalid advertising data" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c -msgid "Invalid argument" -msgstr "" - -#: shared-module/displayio/Bitmap.c -msgid "Invalid bits per value" -msgstr "" - -#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c -#, c-format -msgid "Invalid data_pins[%d]" -msgstr "" - -#: shared-module/msgpack/__init__.c supervisor/shared/settings.c -msgid "Invalid format" -msgstr "" - -#: shared-module/audiocore/WaveFile.c -msgid "Invalid format chunk size" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "Invalid hex password" -msgstr "" - -#: ports/espressif/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "Invalid multicast MAC address" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Invalid size" -msgstr "" - -#: shared-module/ssl/SSLSocket.c -msgid "Invalid socket for TLS" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -#: ports/espressif/common-hal/espidf/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Invalid state" -msgstr "" - -#: supervisor/shared/settings.c -msgid "Invalid unicode escape" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "Key must be 16, 24, or 32 bytes long" -msgstr "" - -#: shared-module/is31fl3741/FrameBuffer.c -msgid "LED mappings must match display size" -msgstr "" - -#: py/compile.c -msgid "LHS of keyword arg must be an id" -msgstr "" - -#: shared-module/displayio/Group.c -msgid "Layer already in a group" -msgstr "" - -#: shared-module/displayio/Group.c -msgid "Layer must be a Group or TileGrid subclass" -msgstr "" - -#: shared-bindings/audiocore/RawSample.c -msgid "Length of %q must be an even multiple of channel_count * type_size" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "MAC address was invalid" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/espressif/common-hal/_bleio/Descriptor.c -msgid "MITM security not supported" -msgstr "" - -#: ports/stm/common-hal/sdioio/SDCard.c -#, c-format -msgid "MMC/SDIO Clock Error %x" -msgstr "" - -#: shared-bindings/is31fl3741/IS31FL3741.c -msgid "Mapping must be a tuple" -msgstr "" - -#: py/persistentcode.c -msgid "MicroPython .mpy file; use CircuitPython mpy-cross" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Mismatched data size" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Mismatched swap flag" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_in_pin. %q[%u] reads pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_in_pin. %q[%u] shifts in from pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_in_pin. %q[%u] waits based on pin" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_out_pin. %q[%u] shifts out to pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_out_pin. %q[%u] writes pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing first_set_pin. %q[%u] sets pin(s)" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Missing jmp_pin. %q[%u] jumps on pin" -msgstr "" - -#: shared-module/storage/__init__.c -msgid "Mount point directory missing" -msgstr "" - -#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c -msgid "Must be a %q subclass." -msgstr "" - -#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c -msgid "Must provide 5/6/5 RGB pins" -msgstr "" - -#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c -msgid "Must provide MISO or MOSI pin" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "Must use a multiple of 6 rgb pins, not %d" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "NLR jump failed. Likely memory corruption." -msgstr "" - -#: ports/espressif/common-hal/nvm/ByteArray.c -msgid "NVS Error" -msgstr "" - -#: shared-bindings/socketpool/SocketPool.c -msgid "Name or service not known" -msgstr "" - -#: shared-bindings/displayio/TileGrid.c -msgid "New bitmap must be same size as old bitmap" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -msgid "Nimble out of memory" -msgstr "" - -#: ports/atmel-samd/common-hal/busio/UART.c -#: ports/espressif/common-hal/busio/SPI.c -#: ports/espressif/common-hal/busio/UART.c -#: ports/mimxrt10xx/common-hal/busio/SPI.c -#: ports/mimxrt10xx/common-hal/busio/UART.c -#: ports/nordic/common-hal/busio/UART.c -#: ports/raspberrypi/common-hal/busio/UART.c ports/stm/common-hal/busio/SPI.c -#: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c -#: shared-bindings/i2cdisplaybus/I2CDisplayBus.c -#: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-bindings/qspibus/QSPIBus.c -#: shared-module/bitbangio/SPI.c -msgid "No %q pin" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -msgid "No CCCD for this Characteristic" -msgstr "" - -#: ports/atmel-samd/common-hal/analogio/AnalogOut.c -#: ports/stm/common-hal/analogio/AnalogOut.c -msgid "No DAC on chip" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "No DMA channel found" -msgstr "" - -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "No DMA pacing timer found" -msgstr "" - -#: shared-module/adafruit_bus_device/i2c_device/I2CDevice.c -#, c-format -msgid "No I2C device at address: 0x%x" -msgstr "" - -#: supervisor/shared/web_workflow/web_workflow.c -msgid "No IP" -msgstr "" - -#: ports/atmel-samd/common-hal/microcontroller/__init__.c -#: ports/cxd56/common-hal/microcontroller/__init__.c -#: ports/mimxrt10xx/common-hal/microcontroller/__init__.c -msgid "No bootloader present" -msgstr "" - -#: shared-module/usb/core/Device.c -msgid "No configuration set" -msgstr "" - -#: shared-bindings/_bleio/PacketBuffer.c -msgid "No connection: length cannot be determined" -msgstr "" - -#: shared-bindings/board/__init__.c -msgid "No default %q bus" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c -#: ports/atmel-samd/common-hal/touchio/TouchIn.c -msgid "No free GCLKs" -msgstr "" - -#: shared-bindings/os/__init__.c -msgid "No hardware random available" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "No in in program" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "No in or out in program" -msgstr "" - -#: py/objint.c shared-bindings/time/__init__.c -msgid "No long integer support" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "No network with that ssid" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "No out in program" -msgstr "" - -#: ports/atmel-samd/common-hal/busio/I2C.c -#: ports/espressif/common-hal/busio/I2C.c -#: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nordic/common-hal/busio/I2C.c -#: ports/raspberrypi/common-hal/busio/I2C.c -msgid "No pull up found on SDA or SCL; check your wiring" -msgstr "" - -#: shared-module/touchio/TouchIn.c -msgid "No pulldown on pin; 1Mohm recommended" -msgstr "" - -#: shared-module/touchio/TouchIn.c -msgid "No pullup on pin; 1Mohm recommended" -msgstr "" - -#: py/moderrno.c -msgid "No space left on device" -msgstr "" - -#: py/moderrno.c -msgid "No such device" -msgstr "" - -#: py/moderrno.c -msgid "No such file/directory" -msgstr "" - -#: shared-module/rgbmatrix/RGBMatrix.c -msgid "No timer available" -msgstr "" - -#: shared-module/usb/core/Device.c -msgid "No usb host port initialized" -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "Nordic system firmware out of memory" -msgstr "" - -#: shared-bindings/ipaddress/IPv4Address.c shared-bindings/ipaddress/__init__.c -msgid "Not a valid IP string" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#: ports/nordic/common-hal/_bleio/__init__.c -#: shared-bindings/_bleio/CharacteristicBuffer.c -msgid "Not connected" -msgstr "" - -#: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c -msgid "Not playing" -msgstr "" - -#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c -#: ports/espressif/common-hal/sdioio/SDCard.c -#, c-format -msgid "Number of data_pins must be %d or %d, not %d" -msgstr "" - -#: shared-bindings/util.c -msgid "" -"Object has been deinitialized and can no longer be used. Create a new object." -msgstr "" - -#: ports/nordic/common-hal/busio/UART.c -msgid "Odd parity is not supported" -msgstr "" - -#: supervisor/shared/bluetooth/bluetooth.c -msgid "Off" -msgstr "" - -#: supervisor/shared/bluetooth/bluetooth.c -msgid "Ok" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c -#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c -#, c-format -msgid "Only 8 or 16 bit mono with %dx oversampling supported." -msgstr "" - -#: ports/espressif/common-hal/wifi/__init__.c -#: ports/raspberrypi/common-hal/wifi/__init__.c -msgid "Only IPv4 addresses supported" -msgstr "" - -#: ports/raspberrypi/common-hal/socketpool/Socket.c -msgid "Only IPv4 sockets supported" -msgstr "" - -#: shared-module/displayio/OnDiskBitmap.c -#, c-format -msgid "" -"Only Windows format, uncompressed BMP supported: given header size is %d" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Only connectable advertisements can be directed" -msgstr "" - -#: ports/stm/common-hal/alarm/pin/PinAlarm.c -msgid "Only edge detection is available on this hardware" -msgstr "" - -#: shared-bindings/ipaddress/__init__.c -msgid "Only int or string supported for ip" -msgstr "" - -#: ports/espressif/common-hal/alarm/touch/TouchAlarm.c -msgid "Only one %q can be set in deep sleep." -msgstr "" - -#: ports/espressif/common-hal/espulp/ULPAlarm.c -msgid "Only one %q can be set." -msgstr "" - -#: ports/espressif/common-hal/i2ctarget/I2CTarget.c -#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c -msgid "Only one address is allowed" -msgstr "" - -#: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c -#: ports/nordic/common-hal/alarm/time/TimeAlarm.c -#: ports/stm/common-hal/alarm/time/TimeAlarm.c -msgid "Only one alarm.time alarm can be set" -msgstr "" - -#: ports/espressif/common-hal/alarm/time/TimeAlarm.c -#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c -msgid "Only one alarm.time alarm can be set." -msgstr "" - -#: shared-module/displayio/ColorConverter.c -msgid "Only one color can be transparent at a time" -msgstr "" - -#: py/moderrno.c -msgid "Operation not permitted" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Operation or feature not supported" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -#: ports/espressif/common-hal/qspibus/QSPIBus.c -msgid "Operation timed out" -msgstr "" - -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "Out of MDNS service slots" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Out of memory" -msgstr "" - -#: ports/espressif/common-hal/socketpool/Socket.c -#: ports/raspberrypi/common-hal/socketpool/Socket.c -#: ports/zephyr-cp/common-hal/socketpool/Socket.c -msgid "Out of sockets" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "Out-buffer elements must be <= 4 bytes long" -msgstr "" - -#: ports/stm/common-hal/pwmio/PWMOut.c -msgid "PWM restart" -msgstr "" - -#: ports/raspberrypi/common-hal/countio/Counter.c -msgid "PWM slice already in use" -msgstr "" - -#: ports/raspberrypi/common-hal/countio/Counter.c -msgid "PWM slice channel A already in use" -msgstr "" - -#: shared-bindings/spitarget/SPITarget.c -msgid "Packet buffers for an SPI transfer must have the same length." -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Parameter error" -msgstr "" - -#: ports/espressif/common-hal/audiobusio/__init__.c -msgid "Peripheral in use" -msgstr "" - -#: py/moderrno.c -msgid "Permission denied" -msgstr "" - -#: ports/stm/common-hal/alarm/pin/PinAlarm.c -msgid "Pin cannot wake from Deep Sleep" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Pin count too large" -msgstr "" - -#: ports/stm/common-hal/alarm/pin/PinAlarm.c -#: ports/stm/common-hal/pulseio/PulseIn.c -msgid "Pin interrupt already in use" -msgstr "" - -#: shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c -msgid "Pin is input only" -msgstr "" - -#: ports/raspberrypi/common-hal/countio/Counter.c -msgid "Pin must be on PWM Channel B" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "" -"Pinout uses %d bytes per element, which consumes more than the ideal %d " -"bytes. If this cannot be avoided, pass allow_inefficient=True to the " -"constructor" -msgstr "" - -#: ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c -msgid "Pins must be sequential" -msgstr "" - -#: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c -msgid "Pins must be sequential GPIO pins" -msgstr "" - -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "Pins must share PWM slice" -msgstr "" - -#: shared-module/usb/core/Device.c -msgid "Pipe error" -msgstr "" - -#: py/builtinhelp.c -msgid "Plus any modules on the filesystem\n" -msgstr "" - -#: shared-module/vectorio/Polygon.c -msgid "Polygon needs at least 3 points" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Power dipped. Make sure you are providing enough power." -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "Prefix buffer must be on the heap" -msgstr "" - -#: main.c -msgid "Press any key to enter the REPL. Use CTRL-D to reload.\n" -msgstr "" - -#: main.c -msgid "Pretending to deep sleep until alarm, CTRL-C or file write.\n" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Program does IN without loading ISR" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "Program does OUT without loading OSR" -msgstr "" - -#: ports/raspberrypi/bindings/rp2pio/StateMachine.c -msgid "Program size invalid" -msgstr "" - -#: ports/espressif/common-hal/espulp/ULP.c -msgid "Program too long" -msgstr "" - -#: shared-bindings/rclcpy/Publisher.c -msgid "Publishers can only be created from a parent node" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/i2cioexpander/IOPin.c -msgid "Pull not used when direction is output." -msgstr "" - -#: ports/raspberrypi/common-hal/countio/Counter.c -msgid "RISE_AND_FALL not available on this chip" -msgstr "" - -#: shared-module/displayio/OnDiskBitmap.c -msgid "RLE-compressed BMP not supported" -msgstr "" - -#: ports/stm/common-hal/os/__init__.c -msgid "RNG DeInit Error" -msgstr "" - -#: ports/stm/common-hal/os/__init__.c -msgid "RNG Init Error" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -msgid "ROS failed to initialize. Is agent connected?" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -msgid "ROS internal setup failure" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/__init__.c -msgid "ROS memory allocator failure" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/Node.c -msgid "ROS node failed to initialize" -msgstr "" - -#: ports/espressif/common-hal/rclcpy/Publisher.c -msgid "ROS topic failed to initialize" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -#: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c -#: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c -msgid "RS485" -msgstr "" - -#: ports/espressif/common-hal/busio/UART.c -#: ports/mimxrt10xx/common-hal/busio/UART.c -msgid "RS485 inversion specified when not in RS485 mode" -msgstr "" - -#: shared-bindings/alarm/time/TimeAlarm.c shared-bindings/time/__init__.c -msgid "RTC is not supported on this board" -msgstr "" - -#: ports/stm/common-hal/os/__init__.c -msgid "Random number generation error" -msgstr "" - -#: shared-bindings/_bleio/__init__.c -#: shared-bindings/memorymonitor/AllocationSize.c -#: shared-bindings/pulseio/PulseIn.c shared-module/bitmaptools/__init__.c -#: shared-module/displayio/Bitmap.c shared-module/displayio/Group.c -msgid "Read-only" -msgstr "" - -#: extmod/vfs_fat.c py/moderrno.c -msgid "Read-only filesystem" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Received response was invalid" -msgstr "" - -#: supervisor/shared/bluetooth/bluetooth.c -msgid "Reconnecting" -msgstr "" - -#: shared-bindings/epaperdisplay/EPaperDisplay.c -msgid "Refresh too soon" -msgstr "" - -#: shared-bindings/canio/RemoteTransmissionRequest.c -msgid "RemoteTransmissionRequests limited to 8 bytes" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "Requested AES mode is unsupported" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Requested resource not found" -msgstr "" - -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -msgid "Right channel unsupported" -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Right format but not supported" -msgstr "" - -#: main.c -msgid "Running in safe mode! Not running saved code.\n" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "SD card CSD format not supported" -msgstr "" - -#: ports/cxd56/common-hal/sdioio/SDCard.c -msgid "SDCard init" -msgstr "" - -#: ports/stm/common-hal/sdioio/SDCard.c -#, c-format -msgid "SDIO GetCardInfo Error %d" -msgstr "" - -#: ports/espressif/common-hal/sdioio/SDCard.c -#: ports/stm/common-hal/sdioio/SDCard.c -#, c-format -msgid "SDIO Init Error %x" -msgstr "" - -#: ports/espressif/common-hal/busio/SPI.c -msgid "SPI configuration failed" -msgstr "" - -#: ports/stm/common-hal/busio/SPI.c -msgid "SPI init error" -msgstr "" - -#: ports/analog/common-hal/busio/SPI.c -msgid "SPI needs MOSI, MISO, and SCK" -msgstr "" - -#: ports/raspberrypi/common-hal/busio/SPI.c -msgid "SPI peripheral in use" -msgstr "" - -#: ports/stm/common-hal/busio/SPI.c -msgid "SPI re-init" -msgstr "" - -#: shared-bindings/is31fl3741/FrameBuffer.c -msgid "Scale dimensions must divide by 3" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "Scan already in progress. Stop with stop_scan." -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c -msgid "Serializer in use" -msgstr "" - -#: shared-bindings/ssl/SSLContext.c -msgid "Server side context cannot have hostname" -msgstr "" - -#: ports/cxd56/common-hal/camera/Camera.c -msgid "Size not supported" -msgstr "" - -#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c -#: shared-bindings/nvm/ByteArray.c -msgid "Slice and value different lengths." -msgstr "" - -#: shared-bindings/displayio/Bitmap.c shared-bindings/displayio/Group.c -#: shared-bindings/displayio/TileGrid.c -#: shared-bindings/memorymonitor/AllocationSize.c -#: shared-bindings/pulseio/PulseIn.c -#: shared-bindings/tilepalettemapper/TilePaletteMapper.c -msgid "Slices not supported" -msgstr "" - -#: ports/espressif/common-hal/socketpool/SocketPool.c -#: ports/raspberrypi/common-hal/socketpool/SocketPool.c -msgid "SocketPool can only be used with wifi.radio" -msgstr "" - -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c -msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" -msgstr "" - -#: shared-bindings/aesio/aes.c -msgid "Source and destination buffers must be the same length" -msgstr "" - -#: shared-bindings/paralleldisplaybus/ParallelBus.c -msgid "Specify exactly one of data0 or data_pins" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Stack overflow. Increase stack size." -msgstr "" - -#: shared-bindings/alarm/time/TimeAlarm.c -msgid "Supply one of monotonic_time or epoch_time" -msgstr "" - -#: shared-bindings/gnss/GNSS.c -msgid "System entry must be gnss.SatelliteSystem" -msgstr "" - -#: ports/stm/common-hal/microcontroller/Processor.c -msgid "Temperature read timed out" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "The `microcontroller` module was used to boot into safe mode." -msgstr "" - -#: py/obj.c -msgid "The above exception was the direct cause of the following exception:" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -msgid "The length of rgb_pins must be 6, 12, 18, 24, or 30" -msgstr "" - -#: shared-module/audiocore/__init__.c -msgid "The sample's %q does not match" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Third-party firmware fatal error." -msgstr "" - -#: shared-module/imagecapture/ParallelImageCapture.c -msgid "This microcontroller does not support continuous capture." -msgstr "" - -#: shared-module/paralleldisplaybus/ParallelBus.c -msgid "" -"This microcontroller only supports data0=, not data_pins=, because it " -"requires contiguous pins." -msgstr "" - -#: shared-bindings/displayio/TileGrid.c -msgid "Tile height must exactly divide bitmap height" -msgstr "" - -#: shared-bindings/displayio/TileGrid.c -#: shared-bindings/tilepalettemapper/TilePaletteMapper.c -#: shared-module/displayio/TileGrid.c -msgid "Tile index out of bounds" -msgstr "" - -#: shared-bindings/displayio/TileGrid.c -msgid "Tile width must exactly divide bitmap width" -msgstr "" - -#: shared-module/tilepalettemapper/TilePaletteMapper.c -msgid "TilePaletteMapper may only be bound to a TileGrid once" -msgstr "" - -#: shared-bindings/alarm/time/TimeAlarm.c -msgid "Time is in the past." -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/nordic/common-hal/_bleio/Adapter.c -#, c-format -msgid "Timeout is too long: Maximum timeout length is %d seconds" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "Timeout must be < 100 seconds" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -msgid "Too many channels in sample" -msgstr "" - -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -msgid "Too many channels in sample." -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -msgid "Too many descriptors" -msgstr "" - -#: shared-module/displayio/__init__.c -msgid "Too many display busses; forgot displayio.release_displays() ?" -msgstr "" - -#: shared-module/displayio/__init__.c -msgid "Too many displays" -msgstr "" - -#: ports/espressif/common-hal/_bleio/PacketBuffer.c -#: ports/nordic/common-hal/_bleio/PacketBuffer.c -msgid "Total data to write is larger than %q" -msgstr "" - -#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c -#: ports/stm/common-hal/alarm/touch/TouchAlarm.c -msgid "Touch alarms not available" -msgstr "" - -#: py/obj.c -msgid "Traceback (most recent call last):\n" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "UART de-init" -msgstr "" - -#: ports/cxd56/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c -#: ports/stm/common-hal/busio/UART.c -msgid "UART init" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "UART needs TX & RX" -msgstr "" - -#: ports/raspberrypi/common-hal/busio/UART.c -msgid "UART peripheral in use" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "UART re-init" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "UART read error" -msgstr "" - -#: ports/analog/common-hal/busio/UART.c -msgid "UART transaction timeout" -msgstr "" - -#: ports/stm/common-hal/busio/UART.c -msgid "UART write" -msgstr "" - -#: main.c -msgid "UID:" -msgstr "" - -#: shared-module/usb_hid/Device.c -msgid "USB busy" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "USB devices need more endpoints than are available." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "USB devices specify too many interface names." -msgstr "" - -#: shared-module/usb_hid/Device.c -msgid "USB error" -msgstr "" - -#: shared-bindings/_bleio/UUID.c -msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" -msgstr "" - -#: shared-bindings/_bleio/UUID.c -msgid "UUID value is not str, int or byte buffer" -msgstr "" - -#: ports/raspberrypi/common-hal/memorymap/AddressRange.c -msgid "Unable to access unaligned IO register" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c -#: ports/atmel-samd/common-hal/audioio/AudioOut.c -#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c -#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c -msgid "Unable to allocate buffers for signed conversion" -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "Unable to allocate to the heap." -msgstr "" - -#: ports/espressif/common-hal/busio/I2C.c -#: ports/espressif/common-hal/busio/SPI.c -msgid "Unable to create lock" -msgstr "" - -#: shared-module/i2cdisplaybus/I2CDisplayBus.c -#: shared-module/is31fl3741/IS31FL3741.c -#, c-format -msgid "Unable to find I2C Display at %x" -msgstr "" - -#: py/parse.c -msgid "Unable to init parser" -msgstr "" - -#: shared-module/displayio/OnDiskBitmap.c -msgid "Unable to read color palette data" -msgstr "" - -#: ports/mimxrt10xx/common-hal/canio/CAN.c -msgid "Unable to send CAN Message: all Tx message buffers are busy" -msgstr "" - -#: ports/espressif/common-hal/mdns/Server.c -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "Unable to start mDNS query" -msgstr "" - -#: shared-bindings/nvm/ByteArray.c -msgid "Unable to write to nvm." -msgstr "" - -#: ports/raspberrypi/common-hal/memorymap/AddressRange.c -msgid "Unable to write to read-only memory" -msgstr "" - -#: shared-bindings/alarm/SleepMemory.c -msgid "Unable to write to sleep_memory." -msgstr "" - -#: ports/nordic/common-hal/_bleio/UUID.c -msgid "Unexpected nrfx uuid type" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown BLE error at %s:%d: %d" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown BLE error: %d" -msgstr "" - -#: ports/espressif/common-hal/max3421e/Max3421E.c -#: ports/raspberrypi/common-hal/wifi/__init__.c -#, c-format -msgid "Unknown error code %d" -msgstr "" - -#: shared-bindings/wifi/Radio.c -#, c-format -msgid "Unknown failure %d" -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown gatt error: 0x%04x" -msgstr "" - -#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c -#: supervisor/shared/safe_mode.c -msgid "Unknown reason." -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown security error: 0x%04x" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown system firmware error at %s:%d: %d" -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown system firmware error: %04x" -msgstr "" - -#: ports/espressif/common-hal/_bleio/__init__.c -#, c-format -msgid "Unknown system firmware error: %d" -msgstr "" - -#: shared-bindings/adafruit_pixelbuf/PixelBuf.c -#: shared-module/_pixelmap/PixelMap.c -#, c-format -msgid "Unmatched number of items on RHS (expected %d, got %d)." -msgstr "" - -#: ports/nordic/common-hal/_bleio/__init__.c -msgid "" -"Unspecified issue. Can be that the pairing prompt on the other device was " -"declined or ignored." -msgstr "" - -#: shared-module/jpegio/JpegDecoder.c -msgid "Unsupported JPEG (may be progressive)" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "Unsupported colorspace" -msgstr "" - -#: shared-module/displayio/bus_core.c -msgid "Unsupported display bus type" -msgstr "" - -#: shared-bindings/hashlib/__init__.c -msgid "Unsupported hash algorithm" -msgstr "" - -#: ports/espressif/common-hal/socketpool/Socket.c -#: ports/zephyr-cp/common-hal/socketpool/Socket.c -msgid "Unsupported socket type" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Adapter.c -#: ports/espressif/common-hal/dualbank/__init__.c -msgid "Update failed" -msgstr "" - -#: ports/zephyr-cp/common-hal/busio/I2C.c -#: ports/zephyr-cp/common-hal/busio/SPI.c -#: ports/zephyr-cp/common-hal/busio/UART.c -msgid "Use device tree to define %q devices" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Descriptor.c -msgid "Value length != required fixed length" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Descriptor.c -msgid "Value length > max_length" -msgstr "" - -#: ports/espressif/common-hal/espidf/__init__.c -msgid "Version was invalid" -msgstr "" - -#: ports/stm/common-hal/microcontroller/Processor.c -msgid "Voltage read timed out" -msgstr "" - -#: main.c -msgid "WARNING: Your code filename has two extensions\n" -msgstr "" - -#: ports/nordic/common-hal/watchdog/WatchDogTimer.c -msgid "WatchDogTimer cannot be deinitialized once mode is set to RESET" -msgstr "" - -#: py/builtinhelp.c -#, c-format -msgid "" -"Welcome to Adafruit CircuitPython %s!\n" -"\n" -"Visit circuitpython.org for more information.\n" -"\n" -"To list built-in modules type `help(\"modules\")`.\n" -msgstr "" - -#: supervisor/shared/web_workflow/web_workflow.c -msgid "Wi-Fi: " -msgstr "" - -#: ports/espressif/common-hal/wifi/Radio.c -#: ports/raspberrypi/common-hal/wifi/Radio.c -#: ports/zephyr-cp/common-hal/wifi/Radio.c -msgid "WiFi is not enabled" -msgstr "" - -#: main.c -msgid "Woken up by alarm.\n" -msgstr "" - -#: ports/espressif/common-hal/_bleio/PacketBuffer.c -#: ports/nordic/common-hal/_bleio/PacketBuffer.c -msgid "Writes not supported on Characteristic" -msgstr "" - -#: ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h -#: ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h -#: ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.h -#: ports/atmel-samd/boards/meowmeow/mpconfigboard.h -msgid "You pressed both buttons at start up." -msgstr "" - -#: ports/espressif/boards/m5stack_core_basic/mpconfigboard.h -#: ports/espressif/boards/m5stack_core_fire/mpconfigboard.h -#: ports/espressif/boards/m5stack_stick_c/mpconfigboard.h -#: ports/espressif/boards/m5stack_stick_c_plus/mpconfigboard.h -#: ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h -msgid "You pressed button A at start up." -msgstr "" - -#: ports/espressif/boards/m5stack_m5paper/mpconfigboard.h -msgid "You pressed button DOWN at start up." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "You pressed the BOOT button at start up" -msgstr "" - -#: ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.h -#: ports/espressif/boards/adafruit_itsybitsy_esp32/mpconfigboard.h -#: ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/mpconfigboard.h -msgid "You pressed the BOOT button at start up." -msgstr "" - -#: ports/espressif/boards/adafruit_huzzah32_breakout/mpconfigboard.h -msgid "You pressed the GPIO0 button at start up." -msgstr "" - -#: ports/espressif/boards/espressif_esp32_lyrat/mpconfigboard.h -msgid "You pressed the Rec button at start up." -msgstr "" - -#: ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h -msgid "You pressed the SW38 button at start up." -msgstr "" - -#: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h -#: ports/espressif/boards/vidi_x/mpconfigboard.h -msgid "You pressed the VOLUME button at start up." -msgstr "" - -#: ports/espressif/boards/m5stack_atom_echo/mpconfigboard.h -#: ports/espressif/boards/m5stack_atom_lite/mpconfigboard.h -#: ports/espressif/boards/m5stack_atom_matrix/mpconfigboard.h -#: ports/espressif/boards/m5stack_atom_u/mpconfigboard.h -msgid "You pressed the central button at start up." -msgstr "" - -#: ports/nordic/boards/aramcon2_badge/mpconfigboard.h -msgid "You pressed the left button at start up." -msgstr "" - -#: supervisor/shared/safe_mode.c -msgid "You pressed the reset button during boot." -msgstr "" - -#: supervisor/shared/micropython.c -msgid "[truncated due to length]" -msgstr "" - -#: py/objtype.c -msgid "__init__() should return None" -msgstr "" - -#: py/objtype.c -#, c-format -msgid "__init__() should return None, not '%s'" -msgstr "" - -#: py/objobject.c -msgid "__new__ arg must be a user-type" -msgstr "" - -#: extmod/modbinascii.c extmod/modhashlib.c py/objarray.c -msgid "a bytes-like object is required" -msgstr "" - -#: shared-bindings/i2cioexpander/IOExpander.c -msgid "address out of range" -msgstr "" - -#: shared-bindings/i2ctarget/I2CTarget.c -msgid "addresses is empty" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "already playing" -msgstr "" - -#: py/compile.c -msgid "annotation must be an identifier" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "arange: cannot compute length" -msgstr "" - -#: py/modbuiltins.c -msgid "arg is an empty sequence" -msgstr "" - -#: py/objobject.c -msgid "arg must be user-type" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "argsort argument must be an ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "argsort is not implemented for flattened arrays" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "argument must be None, an integer or a tuple of integers" -msgstr "" - -#: py/compile.c -msgid "argument name reused" -msgstr "" - -#: py/argcheck.c shared-bindings/_stage/__init__.c -#: shared-bindings/digitalio/DigitalInOut.c -msgid "argument num/types mismatch" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/numpy/transform.c -msgid "arguments must be ndarrays" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "array and index length must be equal" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "array has too many dimensions" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "array is too big" -msgstr "" - -#: py/objarray.c shared-bindings/alarm/SleepMemory.c -#: shared-bindings/memorymap/AddressRange.c shared-bindings/nvm/ByteArray.c -msgid "array/bytes required on right side" -msgstr "" - -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - -#: py/compile.c -msgid "async for/with outside async function" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "attempt to get (arg)min/(arg)max of empty sequence" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "attempt to get argmin/argmax of an empty sequence" -msgstr "" - -#: py/objstr.c -msgid "attributes not supported" -msgstr "" - -#: ports/espressif/common-hal/audioio/AudioOut.c -msgid "audio format not supported" -msgstr "" - -#: extmod/ulab/code/ulab_tools.c -msgid "axis is out of bounds" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c -msgid "axis must be None, or an integer" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "axis too long" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "background value out of range of target" -msgstr "" - -#: py/builtinevex.c -msgid "bad compile mode" -msgstr "" - -#: py/objstr.c -msgid "bad conversion specifier" -msgstr "" - -#: py/objstr.c -msgid "bad format string" -msgstr "" - -#: py/binary.c py/objarray.c -msgid "bad typecode" -msgstr "" - -#: py/emitnative.c -msgid "binary op %q not implemented" -msgstr "" - -#: ports/espressif/common-hal/audiobusio/PDMIn.c -msgid "bit_depth must be 8, 16, 24, or 32." -msgstr "" - -#: shared-module/bitmapfilter/__init__.c -msgid "bitmap size and depth must match" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "bitmap sizes must match" -msgstr "" - -#: extmod/modrandom.c -msgid "bits must be 32 or less" -msgstr "" - -#: shared-bindings/audiofreeverb/Freeverb.c -msgid "bits_per_sample must be 16" -msgstr "" - -#: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c -#: shared-bindings/audiodelays/MultiTapDelay.c -#: shared-bindings/audiodelays/PitchShift.c -#: shared-bindings/audiofilters/Distortion.c -#: shared-bindings/audiofilters/Filter.c shared-bindings/audiofilters/Phaser.c -#: shared-bindings/audiomixer/Mixer.c -msgid "bits_per_sample must be 8 or 16" -msgstr "" - -#: py/emitinlinethumb.c -msgid "branch not in range" -msgstr "" - -#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c -msgid "buffer is smaller than requested size" -msgstr "" - -#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c -msgid "buffer size must be a multiple of element size" -msgstr "" - -#: shared-module/struct/__init__.c -msgid "buffer size must match format" -msgstr "" - -#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c -msgid "buffer slices must be of equal length" -msgstr "" - -#: py/modstruct.c shared-module/struct/__init__.c -msgid "buffer too small" -msgstr "" - -#: shared-bindings/socketpool/Socket.c shared-bindings/ssl/SSLSocket.c -msgid "buffer too small for requested bytes" -msgstr "" - -#: py/emitbc.c -msgid "bytecode overflow" -msgstr "" - -#: py/objarray.c -msgid "bytes length not a multiple of item size" -msgstr "" - -#: py/objstr.c -msgid "bytes value out of range" -msgstr "" - -#: ports/atmel-samd/bindings/samd/Clock.c -msgid "calibration is out of range" -msgstr "" - -#: ports/atmel-samd/bindings/samd/Clock.c -msgid "calibration is read only" -msgstr "" - -#: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c -#: shared-module/vectorio/Rectangle.c -msgid "can only have one parent" -msgstr "" - -#: py/emitinlinerv32.c -msgid "can only have up to 4 parameters for RV32 assembly" -msgstr "" - -#: py/emitinlinethumb.c -msgid "can only have up to 4 parameters to Thumb assembly" -msgstr "" - -#: py/emitinlinextensa.c -msgid "can only have up to 4 parameters to Xtensa assembly" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "can only specify one unknown dimension" -msgstr "" - -#: py/objtype.c -msgid "can't add special method to already-subclassed class" -msgstr "" - -#: py/compile.c -msgid "can't assign to expression" -msgstr "" - -#: extmod/modasyncio.c -msgid "can't cancel self" -msgstr "" - -#: py/obj.c shared-module/adafruit_pixelbuf/PixelBuf.c -msgid "can't convert %q to %q" -msgstr "" - -#: py/obj.c -#, c-format -msgid "can't convert %s to complex" -msgstr "" - -#: py/obj.c -#, c-format -msgid "can't convert %s to float" -msgstr "" - -#: py/objint.c py/runtime.c -#, c-format -msgid "can't convert %s to int" -msgstr "" - -#: py/objstr.c -msgid "can't convert '%q' object to %q implicitly" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "can't convert complex to float" -msgstr "" - -#: py/obj.c -msgid "can't convert to complex" -msgstr "" - -#: py/obj.c -msgid "can't convert to float" -msgstr "" - -#: py/runtime.c -msgid "can't convert to int" -msgstr "" - -#: py/objstr.c -msgid "can't convert to str implicitly" -msgstr "" - -#: py/objtype.c -msgid "can't create '%q' instances" -msgstr "" - -#: py/compile.c -msgid "can't declare nonlocal in outer code" -msgstr "" - -#: py/compile.c -msgid "can't delete expression" -msgstr "" - -#: py/emitnative.c -msgid "can't do binary op between '%q' and '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't do unary op of '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't implicitly convert '%q' to 'bool'" -msgstr "" - -#: py/runtime.c -msgid "can't import name %q" -msgstr "" - -#: py/emitnative.c -msgid "can't load from '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't load with '%q' index" -msgstr "" - -#: py/builtinimport.c -msgid "can't perform relative import" -msgstr "" - -#: py/objgenerator.c -msgid "can't send non-None value to a just-started generator" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "can't set 512 block size" -msgstr "" - -#: py/objexcept.c py/objnamedtuple.c -msgid "can't set attribute" -msgstr "" - -#: py/runtime.c -msgid "can't set attribute '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't store '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't store to '%q'" -msgstr "" - -#: py/emitnative.c -msgid "can't store with '%q' index" -msgstr "" - -#: py/objstr.c -msgid "" -"can't switch from automatic field numbering to manual field specification" -msgstr "" - -#: py/objstr.c -msgid "" -"can't switch from manual field specification to automatic field numbering" -msgstr "" - -#: py/objcomplex.c -msgid "can't truncate-divide a complex number" -msgstr "" - -#: extmod/modasyncio.c -msgid "can't wait" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot assign new shape" -msgstr "" - -#: extmod/ulab/code/ndarray_operators.c -msgid "cannot cast output with casting rule" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot convert complex to dtype" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot convert complex type" -msgstr "" - -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot delete array elements" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "cannot reshape array" -msgstr "" - -#: py/emitnative.c -msgid "casting" -msgstr "" - -#: ports/stm/common-hal/pwmio/PWMOut.c -msgid "channel re-init" -msgstr "" - -#: shared-bindings/_stage/Text.c -msgid "chars buffer too small" -msgstr "" - -#: py/modbuiltins.c -msgid "chr() arg not in range(0x110000)" -msgstr "" - -#: py/modbuiltins.c -msgid "chr() arg not in range(256)" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "clip point must be (x,y) tuple" -msgstr "" - -#: shared-bindings/msgpack/ExtType.c -msgid "code outside range 0~127" -msgstr "" - -#: shared-bindings/displayio/Palette.c -msgid "color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)" -msgstr "" - -#: shared-bindings/displayio/Palette.c -msgid "color buffer must be a buffer, tuple, list, or int" -msgstr "" - -#: shared-bindings/displayio/Palette.c -msgid "color buffer must be a bytearray or array of type 'b' or 'B'" -msgstr "" - -#: shared-bindings/displayio/Palette.c -msgid "color must be between 0x000000 and 0xffffff" -msgstr "" - -#: py/emitnative.c -msgid "comparison of int and uint" -msgstr "" - -#: py/objcomplex.c -msgid "complex divide by zero" -msgstr "" - -#: py/objfloat.c py/parsenum.c -msgid "complex values not supported" -msgstr "" - -#: extmod/modzlib.c -msgid "compression header" -msgstr "" - -#: py/emitnative.c -msgid "conversion to object" -msgstr "" - -#: extmod/ulab/code/numpy/filter.c -msgid "convolve arguments must be linear arrays" -msgstr "" - -#: extmod/ulab/code/numpy/filter.c -msgid "convolve arguments must be ndarrays" -msgstr "" - -#: extmod/ulab/code/numpy/filter.c -msgid "convolve arguments must not be empty" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "corrupted file" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "could not invert Vandermonde matrix" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "couldn't determine SD card version" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "cross is defined for 1D arrays of length 3" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "data must be iterable" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "data must be of equal length" -msgstr "" - -#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c -#, c-format -msgid "data pin #%d in use" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "data type not understood" -msgstr "" - -#: py/parsenum.c -msgid "decimal numbers not supported" -msgstr "" - -#: py/compile.c -msgid "default 'except' must be last" -msgstr "" - -#: shared-bindings/msgpack/__init__.c -msgid "default is not a function" -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "" -"destination buffer must be a bytearray or array of type 'B' for bit_depth = 8" -msgstr "" - -#: shared-bindings/audiobusio/PDMIn.c -msgid "destination buffer must be an array of type 'H' for bit_depth = 16" -msgstr "" - -#: py/objdict.c -msgid "dict update sequence has wrong length" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "diff argument must be an ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "differentiation order out of range" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c -msgid "dimensions do not match" -msgstr "" - -#: py/emitnative.c -msgid "div/mod not implemented for uint" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "divide by zero" -msgstr "" - -#: py/runtime.c -msgid "division by zero" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "dtype must be float, or complex" -msgstr "" - -#: extmod/ulab/code/ndarray_operators.c -msgid "dtype of int32 is not supported" -msgstr "" - -#: py/objdeque.c -msgid "empty" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "empty file" -msgstr "" - -#: extmod/modasyncio.c extmod/modheapq.c -msgid "empty heap" -msgstr "" - -#: py/objstr.c -msgid "empty separator" -msgstr "" - -#: shared-bindings/random/__init__.c -msgid "empty sequence" -msgstr "" - -#: py/objstr.c -msgid "end of format while looking for conversion specifier" -msgstr "" - -#: shared-bindings/alarm/time/TimeAlarm.c -msgid "epoch_time not supported on this board" -msgstr "" - -#: ports/nordic/common-hal/busio/UART.c -#, c-format -msgid "error = 0x%08lX" -msgstr "" - -#: py/runtime.c -msgid "exceptions must derive from BaseException" -msgstr "" - -#: py/objstr.c -msgid "expected ':' after format specifier" -msgstr "" - -#: py/obj.c -msgid "expected tuple/list" -msgstr "" - -#: py/modthread.c -msgid "expecting a dict for keyword args" -msgstr "" - -#: py/compile.c -msgid "expecting an assembler instruction" -msgstr "" - -#: py/compile.c -msgid "expecting just a value for set" -msgstr "" - -#: py/compile.c -msgid "expecting key:value for dict" -msgstr "" - -#: shared-bindings/msgpack/__init__.c -msgid "ext_hook is not a function" -msgstr "" - -#: py/argcheck.c -msgid "extra keyword arguments given" -msgstr "" - -#: py/argcheck.c -msgid "extra positional arguments given" -msgstr "" - -#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c -#: shared-bindings/displayio/OnDiskBitmap.c shared-bindings/gifio/OnDiskGif.c -#: shared-bindings/synthio/__init__.c shared-module/gifio/GifWriter.c -msgid "file must be a file opened in byte mode" -msgstr "" - -#: shared-bindings/traceback/__init__.c -msgid "file write is not available" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "first argument must be a callable" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "first argument must be a function" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "first argument must be a tuple of ndarrays" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c extmod/ulab/code/numpy/vector.c -msgid "first argument must be an ndarray" -msgstr "" - -#: py/objtype.c -msgid "first argument to super() must be type" -msgstr "" - -#: extmod/ulab/code/scipy/linalg/linalg.c -msgid "first two arguments must be ndarrays" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "flattening order must be either 'C', or 'F'" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "flip argument must be an ndarray" -msgstr "" - -#: py/objint.c -msgid "float too big" -msgstr "" - -#: py/nativeglue.c -msgid "float unsupported" -msgstr "" - -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - -#: extmod/moddeflate.c -msgid "format" -msgstr "" - -#: py/objstr.c -msgid "format needs a dict" -msgstr "" - -#: py/objstr.c -msgid "format string didn't convert all arguments" -msgstr "" - -#: py/objstr.c -msgid "format string needs more arguments" -msgstr "" - -#: py/objdeque.c -msgid "full" -msgstr "" - -#: py/argcheck.c -msgid "function doesn't take keyword arguments" -msgstr "" - -#: py/argcheck.c -#, c-format -msgid "function expected at most %d arguments, got %d" -msgstr "" - -#: py/bc.c py/objnamedtuple.c -msgid "function got multiple values for argument '%q'" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "function has the same sign at the ends of interval" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "function is defined for ndarrays only" -msgstr "" - -#: extmod/ulab/code/numpy/carray/carray.c -msgid "function is implemented for ndarrays only" -msgstr "" - -#: py/argcheck.c -#, c-format -msgid "function missing %d required positional arguments" -msgstr "" - -#: py/bc.c -msgid "function missing keyword-only argument" -msgstr "" - -#: py/bc.c -msgid "function missing required keyword argument '%q'" -msgstr "" - -#: py/bc.c -#, c-format -msgid "function missing required positional argument #%d" -msgstr "" - -#: py/argcheck.c py/bc.c py/objnamedtuple.c shared-bindings/_eve/__init__.c -#: shared-bindings/time/__init__.c -#, c-format -msgid "function takes %d positional arguments but %d were given" -msgstr "" - -#: py/objgenerator.c -msgid "generator already executing" -msgstr "" - -#: py/objgenerator.c -msgid "generator ignored GeneratorExit" -msgstr "" - -#: py/objgenerator.c py/runtime.c -msgid "generator raised StopIteration" -msgstr "" - -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - -#: extmod/modhashlib.c -msgid "hash is final" -msgstr "" - -#: extmod/modheapq.c -msgid "heap must be a list" -msgstr "" - -#: py/compile.c -msgid "identifier redefined as global" -msgstr "" - -#: py/compile.c -msgid "identifier redefined as nonlocal" -msgstr "" - -#: py/compile.c -msgid "import * not at module level" -msgstr "" - -#: py/persistentcode.c -msgid "incompatible .mpy arch" -msgstr "" - -#: py/persistentcode.c -msgid "incompatible .mpy file" -msgstr "" - -#: py/objstr.c -msgid "incomplete format" -msgstr "" - -#: py/objstr.c -msgid "incomplete format key" -msgstr "" - -#: extmod/modbinascii.c -msgid "incorrect padding" -msgstr "" - -#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/transform.c -msgid "index is out of bounds" -msgstr "" - -#: shared-bindings/_pixelmap/PixelMap.c -msgid "index must be tuple or int" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c -#: ports/espressif/common-hal/pulseio/PulseIn.c -#: shared-bindings/bitmaptools/__init__.c -msgid "index out of range" -msgstr "" - -#: py/obj.c -msgid "indices must be integers" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "indices must be integers, slices, or Boolean lists" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "initial values must be iterable" -msgstr "" - -#: py/compile.c -msgid "inline assembler must be a function" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "input and output dimensions differ" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "input and output shapes differ" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "input argument must be an integer, a tuple, or a list" -msgstr "" - -#: extmod/ulab/code/numpy/fft/fft_tools.c -msgid "input array length must be power of 2" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "input arrays are not compatible" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "input data must be an iterable" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "input dtype must be float or complex" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "input is not iterable" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "input matrix is asymmetric" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -#: extmod/ulab/code/scipy/linalg/linalg.c -msgid "input matrix is singular" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "input must be 1- or 2-d" -msgstr "" - -#: extmod/ulab/code/numpy/carray/carray.c -msgid "input must be a 1D ndarray" -msgstr "" - -#: extmod/ulab/code/scipy/linalg/linalg.c extmod/ulab/code/user/user.c -msgid "input must be a dense ndarray" -msgstr "" - -#: extmod/ulab/code/user/user.c shared-bindings/_eve/__init__.c -msgid "input must be an ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/carray/carray.c -msgid "input must be an ndarray, or a scalar" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "input must be one-dimensional" -msgstr "" - -#: extmod/ulab/code/ulab_tools.c -msgid "input must be square matrix" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "input must be tuple, list, range, or ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "input vectors must be of equal length" -msgstr "" - -#: extmod/ulab/code/numpy/approx.c -msgid "interp is defined for 1D iterables of equal length" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -#, c-format -msgid "interval must be in range %s-%s" -msgstr "" - -#: py/compile.c -msgid "invalid arch" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -#, c-format -msgid "invalid bits_per_pixel %d, must be, 1, 2, 4, 8, 16, 24, or 32" -msgstr "" - -#: shared-module/ssl/SSLSocket.c -msgid "invalid cert" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -#, c-format -msgid "invalid element size %d for bits_per_pixel %d\n" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -#, c-format -msgid "invalid element_size %d, must be, 1, 2, or 4" -msgstr "" - -#: shared-bindings/traceback/__init__.c -msgid "invalid exception" -msgstr "" - -#: py/objstr.c -msgid "invalid format specifier" -msgstr "" - -#: shared-bindings/wifi/Radio.c -msgid "invalid hostname" -msgstr "" - -#: shared-module/ssl/SSLSocket.c -msgid "invalid key" -msgstr "" - -#: py/compile.c -msgid "invalid micropython decorator" -msgstr "" - -#: ports/espressif/common-hal/espcamera/Camera.c -msgid "invalid setting" -msgstr "" - -#: shared-bindings/random/__init__.c -msgid "invalid step" -msgstr "" - -#: py/compile.c py/parse.c -msgid "invalid syntax" -msgstr "" - -#: py/parsenum.c -msgid "invalid syntax for integer" -msgstr "" - -#: py/parsenum.c -#, c-format -msgid "invalid syntax for integer with base %d" -msgstr "" - -#: py/parsenum.c -msgid "invalid syntax for number" -msgstr "" - -#: py/objtype.c -msgid "issubclass() arg 1 must be a class" -msgstr "" - -#: py/objtype.c -msgid "issubclass() arg 2 must be a class or a tuple of classes" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "iterations did not converge" -msgstr "" - -#: py/objstr.c -msgid "join expects a list of str/bytes objects consistent with self object" -msgstr "" - -#: py/argcheck.c -msgid "keyword argument(s) not implemented - use normal args instead" -msgstr "" - -#: py/emitinlinethumb.c py/emitinlinextensa.c -msgid "label '%q' not defined" -msgstr "" - -#: py/compile.c -msgid "label redefined" -msgstr "" - -#: py/objarray.c -msgid "lhs and rhs should be compatible" -msgstr "" - -#: py/emitnative.c -msgid "local '%q' has type '%q' but source is '%q'" -msgstr "" - -#: py/emitnative.c -msgid "local '%q' used before type known" -msgstr "" - -#: py/vm.c -msgid "local variable referenced before assignment" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -msgid "loopback + silent mode not supported by peripheral" -msgstr "" - -#: ports/espressif/common-hal/mdns/Server.c -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "mDNS already initialized" -msgstr "" - -#: ports/espressif/common-hal/mdns/Server.c -#: ports/raspberrypi/common-hal/mdns/Server.c -msgid "mDNS only works with built-in WiFi" -msgstr "" - -#: py/parse.c -msgid "malformed f-string" -msgstr "" - -#: shared-bindings/_stage/Layer.c -msgid "map buffer too small" -msgstr "" - -#: py/modmath.c shared-bindings/math/__init__.c -msgid "math domain error" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "matrix is not positive definite" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Descriptor.c -#: ports/nordic/common-hal/_bleio/Characteristic.c -#: ports/nordic/common-hal/_bleio/Descriptor.c -#, c-format -msgid "max_length must be 0-%d when fixed_length is %s" -msgstr "" - -#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/random/random.c -msgid "maximum number of dimensions is " -msgstr "" - -#: py/runtime.c -msgid "maximum recursion depth exceeded" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "maxiter must be > 0" -msgstr "" - -#: extmod/ulab/code/scipy/optimize/optimize.c -msgid "maxiter should be > 0" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "median argument must be an ndarray" -msgstr "" - -#: py/runtime.c -#, c-format -msgid "memory allocation failed, allocating %u bytes" -msgstr "" - -#: py/runtime.c -msgid "memory allocation failed, heap is locked" -msgstr "" - -#: py/objarray.c -msgid "memoryview offset too large" -msgstr "" - -#: py/objarray.c -msgid "memoryview: length is not a multiple of itemsize" -msgstr "" - -#: extmod/modtime.c -msgid "mktime needs a tuple of length 8 or 9" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "mode must be complete, or reduced" -msgstr "" - -#: py/builtinimport.c -msgid "module not found" -msgstr "" - -#: ports/espressif/common-hal/wifi/Monitor.c -msgid "monitor init failed" -msgstr "" - -#: extmod/ulab/code/numpy/poly.c -msgid "more degrees of freedom than data points" -msgstr "" - -#: py/compile.c -msgid "multiple *x in assignment" -msgstr "" - -#: py/objtype.c -msgid "multiple bases have instance lay-out conflict" -msgstr "" - -#: py/objtype.c -msgid "multiple inheritance not supported" -msgstr "" - -#: py/emitnative.c -msgid "must raise an object" -msgstr "" - -#: py/modbuiltins.c -msgid "must use keyword argument for key function" -msgstr "" - -#: py/runtime.c -msgid "name '%q' isn't defined" -msgstr "" - -#: py/runtime.c -msgid "name not defined" -msgstr "" - -#: py/qstr.c -msgid "name too long" -msgstr "" - -#: py/persistentcode.c -msgid "native code in .mpy unsupported" -msgstr "" - -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - -#: py/emitnative.c -msgid "native yield" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "ndarray length overflows" -msgstr "" - -#: py/runtime.c -#, c-format -msgid "need more than %d values to unpack" -msgstr "" - -#: py/modmath.c -msgid "negative factorial" -msgstr "" - -#: py/objint_longlong.c py/objint_mpz.c py/runtime.c -msgid "negative power with no float support" -msgstr "" - -#: py/objint_mpz.c py/runtime.c -msgid "negative shift count" -msgstr "" - -#: shared-bindings/_pixelmap/PixelMap.c -msgid "nested index must be int" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "no SD card" -msgstr "" - -#: py/vm.c -msgid "no active exception to reraise" -msgstr "" - -#: py/compile.c -msgid "no binding for nonlocal found" -msgstr "" - -#: shared-module/msgpack/__init__.c -msgid "no default packer" -msgstr "" - -#: extmod/modrandom.c extmod/ulab/code/numpy/random/random.c -msgid "no default seed" -msgstr "" - -#: py/builtinimport.c -msgid "no module named '%q'" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "no response from SD card" -msgstr "" - -#: ports/espressif/common-hal/espcamera/Camera.c py/objobject.c py/runtime.c -msgid "no such attribute" -msgstr "" - -#: ports/espressif/common-hal/_bleio/Connection.c -#: ports/nordic/common-hal/_bleio/Connection.c -msgid "non-UUID found in service_uuids_whitelist" -msgstr "" - -#: py/compile.c -msgid "non-default argument follows default argument" -msgstr "" - -#: py/objstr.c -msgid "non-hex digit" -msgstr "" - -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "non-zero timeout must be > 0.01" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "non-zero timeout must be >= interval" -msgstr "" - -#: shared-bindings/_bleio/UUID.c -msgid "not a 128-bit UUID" -msgstr "" - -#: py/parse.c -msgid "not a constant" -msgstr "" - -#: extmod/ulab/code/numpy/carray/carray_tools.c -msgid "not implemented for complex dtype" -msgstr "" - -#: extmod/ulab/code/numpy/bitwise.c -msgid "not supported for input types" -msgstr "" - -#: shared-bindings/i2cioexpander/IOExpander.c -msgid "num_pins must be 8 or 16" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "number of points must be at least 2" -msgstr "" - -#: py/builtinhelp.c -msgid "object " -msgstr "" - -#: py/obj.c -#, c-format -msgid "object '%s' isn't a tuple or list" -msgstr "" - -#: shared-bindings/digitalio/DigitalInOutProtocol.c -msgid "object does not support DigitalInOut protocol" -msgstr "" - -#: py/obj.c -msgid "object doesn't support item assignment" -msgstr "" - -#: py/obj.c -msgid "object doesn't support item deletion" -msgstr "" - -#: py/obj.c -msgid "object has no len" -msgstr "" - -#: py/obj.c -msgid "object isn't subscriptable" -msgstr "" - -#: py/runtime.c -msgid "object not an iterator" -msgstr "" - -#: py/objtype.c py/runtime.c -msgid "object not callable" -msgstr "" - -#: py/sequence.c shared-bindings/displayio/Group.c -msgid "object not in sequence" -msgstr "" - -#: py/runtime.c -msgid "object not iterable" -msgstr "" - -#: py/obj.c -#, c-format -msgid "object of type '%s' has no len()" -msgstr "" - -#: py/obj.c -msgid "object with buffer protocol required" -msgstr "" - -#: supervisor/shared/web_workflow/web_workflow.c -msgid "off" -msgstr "" - -#: extmod/ulab/code/utils/utils.c -msgid "offset is too large" -msgstr "" - -#: shared-bindings/dualbank/__init__.c -msgid "offset must be >= 0" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "offset must be non-negative and no greater than buffer length" -msgstr "" - -#: ports/nordic/common-hal/audiobusio/PDMIn.c -#: ports/stm/common-hal/audiobusio/PDMIn.c -msgid "only bit_depth=16 is supported" -msgstr "" - -#: ports/stm/common-hal/audiobusio/PDMIn.c -msgid "only mono is supported" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "only ndarrays can be concatenated" -msgstr "" - -#: ports/stm/common-hal/audiobusio/PDMIn.c -msgid "only oversample=64 is supported" -msgstr "" - -#: ports/nordic/common-hal/audiobusio/PDMIn.c -#: ports/stm/common-hal/audiobusio/PDMIn.c -msgid "only sample_rate=16000 is supported" -msgstr "" - -#: py/objarray.c py/objstr.c py/objstrunicode.c py/objtuple.c -#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c -#: shared-bindings/nvm/ByteArray.c -msgid "only slices with step=1 (aka None) are supported" -msgstr "" - -#: py/vm.c -msgid "opcode" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: expecting %q" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: must not be zero" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: out of range" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: undefined label '%q'" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q' argument %d: unknown register" -msgstr "" - -#: py/emitinlinerv32.c -msgid "opcode '%q': expecting %d arguments" -msgstr "" - -#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/bitwise.c -#: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/vector.c -msgid "operands could not be broadcast together" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "operation is defined for 2D arrays only" -msgstr "" - -#: extmod/ulab/code/numpy/linalg/linalg.c -msgid "operation is defined for ndarrays only" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "operation is implemented for 1D Boolean arrays only" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "operation is not implemented on ndarrays" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "operation is not supported for given type" -msgstr "" - -#: extmod/ulab/code/ndarray_operators.c -msgid "operation not supported for the input types" -msgstr "" - -#: py/modbuiltins.c -msgid "ord expects a character" -msgstr "" - -#: py/modbuiltins.c -#, c-format -msgid "ord() expected a character, but string of length %d found" -msgstr "" - -#: extmod/ulab/code/utils/utils.c -msgid "out array is too small" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "out has wrong type" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "out keyword is not supported for complex dtype" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "out keyword is not supported for function" -msgstr "" - -#: extmod/ulab/code/utils/utils.c -msgid "out must be a float dense array" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "out must be an ndarray" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "out must be of float dtype" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "out of range of target" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "output array has wrong type" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "output array must be contiguous" -msgstr "" - -#: py/objint_mpz.c -msgid "overflow converting long int to machine word" -msgstr "" - -#: py/modstruct.c -#, c-format -msgid "pack expected %d items for packing (got %d)" -msgstr "" - -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - -#: py/emitinlinerv32.c -msgid "parameters must be registers in sequence a0 to a3" -msgstr "" - -#: py/emitinlinextensa.c -msgid "parameters must be registers in sequence a2 to a5" -msgstr "" - -#: py/emitinlinethumb.c -msgid "parameters must be registers in sequence r0 to r3" -msgstr "" - -#: extmod/vfs_posix_file.c -msgid "poll on file not available on win32" -msgstr "" - -#: ports/espressif/common-hal/pulseio/PulseIn.c -msgid "pop from an empty PulseIn" -msgstr "" - -#: ports/atmel-samd/common-hal/pulseio/PulseIn.c -#: ports/cxd56/common-hal/pulseio/PulseIn.c -#: ports/nordic/common-hal/pulseio/PulseIn.c -#: ports/raspberrypi/common-hal/pulseio/PulseIn.c -#: ports/stm/common-hal/pulseio/PulseIn.c py/objdict.c py/objlist.c py/objset.c -#: shared-bindings/ps2io/Ps2.c -msgid "pop from empty %q" -msgstr "" - -#: shared-bindings/socketpool/Socket.c -msgid "port must be >= 0" -msgstr "" - -#: py/compile.c -msgid "positional arg after **" -msgstr "" - -#: py/compile.c -msgid "positional arg after keyword arg" -msgstr "" - -#: py/objint_mpz.c -msgid "pow() 3rd argument cannot be 0" -msgstr "" - -#: py/objint_mpz.c -msgid "pow() with 3 arguments requires integers" -msgstr "" - -#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c -msgid "pull masks conflict with direction masks" -msgstr "" - -#: extmod/ulab/code/numpy/fft/fft_tools.c -msgid "real and imaginary parts must be of equal length" -msgstr "" - -#: py/builtinimport.c -msgid "relative import" -msgstr "" - -#: py/obj.c -#, c-format -msgid "requested length %d but object has length %d" -msgstr "" - -#: extmod/ulab/code/ndarray_operators.c -msgid "results cannot be cast to specified type" -msgstr "" - -#: py/compile.c -msgid "return annotation must be an identifier" -msgstr "" - -#: py/emitnative.c -msgid "return expected '%q' but got '%q'" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "rgb_pins[%d] duplicates another pin assignment" -msgstr "" - -#: shared-bindings/rgbmatrix/RGBMatrix.c -#, c-format -msgid "rgb_pins[%d] is not on the same port as clock" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "roll argument must be an ndarray" -msgstr "" - -#: py/objstr.c -msgid "rsplit(None,n)" -msgstr "" - -#: shared-bindings/audiofreeverb/Freeverb.c -msgid "samples_signed must be true" -msgstr "" - -#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c -#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c -msgid "sampling rate out of range" -msgstr "" - -#: py/modmicropython.c -msgid "schedule queue full" -msgstr "" - -#: py/builtinimport.c -msgid "script compilation not supported" -msgstr "" - -#: py/nativeglue.c -msgid "set unsupported" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "shape must be None, and integer or a tuple of integers" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "shape must be integer or tuple of integers" -msgstr "" - -#: shared-module/msgpack/__init__.c -msgid "short read" -msgstr "" - -#: py/objstr.c -msgid "sign not allowed in string format specifier" -msgstr "" - -#: py/objstr.c -msgid "sign not allowed with integer format specifier 'c'" -msgstr "" - -#: extmod/ulab/code/ulab_tools.c -msgid "size is defined for ndarrays only" -msgstr "" - -#: extmod/ulab/code/numpy/random/random.c -msgid "size must match out.shape when used together" -msgstr "" - -#: py/nativeglue.c -msgid "slice unsupported" -msgstr "" - -#: py/objint.c py/sequence.c -msgid "small int overflow" -msgstr "" - -#: main.c -msgid "soft reboot\n" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "sort argument must be an ndarray" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "sos array must be of shape (n_section, 6)" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "sos[:, 3] should be all ones" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "sosfilt requires iterable arguments" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "source palette too large" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "source_bitmap must have value_count of 2 or 65536" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "source_bitmap must have value_count of 65536" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "source_bitmap must have value_count of 8" -msgstr "" - -#: extmod/modre.c -msgid "splitting with sub-captures" -msgstr "" - -#: shared-bindings/random/__init__.c -msgid "stop not reachable from start" -msgstr "" - -#: py/stream.c shared-bindings/getpass/__init__.c -msgid "stream operation not supported" -msgstr "" - -#: py/objarray.c py/objstr.c -msgid "string argument without an encoding" -msgstr "" - -#: py/objstrunicode.c -msgid "string index out of range" -msgstr "" - -#: py/objstrunicode.c -#, c-format -msgid "string indices must be integers, not %s" -msgstr "" - -#: py/objarray.c py/objstr.c -msgid "substring not found" -msgstr "" - -#: py/compile.c -msgid "super() can't find self" -msgstr "" - -#: extmod/modjson.c -msgid "syntax error in JSON" -msgstr "" - -#: extmod/modtime.c -msgid "ticks interval overflow" -msgstr "" - -#: ports/nordic/common-hal/watchdog/WatchDogTimer.c -msgid "timeout duration exceeded the maximum supported value" -msgstr "" - -#: ports/nordic/common-hal/_bleio/Adapter.c -msgid "timeout must be < 655.35 secs" -msgstr "" - -#: ports/raspberrypi/common-hal/floppyio/__init__.c -msgid "timeout waiting for flux" -msgstr "" - -#: ports/raspberrypi/common-hal/floppyio/__init__.c -#: shared-module/floppyio/__init__.c -msgid "timeout waiting for index pulse" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "timeout waiting for v1 card" -msgstr "" - -#: shared-module/sdcardio/SDCard.c -msgid "timeout waiting for v2 card" -msgstr "" - -#: ports/stm/common-hal/pwmio/PWMOut.c -msgid "timer re-init" -msgstr "" - -#: shared-bindings/time/__init__.c -msgid "timestamp out of range for platform time_t" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "tobytes can be invoked for dense arrays only" -msgstr "" - -#: py/compile.c -msgid "too many args" -msgstr "" - -#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/create.c -msgid "too many dimensions" -msgstr "" - -#: extmod/ulab/code/ndarray.c -msgid "too many indices" -msgstr "" - -#: py/asmthumb.c -msgid "too many locals for native method" -msgstr "" - -#: py/runtime.c -#, c-format -msgid "too many values to unpack (expected %d)" -msgstr "" - -#: extmod/ulab/code/numpy/approx.c -msgid "trapz is defined for 1D arrays of equal length" -msgstr "" - -#: extmod/ulab/code/numpy/approx.c -msgid "trapz is defined for 1D iterables" -msgstr "" - -#: py/obj.c -msgid "tuple/list has wrong length" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -#, c-format -msgid "twai_driver_install returned esp-idf error #%d" -msgstr "" - -#: ports/espressif/common-hal/canio/CAN.c -#, c-format -msgid "twai_start returned esp-idf error #%d" -msgstr "" - -#: shared-bindings/busio/UART.c shared-bindings/canio/CAN.c -msgid "tx and rx cannot both be None" -msgstr "" - -#: py/objtype.c -msgid "type '%q' isn't an acceptable base type" -msgstr "" - -#: py/objtype.c -msgid "type isn't an acceptable base type" -msgstr "" - -#: py/runtime.c -msgid "type object '%q' has no attribute '%q'" -msgstr "" - -#: py/objtype.c -msgid "type takes 1 or 3 arguments" -msgstr "" - -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - -#: py/parse.c -msgid "unexpected indent" -msgstr "" - -#: py/bc.c -msgid "unexpected keyword argument" -msgstr "" - -#: py/argcheck.c py/bc.c py/objnamedtuple.c -#: shared-bindings/traceback/__init__.c -msgid "unexpected keyword argument '%q'" -msgstr "" - -#: py/lexer.c -msgid "unicode name escapes" -msgstr "" - -#: py/parse.c -msgid "unindent doesn't match any outer indent level" -msgstr "" - -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - -#: py/objstr.c -#, c-format -msgid "unknown conversion specifier %c" -msgstr "" - -#: py/objstr.c -msgid "unknown format code '%c' for object of type '%q'" -msgstr "" - -#: py/compile.c -msgid "unknown type" -msgstr "" - -#: py/compile.c -msgid "unknown type '%q'" -msgstr "" - -#: py/objstr.c -#, c-format -msgid "unmatched '%c' in format" -msgstr "" - -#: py/objtype.c py/runtime.c -msgid "unreadable attribute" -msgstr "" - -#: shared-bindings/displayio/TileGrid.c shared-bindings/terminalio/Terminal.c -#: shared-bindings/tilepalettemapper/TilePaletteMapper.c -#: shared-bindings/vectorio/VectorShape.c -msgid "unsupported %q type" -msgstr "" - -#: py/emitinlinethumb.c -#, c-format -msgid "unsupported Thumb instruction '%s' with %d arguments" -msgstr "" - -#: py/emitinlinextensa.c -#, c-format -msgid "unsupported Xtensa instruction '%s' with %d arguments" -msgstr "" - -#: shared-module/bitmapfilter/__init__.c -msgid "unsupported bitmap depth" -msgstr "" - -#: shared-module/gifio/GifWriter.c -msgid "unsupported colorspace for GifWriter" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "unsupported colorspace for dither" -msgstr "" - -#: py/objstr.c -#, c-format -msgid "unsupported format character '%c' (0x%x) at index %d" -msgstr "" - -#: py/runtime.c -msgid "unsupported type for %q: '%s'" -msgstr "" - -#: py/runtime.c -msgid "unsupported type for operator" -msgstr "" - -#: py/runtime.c -msgid "unsupported types for %q: '%q', '%q'" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "usecols is too high" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "usecols keyword must be specified" -msgstr "" - -#: py/objint.c -#, c-format -msgid "value must fit in %d byte(s)" -msgstr "" - -#: shared-bindings/bitmaptools/__init__.c -msgid "value out of range of target" -msgstr "" - -#: extmod/moddeflate.c -msgid "wbits" -msgstr "" - -#: shared-bindings/bitmapfilter/__init__.c -msgid "" -"weights must be a sequence with an odd square number of elements (usually 9 " -"or 25)" -msgstr "" - -#: shared-bindings/bitmapfilter/__init__.c -msgid "weights must be an object of type %q, %q, %q, or %q, not %q " -msgstr "" - -#: shared-bindings/is31fl3741/FrameBuffer.c -msgid "width must be greater than zero" -msgstr "" - -#: ports/raspberrypi/common-hal/wifi/Monitor.c -msgid "wifi.Monitor not available" -msgstr "" - -#: shared-bindings/_bleio/Adapter.c -msgid "window must be <= interval" -msgstr "" - -#: extmod/ulab/code/numpy/numerical.c -msgid "wrong axis index" -msgstr "" - -#: extmod/ulab/code/numpy/create.c -msgid "wrong axis specified" -msgstr "" - -#: extmod/ulab/code/numpy/io/io.c -msgid "wrong dtype" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c -msgid "wrong index type" -msgstr "" - -#: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/create.c -#: extmod/ulab/code/numpy/io/io.c extmod/ulab/code/numpy/transform.c -#: extmod/ulab/code/numpy/vector.c -msgid "wrong input type" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c -msgid "wrong length of condition array" -msgstr "" - -#: extmod/ulab/code/numpy/transform.c -msgid "wrong length of index array" -msgstr "" - -#: extmod/ulab/code/numpy/create.c py/objarray.c py/objstr.c -msgid "wrong number of arguments" -msgstr "" - -#: py/runtime.c -msgid "wrong number of values to unpack" -msgstr "" - -#: extmod/ulab/code/numpy/vector.c -msgid "wrong output type" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "zi must be an ndarray" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "zi must be of float type" -msgstr "" - -#: extmod/ulab/code/scipy/signal/signal.c -msgid "zi must be of shape (n_section, 2)" -msgstr "" diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index e6a868ab21226..9065ffebcf831 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -5,5 +5,24 @@ // SPDX-License-Identifier: MIT #include "supervisor/board.h" +#include "shared-bindings/mcp4822/MCP4822.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "peripherals/pins.h" +#include "py/runtime.h" + +// board.DAC() — factory function that constructs an mcp4822.MCP4822 with +// the MTM Workshop Computer's DAC pins (GP18=SCK, GP19=SDI, GP21=CS). +static mp_obj_t board_dac_factory(void) { + mcp4822_mcp4822_obj_t *dac = mp_obj_malloc_with_finaliser( + mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); + common_hal_mcp4822_mcp4822_construct( + dac, + &pin_GPIO18, // clock (SCK) + &pin_GPIO19, // mosi (SDI) + &pin_GPIO21, // cs + 1); // gain 1x + return MP_OBJ_FROM_PTR(dac); +} +MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h deleted file mode 100644 index f9b5dd60108cf..0000000000000 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "common-hal/microcontroller/Pin.h" -#include "common-hal/rp2pio/StateMachine.h" - -#include "audio_dma.h" -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; - rp2pio_statemachine_obj_t state_machine; - audio_dma_t dma; - bool playing; -} mtm_hardware_dacout_obj_t; - -void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, - const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs, uint8_t gain); - -void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); - -void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, - mp_obj_t sample, bool loop); -void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self); - -void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self); -void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self); - -extern const mp_obj_type_t mtm_hardware_dacout_type; diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c deleted file mode 100644 index 8f875496f6745..0000000000000 --- a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c +++ /dev/null @@ -1,276 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT -// -// Python bindings for the mtm_hardware module. -// Provides DACOut: a non-blocking audio player for the MCP4822 SPI DAC. - -#include - -#include "shared/runtime/context_manager_helpers.h" -#include "py/binary.h" -#include "py/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/util.h" -#include "boards/mtm_computer/module/DACOut.h" - -// ───────────────────────────────────────────────────────────────────────────── -// DACOut class -// ───────────────────────────────────────────────────────────────────────────── - -//| class DACOut: -//| """Output audio to the MCP4822 dual-channel 12-bit SPI DAC.""" -//| -//| def __init__( -//| self, -//| clock: microcontroller.Pin, -//| mosi: microcontroller.Pin, -//| cs: microcontroller.Pin, -//| *, -//| gain: int = 1, -//| ) -> None: -//| """Create a DACOut object associated with the given SPI pins. -//| -//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin -//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin -//| :param ~microcontroller.Pin cs: The chip select (CS) pin -//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. -//| -//| Simple 8ksps 440 Hz sine wave:: -//| -//| import mtm_hardware -//| import audiocore -//| import board -//| import array -//| import time -//| import math -//| -//| length = 8000 // 440 -//| sine_wave = array.array("H", [0] * length) -//| for i in range(length): -//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) -//| -//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) -//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) -//| dac.play(sine_wave, loop=True) -//| time.sleep(1) -//| dac.stop() -//| -//| Playing a wave file from flash:: -//| -//| import board -//| import audiocore -//| import mtm_hardware -//| -//| f = open("sound.wav", "rb") -//| wav = audiocore.WaveFile(f) -//| -//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) -//| dac.play(wav) -//| while dac.playing: -//| pass""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, - }; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); - const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); - const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); - - mp_int_t gain = args[ARG_gain].u_int; - if (gain != 1 && gain != 2) { - mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("gain must be 1 or 2")); - } - - mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); - common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs, (uint8_t)gain); - - return MP_OBJ_FROM_PTR(self); -} - -static void check_for_deinit(mtm_hardware_dacout_obj_t *self) { - if (common_hal_mtm_hardware_dacout_deinited(self)) { - raise_deinited_error(); - } -} - -//| def deinit(self) -> None: -//| """Deinitialises the DACOut and releases any hardware resources for reuse.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_deinit(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_mtm_hardware_dacout_deinit(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_deinit_obj, mtm_hardware_dacout_deinit); - -//| def __enter__(self) -> DACOut: -//| """No-op used by Context Managers.""" -//| ... -//| -// Provided by context manager helper. - -//| def __exit__(self) -> None: -//| """Automatically deinitializes the hardware when exiting a context. See -//| :ref:`lifetime-and-contextmanagers` for more info.""" -//| ... -//| -// Provided by context manager helper. - -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: -//| """Plays the sample once when loop=False and continuously when loop=True. -//| Does not block. Use `playing` to block. -//| -//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. -//| -//| The sample itself should consist of 8 bit or 16 bit samples.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sample, ARG_loop }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, - }; - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - check_for_deinit(self); - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_obj_t sample = args[ARG_sample].u_obj; - common_hal_mtm_hardware_dacout_play(self, sample, args[ARG_loop].u_bool); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(mtm_hardware_dacout_play_obj, 1, mtm_hardware_dacout_obj_play); - -//| def stop(self) -> None: -//| """Stops playback.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_stop(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - common_hal_mtm_hardware_dacout_stop(self); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_stop_obj, mtm_hardware_dacout_obj_stop); - -//| playing: bool -//| """True when the audio sample is being output. (read-only)""" -//| -static mp_obj_t mtm_hardware_dacout_obj_get_playing(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_playing(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_playing_obj, mtm_hardware_dacout_obj_get_playing); - -MP_PROPERTY_GETTER(mtm_hardware_dacout_playing_obj, - (mp_obj_t)&mtm_hardware_dacout_get_playing_obj); - -//| def pause(self) -> None: -//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_pause(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - if (!common_hal_mtm_hardware_dacout_get_playing(self)) { - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Not playing")); - } - common_hal_mtm_hardware_dacout_pause(self); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_pause_obj, mtm_hardware_dacout_obj_pause); - -//| def resume(self) -> None: -//| """Resumes sample playback after :py:func:`pause`.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_resume(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - if (common_hal_mtm_hardware_dacout_get_paused(self)) { - common_hal_mtm_hardware_dacout_resume(self); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_resume_obj, mtm_hardware_dacout_obj_resume); - -//| paused: bool -//| """True when playback is paused. (read-only)""" -//| -static mp_obj_t mtm_hardware_dacout_obj_get_paused(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_paused(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_paused_obj, mtm_hardware_dacout_obj_get_paused); - -MP_PROPERTY_GETTER(mtm_hardware_dacout_paused_obj, - (mp_obj_t)&mtm_hardware_dacout_get_paused_obj); - -// ── DACOut type definition ─────────────────────────────────────────────────── - -static const mp_rom_map_elem_t mtm_hardware_dacout_locals_dict_table[] = { - // Methods - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, - { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mtm_hardware_dacout_play_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mtm_hardware_dacout_stop_obj) }, - { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mtm_hardware_dacout_pause_obj) }, - { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mtm_hardware_dacout_resume_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mtm_hardware_dacout_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mtm_hardware_dacout_paused_obj) }, -}; -static MP_DEFINE_CONST_DICT(mtm_hardware_dacout_locals_dict, mtm_hardware_dacout_locals_dict_table); - -MP_DEFINE_CONST_OBJ_TYPE( - mtm_hardware_dacout_type, - MP_QSTR_DACOut, - MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, - make_new, mtm_hardware_dacout_make_new, - locals_dict, &mtm_hardware_dacout_locals_dict - ); - -// ───────────────────────────────────────────────────────────────────────────── -// mtm_hardware module definition -// ───────────────────────────────────────────────────────────────────────────── - -//| """Hardware interface to Music Thing Modular Workshop Computer peripherals. -//| -//| Provides the `DACOut` class for non-blocking audio output via the -//| MCP4822 dual-channel 12-bit SPI DAC. -//| """ - -static const mp_rom_map_elem_t mtm_hardware_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mtm_hardware) }, - { MP_ROM_QSTR(MP_QSTR_DACOut), MP_ROM_PTR(&mtm_hardware_dacout_type) }, -}; - -static MP_DEFINE_CONST_DICT(mtm_hardware_module_globals, mtm_hardware_module_globals_table); - -const mp_obj_module_t mtm_hardware_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&mtm_hardware_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_mtm_hardware, mtm_hardware_module); diff --git a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk index 74d9baac879b4..45c99711d72a3 100644 --- a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk +++ b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk @@ -11,7 +11,4 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY_AUDIOEFFECTS = 1 CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_PICODVI = 0 - -SRC_C += \ - boards/$(BOARD)/module/mtm_hardware.c \ - boards/$(BOARD)/module/DACOut.c +CIRCUITPY_MCP4822 = 1 diff --git a/ports/raspberrypi/boards/mtm_computer/pins.c b/ports/raspberrypi/boards/mtm_computer/pins.c index 6987818233102..ffdf2687702c6 100644 --- a/ports/raspberrypi/boards/mtm_computer/pins.c +++ b/ports/raspberrypi/boards/mtm_computer/pins.c @@ -6,6 +6,8 @@ #include "shared-bindings/board/__init__.h" +extern const mp_obj_fun_builtin_fixed_t board_dac_obj; + static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS @@ -21,7 +23,6 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_PULSE_2_IN), MP_ROM_PTR(&pin_GPIO3) }, { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_NORM_PROBE), MP_ROM_PTR(&pin_GPIO4) }, { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, @@ -105,6 +106,9 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, + // Factory function: dac = board.DAC() returns a configured mcp4822.MCP4822 + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&board_dac_obj) }, + // { MP_ROM_QSTR(MP_QSTR_EEPROM_I2C), MP_ROM_PTR(&board_i2c_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c similarity index 75% rename from ports/raspberrypi/boards/mtm_computer/module/DACOut.c rename to ports/raspberrypi/common-hal/mcp4822/MCP4822.c index fb4ce37d4d0ff..cb6cddb8343ed 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c +++ b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT // -// MCP4822 dual-channel 12-bit SPI DAC driver for the MTM Workshop Computer. +// MCP4822 dual-channel 12-bit SPI DAC audio output. // Uses PIO + DMA for non-blocking audio playback, mirroring audiobusio.I2SOut. #include @@ -15,7 +15,8 @@ #include "py/gc.h" #include "py/mperrno.h" #include "py/runtime.h" -#include "boards/mtm_computer/module/DACOut.h" +#include "common-hal/mcp4822/MCP4822.h" +#include "shared-bindings/mcp4822/MCP4822.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/audiocore/__init__.h" #include "bindings/rp2pio/StateMachine.h" @@ -29,16 +30,14 @@ // SET pins (N) = MOSI through CS — for CS control & command-bit injection // SIDE-SET pin (1) = SCK — serial clock // -// On the MTM Workshop Computer: MOSI=GP19, CS=GP21, SCK=GP18. -// The SET group spans GP19..GP21 (3 pins). GP20 is unused and driven low. -// -// SET PINS bit mapping (bit0=MOSI/GP19, bit1=GP20, bit2=CS/GP21): -// 0 = CS low, MOSI low 1 = CS low, MOSI high 4 = CS high, MOSI low +// SET PINS bit mapping (bit0=MOSI, ..., bit N=CS): +// 0 = CS low, MOSI low 1 = CS low, MOSI high +// (1 << cs_bit_pos) = CS high, MOSI low // // SIDE-SET (1 pin, SCK): side 0 = SCK low, side 1 = SCK high // // MCP4822 16-bit command word: -// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x) +// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x, 0=2x) // [12] output enable (1) [11:0] 12-bit data // // DMA feeds unsigned 16-bit audio samples. RP2040 narrow-write replication @@ -46,8 +45,8 @@ // giving mono→stereo for free. // // The PIO pulls 32 bits, then sends two SPI transactions: -// Channel A: cmd nibble 0b0011, then all 16 sample bits from upper half-word -// Channel B: cmd nibble 0b1011, then all 16 sample bits from lower half-word +// Channel A: cmd nibble, then all 16 sample bits from upper half-word +// Channel B: cmd nibble, then all 16 sample bits from lower half-word // The MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data), // so only the top 12 of the 16 sample bits become DAC data. The bottom // 4 sample bits clock out harmlessly after the DAC has latched. @@ -66,11 +65,7 @@ static const uint16_t mcp4822_pio_program[] = { // 1: mov x, osr side 0 ; Save for pull-noblock fallback 0xA027, - // ── Channel A: command nibble 0b0011 ────────────────────────────────── - // Send 4 cmd bits via SET, then all 16 sample bits via OUT. - // MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data); - // the extra 4 clocks shift out the LSBs which the DAC ignores. - // This gives correct 16→12 bit scaling (top 12 bits become DAC data). + // ── Channel A: command nibble 0b0011 (1x gain) ──────────────────────── // 2: set pins, 0 side 0 ; CS low, MOSI=0 (bit15=0: channel A) 0xE000, // 3: nop side 1 ; SCK high — latch bit 15 @@ -79,7 +74,7 @@ static const uint16_t mcp4822_pio_program[] = { 0xE000, // 5: nop side 1 ; SCK high 0xB042, - // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) [patched for 2x] 0xE001, // 7: nop side 1 ; SCK high 0xB042, @@ -96,7 +91,7 @@ static const uint16_t mcp4822_pio_program[] = { // 13: set pins, 4 side 0 ; CS high — DAC A latches 0xE004, - // ── Channel B: command nibble 0b1011 ────────────────────────────────── + // ── Channel B: command nibble 0b1011 (1x gain) ──────────────────────── // 14: set pins, 1 side 0 ; CS low, MOSI=1 (bit15=1: channel B) 0xE001, // 15: nop side 1 ; SCK high @@ -105,7 +100,7 @@ static const uint16_t mcp4822_pio_program[] = { 0xE000, // 17: nop side 1 ; SCK high 0xB042, - // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) [patched for 2x] 0xE001, // 19: nop side 1 ; SCK high 0xB042, @@ -127,7 +122,6 @@ static const uint16_t mcp4822_pio_program[] = { // Per channel: 8(4 cmd bits × 2 clks) + 1(set y) + 32(16 bits × 2 clks) + 1(cs high) = 42 #define MCP4822_CLOCKS_PER_SAMPLE 86 - // MCP4822 gain bit (bit 13) position in the PIO program: // Instruction 6 = channel A gain bit // Instruction 18 = channel B gain bit @@ -138,15 +132,19 @@ static const uint16_t mcp4822_pio_program[] = { #define MCP4822_PIO_GAIN_1X 0xE001 // set pins, 1 #define MCP4822_PIO_GAIN_2X 0xE000 // set pins, 0 -void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, +void mcp4822_reset(void) { +} + +// Caller validates that pins are free. +void common_hal_mcp4822_mcp4822_construct(mcp4822_mcp4822_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *cs, uint8_t gain) { - // SET pins span from MOSI to CS. MOSI must have a lower GPIO number - // than CS, with at most 4 pins between them (SET count max is 5). + // The SET pin group spans from MOSI to CS. + // MOSI must have a lower GPIO number than CS, gap at most 4. if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { mp_raise_ValueError( - MP_COMPRESSED_ROM_TEXT("cs pin must be 1-4 positions above mosi pin")); + MP_ERROR_TEXT("cs pin must be 1-4 positions above mosi pin")); } uint8_t set_count = cs->number - mosi->number + 1; @@ -197,26 +195,26 @@ void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, audio_dma_init(&self->dma); } -bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self) { +bool common_hal_mcp4822_mcp4822_deinited(mcp4822_mcp4822_obj_t *self) { return common_hal_rp2pio_statemachine_deinited(&self->state_machine); } -void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self) { - if (common_hal_mtm_hardware_dacout_deinited(self)) { +void common_hal_mcp4822_mcp4822_deinit(mcp4822_mcp4822_obj_t *self) { + if (common_hal_mcp4822_mcp4822_deinited(self)) { return; } - if (common_hal_mtm_hardware_dacout_get_playing(self)) { - common_hal_mtm_hardware_dacout_stop(self); + if (common_hal_mcp4822_mcp4822_get_playing(self)) { + common_hal_mcp4822_mcp4822_stop(self); } common_hal_rp2pio_statemachine_deinit(&self->state_machine); audio_dma_deinit(&self->dma); } -void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, +void common_hal_mcp4822_mcp4822_play(mcp4822_mcp4822_obj_t *self, mp_obj_t sample, bool loop) { - if (common_hal_mtm_hardware_dacout_get_playing(self)) { - common_hal_mtm_hardware_dacout_stop(self); + if (common_hal_mcp4822_mcp4822_get_playing(self)) { + common_hal_mcp4822_mcp4822_stop(self); } uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); @@ -227,7 +225,7 @@ void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, uint32_t sample_rate = audiosample_get_sample_rate(sample); uint8_t channel_count = audiosample_get_channel_count(sample); if (channel_count > 2) { - mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("Too many channels in sample.")); + mp_raise_ValueError(MP_ERROR_TEXT("Too many channels in sample.")); } // PIO clock = sample_rate × clocks_per_sample @@ -236,10 +234,6 @@ void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, (uint32_t)sample_rate * MCP4822_CLOCKS_PER_SAMPLE); common_hal_rp2pio_statemachine_restart(&self->state_machine); - // DMA feeds unsigned 16-bit samples. The PIO discards the top 4 bits - // of each 16-bit half and uses the remaining 12 as DAC data. - // RP2040 narrow-write replication: 16-bit DMA write → same value in - // both 32-bit FIFO halves → mono-to-stereo for free. audio_dma_result result = audio_dma_setup_playback( &self->dma, sample, @@ -253,41 +247,41 @@ void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, false); // swap_channel if (result == AUDIO_DMA_DMA_BUSY) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("No DMA channel found")); + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("No DMA channel found")); } else if (result == AUDIO_DMA_MEMORY_ERROR) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Unable to allocate buffers for signed conversion")); + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("Unable to allocate buffers for signed conversion")); } else if (result == AUDIO_DMA_SOURCE_ERROR) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Audio source error")); + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("Audio source error")); } self->playing = true; } -void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self) { +void common_hal_mcp4822_mcp4822_pause(mcp4822_mcp4822_obj_t *self) { audio_dma_pause(&self->dma); } -void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self) { +void common_hal_mcp4822_mcp4822_resume(mcp4822_mcp4822_obj_t *self) { audio_dma_resume(&self->dma); } -bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self) { +bool common_hal_mcp4822_mcp4822_get_paused(mcp4822_mcp4822_obj_t *self) { return audio_dma_get_paused(&self->dma); } -void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self) { +void common_hal_mcp4822_mcp4822_stop(mcp4822_mcp4822_obj_t *self) { audio_dma_stop(&self->dma); common_hal_rp2pio_statemachine_stop(&self->state_machine); self->playing = false; } -bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self) { +bool common_hal_mcp4822_mcp4822_get_playing(mcp4822_mcp4822_obj_t *self) { bool playing = audio_dma_get_playing(&self->dma); if (!playing && self->playing) { - common_hal_mtm_hardware_dacout_stop(self); + common_hal_mcp4822_mcp4822_stop(self); } return playing; } diff --git a/ports/raspberrypi/common-hal/mcp4822/MCP4822.h b/ports/raspberrypi/common-hal/mcp4822/MCP4822.h new file mode 100644 index 0000000000000..53c8e862b63c5 --- /dev/null +++ b/ports/raspberrypi/common-hal/mcp4822/MCP4822.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/rp2pio/StateMachine.h" + +#include "audio_dma.h" +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + rp2pio_statemachine_obj_t state_machine; + audio_dma_t dma; + bool playing; +} mcp4822_mcp4822_obj_t; + +void mcp4822_reset(void); diff --git a/ports/raspberrypi/common-hal/mcp4822/__init__.c b/ports/raspberrypi/common-hal/mcp4822/__init__.c new file mode 100644 index 0000000000000..48981fc88a713 --- /dev/null +++ b/ports/raspberrypi/common-hal/mcp4822/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +// No module-level init needed. diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 8401c5d75453a..30fc75c106b54 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -16,6 +16,7 @@ CIRCUITPY_HASHLIB ?= 1 CIRCUITPY_HASHLIB_MBEDTLS ?= 1 CIRCUITPY_IMAGECAPTURE ?= 1 CIRCUITPY_MAX3421E ?= 0 +CIRCUITPY_MCP4822 ?= 0 CIRCUITPY_MEMORYMAP ?= 1 CIRCUITPY_PWMIO ?= 1 CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_DISPLAYIO) diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 886ba96f3e5fa..14c8c52802e6e 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -288,6 +288,9 @@ endif ifeq ($(CIRCUITPY_MAX3421E),1) SRC_PATTERNS += max3421e/% endif +ifeq ($(CIRCUITPY_MCP4822),1) +SRC_PATTERNS += mcp4822/% +endif ifeq ($(CIRCUITPY_MDNS),1) SRC_PATTERNS += mdns/% endif @@ -532,6 +535,8 @@ SRC_COMMON_HAL_ALL = \ i2ctarget/I2CTarget.c \ i2ctarget/__init__.c \ max3421e/Max3421E.c \ + mcp4822/__init__.c \ + mcp4822/MCP4822.c \ memorymap/__init__.c \ memorymap/AddressRange.c \ microcontroller/__init__.c \ diff --git a/shared-bindings/mcp4822/MCP4822.c b/shared-bindings/mcp4822/MCP4822.c new file mode 100644 index 0000000000000..c48f5426e6690 --- /dev/null +++ b/shared-bindings/mcp4822/MCP4822.c @@ -0,0 +1,247 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/mcp4822/MCP4822.h" +#include "shared-bindings/util.h" + +//| class MCP4822: +//| """Output audio to an MCP4822 dual-channel 12-bit SPI DAC.""" +//| +//| def __init__( +//| self, +//| clock: microcontroller.Pin, +//| mosi: microcontroller.Pin, +//| cs: microcontroller.Pin, +//| *, +//| gain: int = 1, +//| ) -> None: +//| """Create an MCP4822 object associated with the given SPI pins. +//| +//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin +//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin +//| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. +//| +//| Simple 8ksps 440 Hz sine wave:: +//| +//| import mcp4822 +//| import audiocore +//| import board +//| import array +//| import time +//| import math +//| +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac = mcp4822.MCP4822(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import mcp4822 +//| +//| f = open("sound.wav", "rb") +//| wav = audiocore.WaveFile(f) +//| +//| dac = mcp4822.MCP4822(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(wav) +//| while dac.playing: +//| pass""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + + mp_int_t gain = args[ARG_gain].u_int; + if (gain != 1 && gain != 2) { + mp_raise_ValueError(MP_ERROR_TEXT("gain must be 1 or 2")); + } + + mcp4822_mcp4822_obj_t *self = mp_obj_malloc_with_finaliser(mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); + common_hal_mcp4822_mcp4822_construct(self, clock, mosi, cs, (uint8_t)gain); + + return MP_OBJ_FROM_PTR(self); +} + +static void check_for_deinit(mcp4822_mcp4822_obj_t *self) { + if (common_hal_mcp4822_mcp4822_deinited(self)) { + raise_deinited_error(); + } +} + +//| def deinit(self) -> None: +//| """Deinitialises the MCP4822 and releases any hardware resources for reuse.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_deinit(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_mcp4822_mcp4822_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_deinit_obj, mcp4822_mcp4822_deinit); + +//| def __enter__(self) -> MCP4822: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +// Provided by context manager helper. + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 8 bit or 16 bit samples.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_mcp4822_mcp4822_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mcp4822_mcp4822_play_obj, 1, mcp4822_mcp4822_obj_play); + +//| def stop(self) -> None: +//| """Stops playback.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_stop(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_mcp4822_mcp4822_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_stop_obj, mcp4822_mcp4822_obj_stop); + +//| playing: bool +//| """True when the audio sample is being output. (read-only)""" +//| +static mp_obj_t mcp4822_mcp4822_obj_get_playing(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mcp4822_mcp4822_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_get_playing_obj, mcp4822_mcp4822_obj_get_playing); + +MP_PROPERTY_GETTER(mcp4822_mcp4822_playing_obj, + (mp_obj_t)&mcp4822_mcp4822_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_pause(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (!common_hal_mcp4822_mcp4822_get_playing(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Not playing")); + } + common_hal_mcp4822_mcp4822_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_pause_obj, mcp4822_mcp4822_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_resume(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (common_hal_mcp4822_mcp4822_get_paused(self)) { + common_hal_mcp4822_mcp4822_resume(self); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_resume_obj, mcp4822_mcp4822_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +//| +static mp_obj_t mcp4822_mcp4822_obj_get_paused(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mcp4822_mcp4822_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_get_paused_obj, mcp4822_mcp4822_obj_get_paused); + +MP_PROPERTY_GETTER(mcp4822_mcp4822_paused_obj, + (mp_obj_t)&mcp4822_mcp4822_get_paused_obj); + +static const mp_rom_map_elem_t mcp4822_mcp4822_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mcp4822_mcp4822_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mcp4822_mcp4822_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mcp4822_mcp4822_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mcp4822_mcp4822_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mcp4822_mcp4822_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mcp4822_mcp4822_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mcp4822_mcp4822_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mcp4822_mcp4822_paused_obj) }, +}; +static MP_DEFINE_CONST_DICT(mcp4822_mcp4822_locals_dict, mcp4822_mcp4822_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mcp4822_mcp4822_type, + MP_QSTR_MCP4822, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, mcp4822_mcp4822_make_new, + locals_dict, &mcp4822_mcp4822_locals_dict + ); diff --git a/shared-bindings/mcp4822/MCP4822.h b/shared-bindings/mcp4822/MCP4822.h new file mode 100644 index 0000000000000..b129aec306124 --- /dev/null +++ b/shared-bindings/mcp4822/MCP4822.h @@ -0,0 +1,25 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/mcp4822/MCP4822.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t mcp4822_mcp4822_type; + +void common_hal_mcp4822_mcp4822_construct(mcp4822_mcp4822_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs, uint8_t gain); + +void common_hal_mcp4822_mcp4822_deinit(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_deinited(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_play(mcp4822_mcp4822_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_mcp4822_mcp4822_stop(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_get_playing(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_pause(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_resume(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_get_paused(mcp4822_mcp4822_obj_t *self); diff --git a/shared-bindings/mcp4822/__init__.c b/shared-bindings/mcp4822/__init__.c new file mode 100644 index 0000000000000..bac2136d9e7c0 --- /dev/null +++ b/shared-bindings/mcp4822/__init__.c @@ -0,0 +1,36 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/mcp4822/__init__.h" +#include "shared-bindings/mcp4822/MCP4822.h" + +//| """Audio output via MCP4822 dual-channel 12-bit SPI DAC. +//| +//| The `mcp4822` module provides the `MCP4822` class for non-blocking +//| audio playback through the Microchip MCP4822 SPI DAC using PIO and DMA. +//| +//| All classes change hardware state and should be deinitialized when they +//| are no longer needed. To do so, either call :py:meth:`!deinit` or use a +//| context manager.""" + +static const mp_rom_map_elem_t mcp4822_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mcp4822) }, + { MP_ROM_QSTR(MP_QSTR_MCP4822), MP_ROM_PTR(&mcp4822_mcp4822_type) }, +}; + +static MP_DEFINE_CONST_DICT(mcp4822_module_globals, mcp4822_module_globals_table); + +const mp_obj_module_t mcp4822_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mcp4822_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mcp4822, mcp4822_module); diff --git a/shared-bindings/mcp4822/__init__.h b/shared-bindings/mcp4822/__init__.h new file mode 100644 index 0000000000000..c4a52e5819d12 --- /dev/null +++ b/shared-bindings/mcp4822/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once From 6651ac123a90771b8d89d0fa6f4adac793dfef67 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 16:51:02 -0700 Subject: [PATCH 439/517] restore deleted circuitpython.pot, update translation --- locale/circuitpython.pot | 4575 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 4575 insertions(+) create mode 100644 locale/circuitpython.pot diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot new file mode 100644 index 0000000000000..5d0be48fe3a79 --- /dev/null +++ b/locale/circuitpython.pot @@ -0,0 +1,4575 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: main.c +msgid "" +"\n" +"Code done running.\n" +msgstr "" + +#: main.c +msgid "" +"\n" +"Code stopped by auto-reload. Reloading soon.\n" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "" +"\n" +"Please file an issue with your program at github.com/adafruit/circuitpython/" +"issues." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "" +"\n" +"Press reset to exit safe mode.\n" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "" +"\n" +"You are in safe mode because:\n" +msgstr "" + +#: py/obj.c +msgid " File \"%q\"" +msgstr "" + +#: py/obj.c +msgid " File \"%q\", line %d" +msgstr "" + +#: py/builtinhelp.c +msgid " is of type %q\n" +msgstr "" + +#: main.c +msgid " not found.\n" +msgstr "" + +#: main.c +msgid " output:\n" +msgstr "" + +#: py/objstr.c +#, c-format +msgid "%%c needs int or char" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "" +"%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" +msgstr "" + +#: shared-bindings/microcontroller/Pin.c +msgid "%q and %q contain duplicate pins" +msgstr "" + +#: shared-bindings/audioio/AudioOut.c +msgid "%q and %q must be different" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +msgid "%q and %q must share a clock unit" +msgstr "" + +#: ports/nordic/common-hal/watchdog/WatchDogTimer.c +msgid "%q cannot be changed once mode is set to %q" +msgstr "" + +#: shared-bindings/microcontroller/Pin.c +msgid "%q contains duplicate pins" +msgstr "" + +#: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "%q failure: %d" +msgstr "" + +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + +#: py/argcheck.c shared-module/audiofilters/Filter.c +msgid "%q in %q must be of type %q, not %q" +msgstr "" + +#: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c +#: ports/mimxrt10xx/common-hal/usb_host/Port.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +#: ports/raspberrypi/common-hal/usb_host/Port.c +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c +msgid "%q in use" +msgstr "" + +#: py/objstr.c +msgid "%q index out of range" +msgstr "" + +#: py/obj.c +msgid "%q indices must be integers, not %s" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c +msgid "%q init failed" +msgstr "" + +#: ports/espressif/bindings/espnow/Peer.c shared-bindings/dualbank/__init__.c +msgid "%q is %q" +msgstr "" + +#: ports/raspberrypi/common-hal/wifi/Radio.c +msgid "%q is read-only for this board" +msgstr "" + +#: py/argcheck.c shared-bindings/usb_hid/Device.c +msgid "%q length must be %d" +msgstr "" + +#: py/argcheck.c +msgid "%q length must be %d-%d" +msgstr "" + +#: py/argcheck.c +msgid "%q length must be <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q length must be >= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d" +msgstr "" + +#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/displayio/Bitmap.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/is31fl3741/FrameBuffer.c +#: shared-bindings/rgbmatrix/RGBMatrix.c +msgid "%q must be %d-%d" +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +msgid "%q must be 1 when %q is True" +msgstr "" + +#: py/argcheck.c shared-bindings/gifio/GifWriter.c +#: shared-module/gifio/OnDiskGif.c +msgid "%q must be <= %d" +msgstr "" + +#: ports/espressif/common-hal/watchdog/WatchDogTimer.c +msgid "%q must be <= %u" +msgstr "" + +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: shared-bindings/analogbufio/BufferedIn.c +msgid "%q must be a bytearray or array of type 'H' or 'B'" +msgstr "" + +#: shared-bindings/audiocore/RawSample.c +msgid "%q must be a bytearray or array of type 'h', 'H', 'b', or 'B'" +msgstr "" + +#: shared-bindings/warnings/__init__.c +msgid "%q must be a subclass of %q" +msgstr "" + +#: ports/espressif/common-hal/analogbufio/BufferedIn.c +msgid "%q must be array of type 'H'" +msgstr "" + +#: shared-module/synthio/__init__.c +msgid "%q must be array of type 'h'" +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "%q must be multiple of 8." +msgstr "" + +#: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c +#: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c +#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c +#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c +#: shared-module/synthio/Synthesizer.c +msgid "%q must be of type %q or %q, not %q" +msgstr "" + +#: shared-bindings/jpegio/JpegDecoder.c +msgid "%q must be of type %q, %q, or %q, not %q" +msgstr "" + +#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "" + +#: ports/atmel-samd/common-hal/busio/UART.c +msgid "%q must be power of 2" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + +#: shared-bindings/wifi/Monitor.c +msgid "%q out of bounds" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +#: ports/cxd56/common-hal/pulseio/PulseIn.c +#: ports/nordic/common-hal/pulseio/PulseIn.c +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +#: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c +msgid "%q out of range" +msgstr "" + +#: py/objmodule.c +msgid "%q renamed %q" +msgstr "" + +#: py/objrange.c py/objslice.c shared-bindings/random/__init__.c +msgid "%q step cannot be zero" +msgstr "" + +#: shared-module/bitbangio/I2C.c +msgid "%q too long" +msgstr "" + +#: py/bc.c py/objnamedtuple.c +msgid "%q() takes %d positional arguments but %d were given" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "%q() without %q()" +msgstr "" + +#: shared-bindings/usb_hid/Device.c +msgid "%q, %q, and %q must all be the same length" +msgstr "" + +#: py/objint.c shared-bindings/_bleio/Connection.c +#: shared-bindings/storage/__init__.c +msgid "%q=%q" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "%q[%u] shifts in more bits than pin count" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "%q[%u] shifts out more bits than pin count" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "%q[%u] uses extra pin" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "%q[%u] waits on input outside of count" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +#, c-format +msgid "%s error 0x%x" +msgstr "" + +#: py/argcheck.c +msgid "'%q' argument required" +msgstr "" + +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "'%q' object does not support '%q'" +msgstr "" + +#: py/runtime.c +msgid "'%q' object isn't an iterator" +msgstr "" + +#: py/objtype.c py/runtime.c shared-module/atexit/__init__.c +msgid "'%q' object isn't callable" +msgstr "" + +#: py/runtime.c +msgid "'%q' object isn't iterable" +msgstr "" + +#: py/emitinlinethumb.c py/emitinlinextensa.c +#, c-format +msgid "'%s' expects a label" +msgstr "" + +#: py/emitinlinethumb.c py/emitinlinextensa.c +#, c-format +msgid "'%s' expects a register" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects a special register" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects an FPU register" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects an address of the form [a, b]" +msgstr "" + +#: py/emitinlinethumb.c py/emitinlinextensa.c +#, c-format +msgid "'%s' expects an integer" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects at most r%d" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects {r0, r1, ...}" +msgstr "" + +#: py/emitinlinextensa.c +#, c-format +msgid "'%s' integer %d isn't within range %d..%d" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' integer 0x%x doesn't fit in mask 0x%x" +msgstr "" + +#: py/obj.c +#, c-format +msgid "'%s' object doesn't support item assignment" +msgstr "" + +#: py/obj.c +#, c-format +msgid "'%s' object doesn't support item deletion" +msgstr "" + +#: py/runtime.c +msgid "'%s' object has no attribute '%q'" +msgstr "" + +#: py/obj.c +#, c-format +msgid "'%s' object isn't subscriptable" +msgstr "" + +#: py/objstr.c +msgid "'=' alignment not allowed in string format specifier" +msgstr "" + +#: shared-module/struct/__init__.c +msgid "'S' and 'O' are not supported format types" +msgstr "" + +#: py/compile.c +msgid "'align' requires 1 argument" +msgstr "" + +#: py/compile.c +msgid "'await' outside function" +msgstr "" + +#: py/compile.c +msgid "'break'/'continue' outside loop" +msgstr "" + +#: py/compile.c +msgid "'data' requires at least 2 arguments" +msgstr "" + +#: py/compile.c +msgid "'data' requires integer arguments" +msgstr "" + +#: py/compile.c +msgid "'label' requires 1 argument" +msgstr "" + +#: py/emitnative.c +msgid "'not' not implemented" +msgstr "" + +#: py/compile.c +msgid "'return' outside function" +msgstr "" + +#: py/compile.c +msgid "'yield from' inside async function" +msgstr "" + +#: py/compile.c +msgid "'yield' outside function" +msgstr "" + +#: py/compile.c +msgid "* arg after **" +msgstr "" + +#: py/compile.c +msgid "*x must be assignment target" +msgstr "" + +#: py/obj.c +msgid ", in %q\n" +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +msgid ".show(x) removed. Use .root_group = x" +msgstr "" + +#: py/objcomplex.c +msgid "0.0 to a complex power" +msgstr "" + +#: py/modbuiltins.c +msgid "3-arg pow() not supported" +msgstr "" + +#: ports/raspberrypi/common-hal/wifi/Radio.c +msgid "AP could not be started" +msgstr "" + +#: shared-bindings/ipaddress/IPv4Address.c +#, c-format +msgid "Address must be %d bytes long" +msgstr "" + +#: ports/espressif/common-hal/memorymap/AddressRange.c +#: ports/nordic/common-hal/memorymap/AddressRange.c +#: ports/raspberrypi/common-hal/memorymap/AddressRange.c +msgid "Address range not allowed" +msgstr "" + +#: shared-bindings/memorymap/AddressRange.c +msgid "Address range wraps around" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +msgid "All CAN peripherals are in use" +msgstr "" + +#: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/i2ctarget/I2CTarget.c +#: ports/nordic/common-hal/busio/I2C.c +msgid "All I2C peripherals are in use" +msgstr "" + +#: ports/atmel-samd/common-hal/canio/Listener.c +#: ports/espressif/common-hal/canio/Listener.c +#: ports/stm/common-hal/canio/Listener.c +msgid "All RX FIFOs in use" +msgstr "" + +#: ports/espressif/common-hal/busio/SPI.c ports/nordic/common-hal/busio/SPI.c +msgid "All SPI peripherals are in use" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c +msgid "All UART peripherals are in use" +msgstr "" + +#: ports/nordic/common-hal/countio/Counter.c +#: ports/nordic/common-hal/pulseio/PulseIn.c +#: ports/nordic/common-hal/rotaryio/IncrementalEncoder.c +msgid "All channels in use" +msgstr "" + +#: ports/raspberrypi/common-hal/usb_host/Port.c +msgid "All dma channels in use" +msgstr "" + +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +msgid "All event channels in use" +msgstr "" + +#: ports/raspberrypi/common-hal/floppyio/__init__.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +#: ports/raspberrypi/common-hal/usb_host/Port.c +msgid "All state machines in use" +msgstr "" + +#: ports/atmel-samd/audio_dma.c +msgid "All sync event channels in use" +msgstr "" + +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c +msgid "All timers for this pin are in use" +msgstr "" + +#: ports/atmel-samd/common-hal/_pew/PewPew.c +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +#: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/cxd56/common-hal/pulseio/PulseOut.c +#: ports/nordic/common-hal/audiopwmio/PWMAudioOut.c +#: ports/nordic/common-hal/pulseio/PulseIn.c +#: ports/nordic/peripherals/nrf/timers.c +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +msgid "All timers in use" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Already advertising." +msgstr "" + +#: ports/atmel-samd/common-hal/canio/Listener.c +msgid "Already have all-matches listener" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +msgid "Already in progress" +msgstr "" + +#: ports/espressif/bindings/espnow/ESPNow.c +#: ports/espressif/common-hal/espulp/ULP.c +#: shared-module/memorymonitor/AllocationAlarm.c +#: shared-module/memorymonitor/AllocationSize.c +msgid "Already running" +msgstr "" + +#: ports/espressif/common-hal/wifi/Radio.c +#: ports/raspberrypi/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "Already scanning for wifi networks" +msgstr "" + +#: supervisor/shared/settings.c +#, c-format +msgid "An error occurred while retrieving '%s':\n" +msgstr "" + +#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c +msgid "Another PWMAudioOut is already active" +msgstr "" + +#: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/cxd56/common-hal/pulseio/PulseOut.c +msgid "Another send is already active" +msgstr "" + +#: shared-bindings/pulseio/PulseOut.c +msgid "Array must contain halfwords (type 'H')" +msgstr "" + +#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c +#: shared-bindings/nvm/ByteArray.c +msgid "Array values should be single bytes." +msgstr "" + +#: ports/atmel-samd/common-hal/spitarget/SPITarget.c +msgid "Async SPI transfer in progress on this bus, keep awaiting." +msgstr "" + +#: shared-module/memorymonitor/AllocationAlarm.c +#, c-format +msgid "Attempt to allocate %d blocks" +msgstr "" + +#: ports/raspberrypi/audio_dma.c +msgid "Audio conversion not implemented" +msgstr "" + +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "Audio source error" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "AuthMode.OPEN is not used with password" +msgstr "" + +#: shared-bindings/wifi/Radio.c supervisor/shared/web_workflow/web_workflow.c +msgid "Authentication failure" +msgstr "" + +#: main.c +msgid "Auto-reload is off.\n" +msgstr "" + +#: main.c +msgid "" +"Auto-reload is on. Simply save files over USB to run them or enter REPL to " +"disable.\n" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +msgid "Baudrate not supported by peripheral" +msgstr "" + +#: shared-module/busdisplay/BusDisplay.c +#: shared-module/framebufferio/FramebufferDisplay.c +msgid "Below minimum frame rate" +msgstr "" + +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +msgid "Bit clock and word select must be sequential GPIO pins" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "Bitmap size and bits per value must match" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Boot device must be first (interface #0)." +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/UART.c +msgid "Both RX and TX required for flow control" +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +msgid "Brightness not adjustable" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "Buffer elements must be 4 bytes long or less" +msgstr "" + +#: shared-bindings/framebufferio/FramebufferDisplay.c +msgid "Buffer is not a bytearray." +msgstr "" + +#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + +#: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/cxd56/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/sdioio/SDCard.c +#: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c +#: shared-module/sdcardio/SDCard.c +#, c-format +msgid "Buffer must be a multiple of %d bytes" +msgstr "" + +#: shared-bindings/_bleio/PacketBuffer.c +#, c-format +msgid "Buffer too short by %d bytes" +msgstr "" + +#: ports/cxd56/common-hal/camera/Camera.c +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/struct/__init__.c shared-module/struct/__init__.c +msgid "Buffer too small" +msgstr "" + +#: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/raspberrypi/common-hal/paralleldisplaybus/ParallelBus.c +#, c-format +msgid "Bus pin %d is already in use" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "CBC blocks must be multiples of 16 bytes" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "CIRCUITPY drive could not be found or created." +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "CRC or checksum was invalid" +msgstr "" + +#: py/objtype.c +msgid "Call super().__init__() before accessing native object." +msgstr "" + +#: ports/cxd56/common-hal/camera/Camera.c +msgid "Camera init" +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Can only alarm on RTC IO from deep sleep." +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Can only alarm on one low pin while others alarm high from deep sleep." +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Can only alarm on two low pins from deep sleep." +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Can't construct AudioOut because continuous channel already open" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +msgid "Can't set CCCD on local Characteristic" +msgstr "" + +#: shared-bindings/storage/__init__.c shared-bindings/usb_cdc/__init__.c +#: shared-bindings/usb_hid/__init__.c shared-bindings/usb_midi/__init__.c +#: shared-bindings/usb_video/__init__.c +msgid "Cannot change USB devices now" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Cannot create a new Adapter; use _bleio.adapter;" +msgstr "" + +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + +#: shared-bindings/displayio/Bitmap.c +#: shared-bindings/memorymonitor/AllocationSize.c +#: shared-bindings/pulseio/PulseIn.c +msgid "Cannot delete values" +msgstr "" + +#: ports/atmel-samd/common-hal/digitalio/DigitalInOut.c +#: ports/mimxrt10xx/common-hal/digitalio/DigitalInOut.c +#: ports/nordic/common-hal/digitalio/DigitalInOut.c +#: ports/raspberrypi/common-hal/digitalio/DigitalInOut.c +msgid "Cannot get pull while in output mode" +msgstr "" + +#: ports/nordic/common-hal/microcontroller/Processor.c +msgid "Cannot get temperature" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Cannot have scan responses for extended, connectable advertisements." +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Cannot pull on input-only pin." +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "Cannot record to a file" +msgstr "" + +#: shared-module/storage/__init__.c +msgid "Cannot remount path when visible via USB." +msgstr "" + +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c +msgid "Cannot set value when direction is input." +msgstr "" + +#: ports/espressif/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/UART.c +msgid "Cannot specify RTS or CTS in RS485 mode" +msgstr "" + +#: py/objslice.c +msgid "Cannot subclass slice" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Cannot use GPIO0..15 together with GPIO32..47" +msgstr "" + +#: ports/nordic/common-hal/alarm/pin/PinAlarm.c +msgid "Cannot wake on pin edge, only level" +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Cannot wake on pin edge. Only level." +msgstr "" + +#: shared-bindings/_bleio/CharacteristicBuffer.c +msgid "CharacteristicBuffer writing not provided" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "CircuitPython core code crashed hard. Whoops!\n" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +msgid "Clock unit in use" +msgstr "" + +#: shared-bindings/_bleio/Connection.c +msgid "" +"Connection has been disconnected and can no longer be used. Create a new " +"connection." +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "Coordinate arrays have different lengths" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "Coordinate arrays types have different sizes" +msgstr "" + +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/Publisher.c +msgid "Could not publish to ROS topic" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Could not set address" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "Could not start interrupt, RX busy" +msgstr "" + +#: shared-module/audiomp3/MP3Decoder.c +msgid "Couldn't allocate decoder" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +#, c-format +msgid "Critical ROS failure during soft reboot, reset required: %d" +msgstr "" + +#: ports/stm/common-hal/analogio/AnalogOut.c +msgid "DAC Channel Init Error" +msgstr "" + +#: ports/stm/common-hal/analogio/AnalogOut.c +msgid "DAC Device Init Error" +msgstr "" + +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +msgid "DAC already in use" +msgstr "" + +#: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c +msgid "Data 0 pin must be byte aligned" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Data format error (may be broken data)" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Data too large for advertisement packet" +msgstr "" + +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Deep sleep pins must use a rising edge with pulldown" +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "Destination capacity is smaller than destination_length." +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Device error or wrong termination of input stream" +msgstr "" + +#: ports/nordic/common-hal/audiobusio/I2SOut.c +msgid "Device in use" +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +msgid "Display must have a 16 bit colorspace." +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c +msgid "Display rotation must be in 90 degree increments" +msgstr "" + +#: main.c +msgid "Done" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c +msgid "Drive mode not used when direction is input." +msgstr "" + +#: py/obj.c +msgid "During handling of the above exception, another exception occurred:" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "ECB only operates on 16 bytes at a time" +msgstr "" + +#: ports/espressif/common-hal/busio/SPI.c +#: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "ESP-IDF memory allocation failed" +msgstr "" + +#: extmod/modre.c +msgid "Error in regex" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Error in safemode.py." +msgstr "" + +#: shared-bindings/alarm/__init__.c +msgid "Expected a kind of %q" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Extended advertisements with scan response not supported." +msgstr "" + +#: extmod/ulab/code/numpy/fft/fft_tools.c +msgid "FFT is defined for ndarrays only" +msgstr "" + +#: extmod/ulab/code/numpy/fft/fft_tools.c +msgid "FFT is implemented for linear arrays only" +msgstr "" + +#: shared-bindings/ps2io/Ps2.c +msgid "Failed sending command." +msgstr "" + +#: ports/nordic/sd_mutex.c +#, c-format +msgid "Failed to acquire mutex, err 0x%04x" +msgstr "" + +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "Failed to add service TXT record" +msgstr "" + +#: shared-bindings/mdns/Server.c +msgid "" +"Failed to add service TXT record; non-string or bytes found in txt_records" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c +msgid "Failed to allocate %q buffer" +msgstr "" + +#: ports/espressif/common-hal/wifi/__init__.c +msgid "Failed to allocate Wifi memory" +msgstr "" + +#: ports/espressif/common-hal/wifi/ScannedNetworks.c +msgid "Failed to allocate wifi scan memory" +msgstr "" + +#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c +msgid "Failed to buffer the sample" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect: internal error" +msgstr "" + +#: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect: timeout" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to create continuous channels: invalid arg" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to create continuous channels: invalid state" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to create continuous channels: no mem" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to create continuous channels: not found" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to enable continuous" +msgstr "" + +#: shared-module/audiomp3/MP3Decoder.c +msgid "Failed to parse MP3 file" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to register continuous events callback" +msgstr "" + +#: ports/nordic/sd_mutex.c +#, c-format +msgid "Failed to release mutex, err 0x%04x" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "Failed to set hostname" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to start async audio" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Failed to write internal flash." +msgstr "" + +#: py/moderrno.c +msgid "File exists" +msgstr "" + +#: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c +msgid "File not found" +msgstr "" + +#: ports/atmel-samd/common-hal/canio/Listener.c +#: ports/espressif/common-hal/canio/Listener.c +#: ports/mimxrt10xx/common-hal/canio/Listener.c +#: ports/stm/common-hal/canio/Listener.c +msgid "Filters too complex" +msgstr "" + +#: ports/espressif/common-hal/dualbank/__init__.c +msgid "Firmware is duplicate" +msgstr "" + +#: ports/espressif/common-hal/dualbank/__init__.c +msgid "Firmware is invalid" +msgstr "" + +#: ports/espressif/common-hal/dualbank/__init__.c +msgid "Firmware is too big" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "For L8 colorspace, input bitmap must have 8 bits per pixel" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "For RGB colorspaces, input bitmap must have 16 bits per pixel" +msgstr "" + +#: ports/cxd56/common-hal/camera/Camera.c shared-module/audiocore/WaveFile.c +msgid "Format not supported" +msgstr "" + +#: ports/mimxrt10xx/common-hal/microcontroller/Processor.c +msgid "" +"Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" +msgstr "" + +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "" + +#: ports/cxd56/common-hal/gnss/GNSS.c +msgid "GNSS init" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Generic Failure" +msgstr "" + +#: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-module/busdisplay/BusDisplay.c +#: shared-module/framebufferio/FramebufferDisplay.c +msgid "Group already used" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Hard fault: memory access or instruction error." +msgstr "" + +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/UART.c ports/stm/common-hal/busio/I2C.c +#: ports/stm/common-hal/busio/SPI.c ports/stm/common-hal/busio/UART.c +#: ports/stm/common-hal/canio/CAN.c ports/stm/common-hal/sdioio/SDCard.c +msgid "Hardware in use, try alternative pins" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Heap allocation when VM not running." +msgstr "" + +#: extmod/vfs_posix_file.c py/objstringio.c +msgid "I/O operation on closed file" +msgstr "" + +#: ports/stm/common-hal/busio/I2C.c +msgid "I2C init error" +msgstr "" + +#: ports/raspberrypi/common-hal/busio/I2C.c +#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c +msgid "I2C peripheral in use" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "In-buffer elements must be <= 4 bytes long" +msgstr "" + +#: shared-bindings/_pew/PewPew.c +msgid "Incorrect buffer size" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "Init program size invalid" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Initial set pin direction conflicts with initial out pin direction" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Initial set pin state conflicts with initial out pin state" +msgstr "" + +#: shared-bindings/bitops/__init__.c +#, c-format +msgid "Input buffer length (%d) must be a multiple of the strand count (%d)" +msgstr "" + +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +msgid "Input taking too long" +msgstr "" + +#: py/moderrno.c +msgid "Input/output error" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Insufficient authentication" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Insufficient encryption" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Insufficient memory pool for the image" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Insufficient stream input buffer" +msgstr "" + +#: ports/espressif/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "Interface must be started" +msgstr "" + +#: ports/atmel-samd/audio_dma.c ports/raspberrypi/audio_dma.c +msgid "Internal audio buffer too small" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "Internal define error" +msgstr "" + +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c +msgid "Internal error" +msgstr "" + +#: shared-module/rgbmatrix/RGBMatrix.c +#, c-format +msgid "Internal error #%d" +msgstr "" + +#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c +#: ports/atmel-samd/common-hal/countio/Counter.c +#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c +#: ports/atmel-samd/common-hal/max3421e/Max3421E.c +#: ports/atmel-samd/common-hal/ps2io/Ps2.c +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c +#: ports/cxd56/common-hal/pulseio/PulseIn.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +#: shared-bindings/pwmio/PWMOut.c +msgid "Internal resource(s) in use" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Internal watchdog timer expired." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Interrupt error." +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Interrupted by output function" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c +#: ports/espressif/common-hal/_bleio/Service.c +#: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c +#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c +#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c +#: ports/raspberrypi/bindings/picodvi/Framebuffer.c +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c +#: shared-module/aurora_epaper/aurora_framebuffer.c +#: shared-module/lvfontio/OnDiskFont.c +msgid "Invalid %q" +msgstr "" + +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +#: shared-module/aurora_epaper/aurora_framebuffer.c +msgid "Invalid %q and %q" +msgstr "" + +#: ports/atmel-samd/common-hal/microcontroller/Pin.c +#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c +#: ports/mimxrt10xx/common-hal/microcontroller/Pin.c +#: shared-bindings/microcontroller/Pin.c +msgid "Invalid %q pin" +msgstr "" + +#: ports/stm/common-hal/analogio/AnalogIn.c +msgid "Invalid ADC Unit value" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Invalid BLE parameter" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "Invalid BSSID" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "Invalid MAC address" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +msgid "Invalid ROS domain ID" +msgstr "" + +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c +msgid "Invalid argument" +msgstr "" + +#: shared-module/displayio/Bitmap.c +msgid "Invalid bits per value" +msgstr "" + +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +#, c-format +msgid "Invalid data_pins[%d]" +msgstr "" + +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c +msgid "Invalid format" +msgstr "" + +#: shared-module/audiocore/WaveFile.c +msgid "Invalid format chunk size" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "Invalid hex password" +msgstr "" + +#: ports/espressif/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "Invalid multicast MAC address" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Invalid size" +msgstr "" + +#: shared-module/ssl/SSLSocket.c +msgid "Invalid socket for TLS" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +#: ports/espressif/common-hal/espidf/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Invalid state" +msgstr "" + +#: supervisor/shared/settings.c +msgid "Invalid unicode escape" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "Key must be 16, 24, or 32 bytes long" +msgstr "" + +#: shared-module/is31fl3741/FrameBuffer.c +msgid "LED mappings must match display size" +msgstr "" + +#: py/compile.c +msgid "LHS of keyword arg must be an id" +msgstr "" + +#: shared-module/displayio/Group.c +msgid "Layer already in a group" +msgstr "" + +#: shared-module/displayio/Group.c +msgid "Layer must be a Group or TileGrid subclass" +msgstr "" + +#: shared-bindings/audiocore/RawSample.c +msgid "Length of %q must be an even multiple of channel_count * type_size" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "MAC address was invalid" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/espressif/common-hal/_bleio/Descriptor.c +msgid "MITM security not supported" +msgstr "" + +#: ports/stm/common-hal/sdioio/SDCard.c +#, c-format +msgid "MMC/SDIO Clock Error %x" +msgstr "" + +#: shared-bindings/is31fl3741/IS31FL3741.c +msgid "Mapping must be a tuple" +msgstr "" + +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Mismatched data size" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Mismatched swap flag" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_in_pin. %q[%u] reads pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_in_pin. %q[%u] shifts in from pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_in_pin. %q[%u] waits based on pin" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_out_pin. %q[%u] shifts out to pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_out_pin. %q[%u] writes pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_set_pin. %q[%u] sets pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing jmp_pin. %q[%u] jumps on pin" +msgstr "" + +#: shared-module/storage/__init__.c +msgid "Mount point directory missing" +msgstr "" + +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c +msgid "Must be a %q subclass." +msgstr "" + +#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c +msgid "Must provide 5/6/5 RGB pins" +msgstr "" + +#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +msgid "Must provide MISO or MOSI pin" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "Must use a multiple of 6 rgb pins, not %d" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "NLR jump failed. Likely memory corruption." +msgstr "" + +#: ports/espressif/common-hal/nvm/ByteArray.c +msgid "NVS Error" +msgstr "" + +#: shared-bindings/socketpool/SocketPool.c +msgid "Name or service not known" +msgstr "" + +#: shared-bindings/displayio/TileGrid.c +msgid "New bitmap must be same size as old bitmap" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +msgid "Nimble out of memory" +msgstr "" + +#: ports/atmel-samd/common-hal/busio/UART.c +#: ports/espressif/common-hal/busio/SPI.c +#: ports/espressif/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c +#: ports/raspberrypi/common-hal/busio/UART.c ports/stm/common-hal/busio/SPI.c +#: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c +#: shared-bindings/i2cdisplaybus/I2CDisplayBus.c +#: shared-bindings/paralleldisplaybus/ParallelBus.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c +msgid "No %q pin" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +msgid "No CCCD for this Characteristic" +msgstr "" + +#: ports/atmel-samd/common-hal/analogio/AnalogOut.c +#: ports/stm/common-hal/analogio/AnalogOut.c +msgid "No DAC on chip" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "No DMA channel found" +msgstr "" + +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +msgid "No DMA pacing timer found" +msgstr "" + +#: shared-module/adafruit_bus_device/i2c_device/I2CDevice.c +#, c-format +msgid "No I2C device at address: 0x%x" +msgstr "" + +#: supervisor/shared/web_workflow/web_workflow.c +msgid "No IP" +msgstr "" + +#: ports/atmel-samd/common-hal/microcontroller/__init__.c +#: ports/cxd56/common-hal/microcontroller/__init__.c +#: ports/mimxrt10xx/common-hal/microcontroller/__init__.c +msgid "No bootloader present" +msgstr "" + +#: shared-module/usb/core/Device.c +msgid "No configuration set" +msgstr "" + +#: shared-bindings/_bleio/PacketBuffer.c +msgid "No connection: length cannot be determined" +msgstr "" + +#: shared-bindings/board/__init__.c +msgid "No default %q bus" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +#: ports/atmel-samd/common-hal/touchio/TouchIn.c +msgid "No free GCLKs" +msgstr "" + +#: shared-bindings/os/__init__.c +msgid "No hardware random available" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "No in in program" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "No in or out in program" +msgstr "" + +#: py/objint.c shared-bindings/time/__init__.c +msgid "No long integer support" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "No network with that ssid" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "No out in program" +msgstr "" + +#: ports/atmel-samd/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/I2C.c +#: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nordic/common-hal/busio/I2C.c +#: ports/raspberrypi/common-hal/busio/I2C.c +msgid "No pull up found on SDA or SCL; check your wiring" +msgstr "" + +#: shared-module/touchio/TouchIn.c +msgid "No pulldown on pin; 1Mohm recommended" +msgstr "" + +#: shared-module/touchio/TouchIn.c +msgid "No pullup on pin; 1Mohm recommended" +msgstr "" + +#: py/moderrno.c +msgid "No space left on device" +msgstr "" + +#: py/moderrno.c +msgid "No such device" +msgstr "" + +#: py/moderrno.c +msgid "No such file/directory" +msgstr "" + +#: shared-module/rgbmatrix/RGBMatrix.c +msgid "No timer available" +msgstr "" + +#: shared-module/usb/core/Device.c +msgid "No usb host port initialized" +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Nordic system firmware out of memory" +msgstr "" + +#: shared-bindings/ipaddress/IPv4Address.c shared-bindings/ipaddress/__init__.c +msgid "Not a valid IP string" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +#: shared-bindings/_bleio/CharacteristicBuffer.c +msgid "Not connected" +msgstr "" + +#: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c +msgid "Not playing" +msgstr "" + +#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/espressif/common-hal/sdioio/SDCard.c +#, c-format +msgid "Number of data_pins must be %d or %d, not %d" +msgstr "" + +#: shared-bindings/util.c +msgid "" +"Object has been deinitialized and can no longer be used. Create a new object." +msgstr "" + +#: ports/nordic/common-hal/busio/UART.c +msgid "Odd parity is not supported" +msgstr "" + +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Off" +msgstr "" + +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Ok" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c +#, c-format +msgid "Only 8 or 16 bit mono with %dx oversampling supported." +msgstr "" + +#: ports/espressif/common-hal/wifi/__init__.c +#: ports/raspberrypi/common-hal/wifi/__init__.c +msgid "Only IPv4 addresses supported" +msgstr "" + +#: ports/raspberrypi/common-hal/socketpool/Socket.c +msgid "Only IPv4 sockets supported" +msgstr "" + +#: shared-module/displayio/OnDiskBitmap.c +#, c-format +msgid "" +"Only Windows format, uncompressed BMP supported: given header size is %d" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Only edge detection is available on this hardware" +msgstr "" + +#: shared-bindings/ipaddress/__init__.c +msgid "Only int or string supported for ip" +msgstr "" + +#: ports/espressif/common-hal/alarm/touch/TouchAlarm.c +msgid "Only one %q can be set in deep sleep." +msgstr "" + +#: ports/espressif/common-hal/espulp/ULPAlarm.c +msgid "Only one %q can be set." +msgstr "" + +#: ports/espressif/common-hal/i2ctarget/I2CTarget.c +#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c +msgid "Only one address is allowed" +msgstr "" + +#: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c +#: ports/nordic/common-hal/alarm/time/TimeAlarm.c +#: ports/stm/common-hal/alarm/time/TimeAlarm.c +msgid "Only one alarm.time alarm can be set" +msgstr "" + +#: ports/espressif/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c +msgid "Only one alarm.time alarm can be set." +msgstr "" + +#: shared-module/displayio/ColorConverter.c +msgid "Only one color can be transparent at a time" +msgstr "" + +#: py/moderrno.c +msgid "Operation not permitted" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Operation or feature not supported" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "Operation timed out" +msgstr "" + +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "Out of MDNS service slots" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Out of memory" +msgstr "" + +#: ports/espressif/common-hal/socketpool/Socket.c +#: ports/raspberrypi/common-hal/socketpool/Socket.c +#: ports/zephyr-cp/common-hal/socketpool/Socket.c +msgid "Out of sockets" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "Out-buffer elements must be <= 4 bytes long" +msgstr "" + +#: ports/stm/common-hal/pwmio/PWMOut.c +msgid "PWM restart" +msgstr "" + +#: ports/raspberrypi/common-hal/countio/Counter.c +msgid "PWM slice already in use" +msgstr "" + +#: ports/raspberrypi/common-hal/countio/Counter.c +msgid "PWM slice channel A already in use" +msgstr "" + +#: shared-bindings/spitarget/SPITarget.c +msgid "Packet buffers for an SPI transfer must have the same length." +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Parameter error" +msgstr "" + +#: ports/espressif/common-hal/audiobusio/__init__.c +msgid "Peripheral in use" +msgstr "" + +#: py/moderrno.c +msgid "Permission denied" +msgstr "" + +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Pin cannot wake from Deep Sleep" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Pin count too large" +msgstr "" + +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +#: ports/stm/common-hal/pulseio/PulseIn.c +msgid "Pin interrupt already in use" +msgstr "" + +#: shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c +msgid "Pin is input only" +msgstr "" + +#: ports/raspberrypi/common-hal/countio/Counter.c +msgid "Pin must be on PWM Channel B" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "" +"Pinout uses %d bytes per element, which consumes more than the ideal %d " +"bytes. If this cannot be avoided, pass allow_inefficient=True to the " +"constructor" +msgstr "" + +#: ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c +msgid "Pins must be sequential" +msgstr "" + +#: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c +msgid "Pins must be sequential GPIO pins" +msgstr "" + +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +msgid "Pins must share PWM slice" +msgstr "" + +#: shared-module/usb/core/Device.c +msgid "Pipe error" +msgstr "" + +#: py/builtinhelp.c +msgid "Plus any modules on the filesystem\n" +msgstr "" + +#: shared-module/vectorio/Polygon.c +msgid "Polygon needs at least 3 points" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Power dipped. Make sure you are providing enough power." +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Prefix buffer must be on the heap" +msgstr "" + +#: main.c +msgid "Press any key to enter the REPL. Use CTRL-D to reload.\n" +msgstr "" + +#: main.c +msgid "Pretending to deep sleep until alarm, CTRL-C or file write.\n" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Program does IN without loading ISR" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Program does OUT without loading OSR" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "Program size invalid" +msgstr "" + +#: ports/espressif/common-hal/espulp/ULP.c +msgid "Program too long" +msgstr "" + +#: shared-bindings/rclcpy/Publisher.c +msgid "Publishers can only be created from a parent node" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c +msgid "Pull not used when direction is output." +msgstr "" + +#: ports/raspberrypi/common-hal/countio/Counter.c +msgid "RISE_AND_FALL not available on this chip" +msgstr "" + +#: shared-module/displayio/OnDiskBitmap.c +msgid "RLE-compressed BMP not supported" +msgstr "" + +#: ports/stm/common-hal/os/__init__.c +msgid "RNG DeInit Error" +msgstr "" + +#: ports/stm/common-hal/os/__init__.c +msgid "RNG Init Error" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +msgid "ROS failed to initialize. Is agent connected?" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +msgid "ROS internal setup failure" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +msgid "ROS memory allocator failure" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/Node.c +msgid "ROS node failed to initialize" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/Publisher.c +msgid "ROS topic failed to initialize" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +#: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c +msgid "RS485" +msgstr "" + +#: ports/espressif/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/UART.c +msgid "RS485 inversion specified when not in RS485 mode" +msgstr "" + +#: shared-bindings/alarm/time/TimeAlarm.c shared-bindings/time/__init__.c +msgid "RTC is not supported on this board" +msgstr "" + +#: ports/stm/common-hal/os/__init__.c +msgid "Random number generation error" +msgstr "" + +#: shared-bindings/_bleio/__init__.c +#: shared-bindings/memorymonitor/AllocationSize.c +#: shared-bindings/pulseio/PulseIn.c shared-module/bitmaptools/__init__.c +#: shared-module/displayio/Bitmap.c shared-module/displayio/Group.c +msgid "Read-only" +msgstr "" + +#: extmod/vfs_fat.c py/moderrno.c +msgid "Read-only filesystem" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Received response was invalid" +msgstr "" + +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Reconnecting" +msgstr "" + +#: shared-bindings/epaperdisplay/EPaperDisplay.c +msgid "Refresh too soon" +msgstr "" + +#: shared-bindings/canio/RemoteTransmissionRequest.c +msgid "RemoteTransmissionRequests limited to 8 bytes" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "Requested AES mode is unsupported" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Requested resource not found" +msgstr "" + +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +msgid "Right channel unsupported" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Right format but not supported" +msgstr "" + +#: main.c +msgid "Running in safe mode! Not running saved code.\n" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "SD card CSD format not supported" +msgstr "" + +#: ports/cxd56/common-hal/sdioio/SDCard.c +msgid "SDCard init" +msgstr "" + +#: ports/stm/common-hal/sdioio/SDCard.c +#, c-format +msgid "SDIO GetCardInfo Error %d" +msgstr "" + +#: ports/espressif/common-hal/sdioio/SDCard.c +#: ports/stm/common-hal/sdioio/SDCard.c +#, c-format +msgid "SDIO Init Error %x" +msgstr "" + +#: ports/espressif/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + +#: ports/stm/common-hal/busio/SPI.c +msgid "SPI init error" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + +#: ports/raspberrypi/common-hal/busio/SPI.c +msgid "SPI peripheral in use" +msgstr "" + +#: ports/stm/common-hal/busio/SPI.c +msgid "SPI re-init" +msgstr "" + +#: shared-bindings/is31fl3741/FrameBuffer.c +msgid "Scale dimensions must divide by 3" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Scan already in progress. Stop with stop_scan." +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +msgid "Serializer in use" +msgstr "" + +#: shared-bindings/ssl/SSLContext.c +msgid "Server side context cannot have hostname" +msgstr "" + +#: ports/cxd56/common-hal/camera/Camera.c +msgid "Size not supported" +msgstr "" + +#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c +#: shared-bindings/nvm/ByteArray.c +msgid "Slice and value different lengths." +msgstr "" + +#: shared-bindings/displayio/Bitmap.c shared-bindings/displayio/Group.c +#: shared-bindings/displayio/TileGrid.c +#: shared-bindings/memorymonitor/AllocationSize.c +#: shared-bindings/pulseio/PulseIn.c +#: shared-bindings/tilepalettemapper/TilePaletteMapper.c +msgid "Slices not supported" +msgstr "" + +#: ports/espressif/common-hal/socketpool/SocketPool.c +#: ports/raspberrypi/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio" +msgstr "" + +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "Source and destination buffers must be the same length" +msgstr "" + +#: shared-bindings/paralleldisplaybus/ParallelBus.c +msgid "Specify exactly one of data0 or data_pins" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Stack overflow. Increase stack size." +msgstr "" + +#: shared-bindings/alarm/time/TimeAlarm.c +msgid "Supply one of monotonic_time or epoch_time" +msgstr "" + +#: shared-bindings/gnss/GNSS.c +msgid "System entry must be gnss.SatelliteSystem" +msgstr "" + +#: ports/stm/common-hal/microcontroller/Processor.c +msgid "Temperature read timed out" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "The `microcontroller` module was used to boot into safe mode." +msgstr "" + +#: py/obj.c +msgid "The above exception was the direct cause of the following exception:" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +msgid "The length of rgb_pins must be 6, 12, 18, 24, or 30" +msgstr "" + +#: shared-module/audiocore/__init__.c +msgid "The sample's %q does not match" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Third-party firmware fatal error." +msgstr "" + +#: shared-module/imagecapture/ParallelImageCapture.c +msgid "This microcontroller does not support continuous capture." +msgstr "" + +#: shared-module/paralleldisplaybus/ParallelBus.c +msgid "" +"This microcontroller only supports data0=, not data_pins=, because it " +"requires contiguous pins." +msgstr "" + +#: shared-bindings/displayio/TileGrid.c +msgid "Tile height must exactly divide bitmap height" +msgstr "" + +#: shared-bindings/displayio/TileGrid.c +#: shared-bindings/tilepalettemapper/TilePaletteMapper.c +#: shared-module/displayio/TileGrid.c +msgid "Tile index out of bounds" +msgstr "" + +#: shared-bindings/displayio/TileGrid.c +msgid "Tile width must exactly divide bitmap width" +msgstr "" + +#: shared-module/tilepalettemapper/TilePaletteMapper.c +msgid "TilePaletteMapper may only be bound to a TileGrid once" +msgstr "" + +#: shared-bindings/alarm/time/TimeAlarm.c +msgid "Time is in the past." +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +#, c-format +msgid "Timeout is too long: Maximum timeout length is %d seconds" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +msgid "Too many channels in sample" +msgstr "" + +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "Too many channels in sample." +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +msgid "Too many descriptors" +msgstr "" + +#: shared-module/displayio/__init__.c +msgid "Too many display busses; forgot displayio.release_displays() ?" +msgstr "" + +#: shared-module/displayio/__init__.c +msgid "Too many displays" +msgstr "" + +#: ports/espressif/common-hal/_bleio/PacketBuffer.c +#: ports/nordic/common-hal/_bleio/PacketBuffer.c +msgid "Total data to write is larger than %q" +msgstr "" + +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c +#: ports/stm/common-hal/alarm/touch/TouchAlarm.c +msgid "Touch alarms not available" +msgstr "" + +#: py/obj.c +msgid "Traceback (most recent call last):\n" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "UART de-init" +msgstr "" + +#: ports/cxd56/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/stm/common-hal/busio/UART.c +msgid "UART init" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + +#: ports/raspberrypi/common-hal/busio/UART.c +msgid "UART peripheral in use" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "UART re-init" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "UART write" +msgstr "" + +#: main.c +msgid "UID:" +msgstr "" + +#: shared-module/usb_hid/Device.c +msgid "USB busy" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "USB devices need more endpoints than are available." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "USB devices specify too many interface names." +msgstr "" + +#: shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "" + +#: shared-bindings/_bleio/UUID.c +msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" +msgstr "" + +#: shared-bindings/_bleio/UUID.c +msgid "UUID value is not str, int or byte buffer" +msgstr "" + +#: ports/raspberrypi/common-hal/memorymap/AddressRange.c +msgid "Unable to access unaligned IO register" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "Unable to allocate buffers for signed conversion" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Unable to allocate to the heap." +msgstr "" + +#: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c +msgid "Unable to create lock" +msgstr "" + +#: shared-module/i2cdisplaybus/I2CDisplayBus.c +#: shared-module/is31fl3741/IS31FL3741.c +#, c-format +msgid "Unable to find I2C Display at %x" +msgstr "" + +#: py/parse.c +msgid "Unable to init parser" +msgstr "" + +#: shared-module/displayio/OnDiskBitmap.c +msgid "Unable to read color palette data" +msgstr "" + +#: ports/mimxrt10xx/common-hal/canio/CAN.c +msgid "Unable to send CAN Message: all Tx message buffers are busy" +msgstr "" + +#: ports/espressif/common-hal/mdns/Server.c +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "Unable to start mDNS query" +msgstr "" + +#: shared-bindings/nvm/ByteArray.c +msgid "Unable to write to nvm." +msgstr "" + +#: ports/raspberrypi/common-hal/memorymap/AddressRange.c +msgid "Unable to write to read-only memory" +msgstr "" + +#: shared-bindings/alarm/SleepMemory.c +msgid "Unable to write to sleep_memory." +msgstr "" + +#: ports/nordic/common-hal/_bleio/UUID.c +msgid "Unexpected nrfx uuid type" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown BLE error at %s:%d: %d" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown BLE error: %d" +msgstr "" + +#: ports/espressif/common-hal/max3421e/Max3421E.c +#: ports/raspberrypi/common-hal/wifi/__init__.c +#, c-format +msgid "Unknown error code %d" +msgstr "" + +#: shared-bindings/wifi/Radio.c +#, c-format +msgid "Unknown failure %d" +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown gatt error: 0x%04x" +msgstr "" + +#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c +#: supervisor/shared/safe_mode.c +msgid "Unknown reason." +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown security error: 0x%04x" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown system firmware error at %s:%d: %d" +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown system firmware error: %04x" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown system firmware error: %d" +msgstr "" + +#: shared-bindings/adafruit_pixelbuf/PixelBuf.c +#: shared-module/_pixelmap/PixelMap.c +#, c-format +msgid "Unmatched number of items on RHS (expected %d, got %d)." +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "" +"Unspecified issue. Can be that the pairing prompt on the other device was " +"declined or ignored." +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "Unsupported colorspace" +msgstr "" + +#: shared-module/displayio/bus_core.c +msgid "Unsupported display bus type" +msgstr "" + +#: shared-bindings/hashlib/__init__.c +msgid "Unsupported hash algorithm" +msgstr "" + +#: ports/espressif/common-hal/socketpool/Socket.c +#: ports/zephyr-cp/common-hal/socketpool/Socket.c +msgid "Unsupported socket type" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/espressif/common-hal/dualbank/__init__.c +msgid "Update failed" +msgstr "" + +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Descriptor.c +msgid "Value length != required fixed length" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Descriptor.c +msgid "Value length > max_length" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Version was invalid" +msgstr "" + +#: ports/stm/common-hal/microcontroller/Processor.c +msgid "Voltage read timed out" +msgstr "" + +#: main.c +msgid "WARNING: Your code filename has two extensions\n" +msgstr "" + +#: ports/nordic/common-hal/watchdog/WatchDogTimer.c +msgid "WatchDogTimer cannot be deinitialized once mode is set to RESET" +msgstr "" + +#: py/builtinhelp.c +#, c-format +msgid "" +"Welcome to Adafruit CircuitPython %s!\n" +"\n" +"Visit circuitpython.org for more information.\n" +"\n" +"To list built-in modules type `help(\"modules\")`.\n" +msgstr "" + +#: supervisor/shared/web_workflow/web_workflow.c +msgid "Wi-Fi: " +msgstr "" + +#: ports/espressif/common-hal/wifi/Radio.c +#: ports/raspberrypi/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "WiFi is not enabled" +msgstr "" + +#: main.c +msgid "Woken up by alarm.\n" +msgstr "" + +#: ports/espressif/common-hal/_bleio/PacketBuffer.c +#: ports/nordic/common-hal/_bleio/PacketBuffer.c +msgid "Writes not supported on Characteristic" +msgstr "" + +#: ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h +#: ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h +#: ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.h +#: ports/atmel-samd/boards/meowmeow/mpconfigboard.h +msgid "You pressed both buttons at start up." +msgstr "" + +#: ports/espressif/boards/m5stack_core_basic/mpconfigboard.h +#: ports/espressif/boards/m5stack_core_fire/mpconfigboard.h +#: ports/espressif/boards/m5stack_stick_c/mpconfigboard.h +#: ports/espressif/boards/m5stack_stick_c_plus/mpconfigboard.h +#: ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h +msgid "You pressed button A at start up." +msgstr "" + +#: ports/espressif/boards/m5stack_m5paper/mpconfigboard.h +msgid "You pressed button DOWN at start up." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "You pressed the BOOT button at start up" +msgstr "" + +#: ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.h +#: ports/espressif/boards/adafruit_itsybitsy_esp32/mpconfigboard.h +#: ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/mpconfigboard.h +msgid "You pressed the BOOT button at start up." +msgstr "" + +#: ports/espressif/boards/adafruit_huzzah32_breakout/mpconfigboard.h +msgid "You pressed the GPIO0 button at start up." +msgstr "" + +#: ports/espressif/boards/espressif_esp32_lyrat/mpconfigboard.h +msgid "You pressed the Rec button at start up." +msgstr "" + +#: ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h +msgid "You pressed the SW38 button at start up." +msgstr "" + +#: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h +#: ports/espressif/boards/vidi_x/mpconfigboard.h +msgid "You pressed the VOLUME button at start up." +msgstr "" + +#: ports/espressif/boards/m5stack_atom_echo/mpconfigboard.h +#: ports/espressif/boards/m5stack_atom_lite/mpconfigboard.h +#: ports/espressif/boards/m5stack_atom_matrix/mpconfigboard.h +#: ports/espressif/boards/m5stack_atom_u/mpconfigboard.h +msgid "You pressed the central button at start up." +msgstr "" + +#: ports/nordic/boards/aramcon2_badge/mpconfigboard.h +msgid "You pressed the left button at start up." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "You pressed the reset button during boot." +msgstr "" + +#: supervisor/shared/micropython.c +msgid "[truncated due to length]" +msgstr "" + +#: py/objtype.c +msgid "__init__() should return None" +msgstr "" + +#: py/objtype.c +#, c-format +msgid "__init__() should return None, not '%s'" +msgstr "" + +#: py/objobject.c +msgid "__new__ arg must be a user-type" +msgstr "" + +#: extmod/modbinascii.c extmod/modhashlib.c py/objarray.c +msgid "a bytes-like object is required" +msgstr "" + +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + +#: shared-bindings/i2ctarget/I2CTarget.c +msgid "addresses is empty" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "already playing" +msgstr "" + +#: py/compile.c +msgid "annotation must be an identifier" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "arange: cannot compute length" +msgstr "" + +#: py/modbuiltins.c +msgid "arg is an empty sequence" +msgstr "" + +#: py/objobject.c +msgid "arg must be user-type" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "argsort argument must be an ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "argsort is not implemented for flattened arrays" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "argument must be None, an integer or a tuple of integers" +msgstr "" + +#: py/compile.c +msgid "argument name reused" +msgstr "" + +#: py/argcheck.c shared-bindings/_stage/__init__.c +#: shared-bindings/digitalio/DigitalInOut.c +msgid "argument num/types mismatch" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/numpy/transform.c +msgid "arguments must be ndarrays" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "array and index length must be equal" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "array has too many dimensions" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "array is too big" +msgstr "" + +#: py/objarray.c shared-bindings/alarm/SleepMemory.c +#: shared-bindings/memorymap/AddressRange.c shared-bindings/nvm/ByteArray.c +msgid "array/bytes required on right side" +msgstr "" + +#: py/asmxtensa.c +msgid "asm overflow" +msgstr "" + +#: py/compile.c +msgid "async for/with outside async function" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "attempt to get (arg)min/(arg)max of empty sequence" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "attempt to get argmin/argmax of an empty sequence" +msgstr "" + +#: py/objstr.c +msgid "attributes not supported" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "audio format not supported" +msgstr "" + +#: extmod/ulab/code/ulab_tools.c +msgid "axis is out of bounds" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c +msgid "axis must be None, or an integer" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "axis too long" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "background value out of range of target" +msgstr "" + +#: py/builtinevex.c +msgid "bad compile mode" +msgstr "" + +#: py/objstr.c +msgid "bad conversion specifier" +msgstr "" + +#: py/objstr.c +msgid "bad format string" +msgstr "" + +#: py/binary.c py/objarray.c +msgid "bad typecode" +msgstr "" + +#: py/emitnative.c +msgid "binary op %q not implemented" +msgstr "" + +#: ports/espressif/common-hal/audiobusio/PDMIn.c +msgid "bit_depth must be 8, 16, 24, or 32." +msgstr "" + +#: shared-module/bitmapfilter/__init__.c +msgid "bitmap size and depth must match" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "bitmap sizes must match" +msgstr "" + +#: extmod/modrandom.c +msgid "bits must be 32 or less" +msgstr "" + +#: shared-bindings/audiofreeverb/Freeverb.c +msgid "bits_per_sample must be 16" +msgstr "" + +#: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c +#: shared-bindings/audiodelays/PitchShift.c +#: shared-bindings/audiofilters/Distortion.c +#: shared-bindings/audiofilters/Filter.c shared-bindings/audiofilters/Phaser.c +#: shared-bindings/audiomixer/Mixer.c +msgid "bits_per_sample must be 8 or 16" +msgstr "" + +#: py/emitinlinethumb.c +msgid "branch not in range" +msgstr "" + +#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c +msgid "buffer is smaller than requested size" +msgstr "" + +#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c +msgid "buffer size must be a multiple of element size" +msgstr "" + +#: shared-module/struct/__init__.c +msgid "buffer size must match format" +msgstr "" + +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "" + +#: py/modstruct.c shared-module/struct/__init__.c +msgid "buffer too small" +msgstr "" + +#: shared-bindings/socketpool/Socket.c shared-bindings/ssl/SSLSocket.c +msgid "buffer too small for requested bytes" +msgstr "" + +#: py/emitbc.c +msgid "bytecode overflow" +msgstr "" + +#: py/objarray.c +msgid "bytes length not a multiple of item size" +msgstr "" + +#: py/objstr.c +msgid "bytes value out of range" +msgstr "" + +#: ports/atmel-samd/bindings/samd/Clock.c +msgid "calibration is out of range" +msgstr "" + +#: ports/atmel-samd/bindings/samd/Clock.c +msgid "calibration is read only" +msgstr "" + +#: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c +#: shared-module/vectorio/Rectangle.c +msgid "can only have one parent" +msgstr "" + +#: py/emitinlinerv32.c +msgid "can only have up to 4 parameters for RV32 assembly" +msgstr "" + +#: py/emitinlinethumb.c +msgid "can only have up to 4 parameters to Thumb assembly" +msgstr "" + +#: py/emitinlinextensa.c +msgid "can only have up to 4 parameters to Xtensa assembly" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "can only specify one unknown dimension" +msgstr "" + +#: py/objtype.c +msgid "can't add special method to already-subclassed class" +msgstr "" + +#: py/compile.c +msgid "can't assign to expression" +msgstr "" + +#: extmod/modasyncio.c +msgid "can't cancel self" +msgstr "" + +#: py/obj.c shared-module/adafruit_pixelbuf/PixelBuf.c +msgid "can't convert %q to %q" +msgstr "" + +#: py/obj.c +#, c-format +msgid "can't convert %s to complex" +msgstr "" + +#: py/obj.c +#, c-format +msgid "can't convert %s to float" +msgstr "" + +#: py/objint.c py/runtime.c +#, c-format +msgid "can't convert %s to int" +msgstr "" + +#: py/objstr.c +msgid "can't convert '%q' object to %q implicitly" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "can't convert complex to float" +msgstr "" + +#: py/obj.c +msgid "can't convert to complex" +msgstr "" + +#: py/obj.c +msgid "can't convert to float" +msgstr "" + +#: py/runtime.c +msgid "can't convert to int" +msgstr "" + +#: py/objstr.c +msgid "can't convert to str implicitly" +msgstr "" + +#: py/objtype.c +msgid "can't create '%q' instances" +msgstr "" + +#: py/compile.c +msgid "can't declare nonlocal in outer code" +msgstr "" + +#: py/compile.c +msgid "can't delete expression" +msgstr "" + +#: py/emitnative.c +msgid "can't do binary op between '%q' and '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't do unary op of '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't implicitly convert '%q' to 'bool'" +msgstr "" + +#: py/runtime.c +msgid "can't import name %q" +msgstr "" + +#: py/emitnative.c +msgid "can't load from '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't load with '%q' index" +msgstr "" + +#: py/builtinimport.c +msgid "can't perform relative import" +msgstr "" + +#: py/objgenerator.c +msgid "can't send non-None value to a just-started generator" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "can't set 512 block size" +msgstr "" + +#: py/objexcept.c py/objnamedtuple.c +msgid "can't set attribute" +msgstr "" + +#: py/runtime.c +msgid "can't set attribute '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't store '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't store to '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't store with '%q' index" +msgstr "" + +#: py/objstr.c +msgid "" +"can't switch from automatic field numbering to manual field specification" +msgstr "" + +#: py/objstr.c +msgid "" +"can't switch from manual field specification to automatic field numbering" +msgstr "" + +#: py/objcomplex.c +msgid "can't truncate-divide a complex number" +msgstr "" + +#: extmod/modasyncio.c +msgid "can't wait" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot assign new shape" +msgstr "" + +#: extmod/ulab/code/ndarray_operators.c +msgid "cannot cast output with casting rule" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot convert complex to dtype" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot convert complex type" +msgstr "" + +#: py/objtype.c +msgid "cannot create instance" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot delete array elements" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot reshape array" +msgstr "" + +#: py/emitnative.c +msgid "casting" +msgstr "" + +#: ports/stm/common-hal/pwmio/PWMOut.c +msgid "channel re-init" +msgstr "" + +#: shared-bindings/_stage/Text.c +msgid "chars buffer too small" +msgstr "" + +#: py/modbuiltins.c +msgid "chr() arg not in range(0x110000)" +msgstr "" + +#: py/modbuiltins.c +msgid "chr() arg not in range(256)" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "clip point must be (x,y) tuple" +msgstr "" + +#: shared-bindings/msgpack/ExtType.c +msgid "code outside range 0~127" +msgstr "" + +#: shared-bindings/displayio/Palette.c +msgid "color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)" +msgstr "" + +#: shared-bindings/displayio/Palette.c +msgid "color buffer must be a buffer, tuple, list, or int" +msgstr "" + +#: shared-bindings/displayio/Palette.c +msgid "color buffer must be a bytearray or array of type 'b' or 'B'" +msgstr "" + +#: shared-bindings/displayio/Palette.c +msgid "color must be between 0x000000 and 0xffffff" +msgstr "" + +#: py/emitnative.c +msgid "comparison of int and uint" +msgstr "" + +#: py/objcomplex.c +msgid "complex divide by zero" +msgstr "" + +#: py/objfloat.c py/parsenum.c +msgid "complex values not supported" +msgstr "" + +#: extmod/modzlib.c +msgid "compression header" +msgstr "" + +#: py/emitnative.c +msgid "conversion to object" +msgstr "" + +#: extmod/ulab/code/numpy/filter.c +msgid "convolve arguments must be linear arrays" +msgstr "" + +#: extmod/ulab/code/numpy/filter.c +msgid "convolve arguments must be ndarrays" +msgstr "" + +#: extmod/ulab/code/numpy/filter.c +msgid "convolve arguments must not be empty" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "corrupted file" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "could not invert Vandermonde matrix" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "couldn't determine SD card version" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "cross is defined for 1D arrays of length 3" +msgstr "" + +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "cs pin must be 1-4 positions above mosi pin" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "data must be iterable" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "data must be of equal length" +msgstr "" + +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +#, c-format +msgid "data pin #%d in use" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "data type not understood" +msgstr "" + +#: py/parsenum.c +msgid "decimal numbers not supported" +msgstr "" + +#: py/compile.c +msgid "default 'except' must be last" +msgstr "" + +#: shared-bindings/msgpack/__init__.c +msgid "default is not a function" +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "" +"destination buffer must be a bytearray or array of type 'B' for bit_depth = 8" +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "destination buffer must be an array of type 'H' for bit_depth = 16" +msgstr "" + +#: py/objdict.c +msgid "dict update sequence has wrong length" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "diff argument must be an ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "differentiation order out of range" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c +msgid "dimensions do not match" +msgstr "" + +#: py/emitnative.c +msgid "div/mod not implemented for uint" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "divide by zero" +msgstr "" + +#: py/runtime.c +msgid "division by zero" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "dtype must be float, or complex" +msgstr "" + +#: extmod/ulab/code/ndarray_operators.c +msgid "dtype of int32 is not supported" +msgstr "" + +#: py/objdeque.c +msgid "empty" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "empty file" +msgstr "" + +#: extmod/modasyncio.c extmod/modheapq.c +msgid "empty heap" +msgstr "" + +#: py/objstr.c +msgid "empty separator" +msgstr "" + +#: shared-bindings/random/__init__.c +msgid "empty sequence" +msgstr "" + +#: py/objstr.c +msgid "end of format while looking for conversion specifier" +msgstr "" + +#: shared-bindings/alarm/time/TimeAlarm.c +msgid "epoch_time not supported on this board" +msgstr "" + +#: ports/nordic/common-hal/busio/UART.c +#, c-format +msgid "error = 0x%08lX" +msgstr "" + +#: py/runtime.c +msgid "exceptions must derive from BaseException" +msgstr "" + +#: py/objstr.c +msgid "expected ':' after format specifier" +msgstr "" + +#: py/obj.c +msgid "expected tuple/list" +msgstr "" + +#: py/modthread.c +msgid "expecting a dict for keyword args" +msgstr "" + +#: py/compile.c +msgid "expecting an assembler instruction" +msgstr "" + +#: py/compile.c +msgid "expecting just a value for set" +msgstr "" + +#: py/compile.c +msgid "expecting key:value for dict" +msgstr "" + +#: shared-bindings/msgpack/__init__.c +msgid "ext_hook is not a function" +msgstr "" + +#: py/argcheck.c +msgid "extra keyword arguments given" +msgstr "" + +#: py/argcheck.c +msgid "extra positional arguments given" +msgstr "" + +#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c +#: shared-bindings/displayio/OnDiskBitmap.c shared-bindings/gifio/OnDiskGif.c +#: shared-bindings/synthio/__init__.c shared-module/gifio/GifWriter.c +msgid "file must be a file opened in byte mode" +msgstr "" + +#: shared-bindings/traceback/__init__.c +msgid "file write is not available" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "first argument must be a callable" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "first argument must be a function" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "first argument must be a tuple of ndarrays" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c extmod/ulab/code/numpy/vector.c +msgid "first argument must be an ndarray" +msgstr "" + +#: py/objtype.c +msgid "first argument to super() must be type" +msgstr "" + +#: extmod/ulab/code/scipy/linalg/linalg.c +msgid "first two arguments must be ndarrays" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "flattening order must be either 'C', or 'F'" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "flip argument must be an ndarray" +msgstr "" + +#: py/objint.c +msgid "float too big" +msgstr "" + +#: py/nativeglue.c +msgid "float unsupported" +msgstr "" + +#: extmod/moddeflate.c +msgid "format" +msgstr "" + +#: py/objstr.c +msgid "format needs a dict" +msgstr "" + +#: py/objstr.c +msgid "format string didn't convert all arguments" +msgstr "" + +#: py/objstr.c +msgid "format string needs more arguments" +msgstr "" + +#: py/objdeque.c +msgid "full" +msgstr "" + +#: py/argcheck.c +msgid "function doesn't take keyword arguments" +msgstr "" + +#: py/argcheck.c +#, c-format +msgid "function expected at most %d arguments, got %d" +msgstr "" + +#: py/bc.c py/objnamedtuple.c +msgid "function got multiple values for argument '%q'" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "function has the same sign at the ends of interval" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "function is defined for ndarrays only" +msgstr "" + +#: extmod/ulab/code/numpy/carray/carray.c +msgid "function is implemented for ndarrays only" +msgstr "" + +#: py/argcheck.c +#, c-format +msgid "function missing %d required positional arguments" +msgstr "" + +#: py/bc.c +msgid "function missing keyword-only argument" +msgstr "" + +#: py/bc.c +msgid "function missing required keyword argument '%q'" +msgstr "" + +#: py/bc.c +#, c-format +msgid "function missing required positional argument #%d" +msgstr "" + +#: py/argcheck.c py/bc.c py/objnamedtuple.c shared-bindings/_eve/__init__.c +#: shared-bindings/time/__init__.c +#, c-format +msgid "function takes %d positional arguments but %d were given" +msgstr "" + +#: shared-bindings/mcp4822/MCP4822.c +msgid "gain must be 1 or 2" +msgstr "" + +#: py/objgenerator.c +msgid "generator already executing" +msgstr "" + +#: py/objgenerator.c +msgid "generator ignored GeneratorExit" +msgstr "" + +#: py/objgenerator.c py/runtime.c +msgid "generator raised StopIteration" +msgstr "" + +#: extmod/modhashlib.c +msgid "hash is final" +msgstr "" + +#: extmod/modheapq.c +msgid "heap must be a list" +msgstr "" + +#: py/compile.c +msgid "identifier redefined as global" +msgstr "" + +#: py/compile.c +msgid "identifier redefined as nonlocal" +msgstr "" + +#: py/compile.c +msgid "import * not at module level" +msgstr "" + +#: py/persistentcode.c +msgid "incompatible .mpy arch" +msgstr "" + +#: py/persistentcode.c +msgid "incompatible .mpy file" +msgstr "" + +#: py/objstr.c +msgid "incomplete format" +msgstr "" + +#: py/objstr.c +msgid "incomplete format key" +msgstr "" + +#: extmod/modbinascii.c +msgid "incorrect padding" +msgstr "" + +#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/transform.c +msgid "index is out of bounds" +msgstr "" + +#: shared-bindings/_pixelmap/PixelMap.c +msgid "index must be tuple or int" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c +#: ports/espressif/common-hal/pulseio/PulseIn.c +#: shared-bindings/bitmaptools/__init__.c +msgid "index out of range" +msgstr "" + +#: py/obj.c +msgid "indices must be integers" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "indices must be integers, slices, or Boolean lists" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "initial values must be iterable" +msgstr "" + +#: py/compile.c +msgid "inline assembler must be a function" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "input and output dimensions differ" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "input and output shapes differ" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "input argument must be an integer, a tuple, or a list" +msgstr "" + +#: extmod/ulab/code/numpy/fft/fft_tools.c +msgid "input array length must be power of 2" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "input arrays are not compatible" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "input data must be an iterable" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "input dtype must be float or complex" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "input is not iterable" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "input matrix is asymmetric" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +#: extmod/ulab/code/scipy/linalg/linalg.c +msgid "input matrix is singular" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "input must be 1- or 2-d" +msgstr "" + +#: extmod/ulab/code/numpy/carray/carray.c +msgid "input must be a 1D ndarray" +msgstr "" + +#: extmod/ulab/code/scipy/linalg/linalg.c extmod/ulab/code/user/user.c +msgid "input must be a dense ndarray" +msgstr "" + +#: extmod/ulab/code/user/user.c shared-bindings/_eve/__init__.c +msgid "input must be an ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/carray/carray.c +msgid "input must be an ndarray, or a scalar" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "input must be one-dimensional" +msgstr "" + +#: extmod/ulab/code/ulab_tools.c +msgid "input must be square matrix" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "input must be tuple, list, range, or ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "input vectors must be of equal length" +msgstr "" + +#: extmod/ulab/code/numpy/approx.c +msgid "interp is defined for 1D iterables of equal length" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +#, c-format +msgid "interval must be in range %s-%s" +msgstr "" + +#: py/compile.c +msgid "invalid arch" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +#, c-format +msgid "invalid bits_per_pixel %d, must be, 1, 2, 4, 8, 16, 24, or 32" +msgstr "" + +#: shared-module/ssl/SSLSocket.c +msgid "invalid cert" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +#, c-format +msgid "invalid element size %d for bits_per_pixel %d\n" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +#, c-format +msgid "invalid element_size %d, must be, 1, 2, or 4" +msgstr "" + +#: shared-bindings/traceback/__init__.c +msgid "invalid exception" +msgstr "" + +#: py/objstr.c +msgid "invalid format specifier" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "invalid hostname" +msgstr "" + +#: shared-module/ssl/SSLSocket.c +msgid "invalid key" +msgstr "" + +#: py/compile.c +msgid "invalid micropython decorator" +msgstr "" + +#: ports/espressif/common-hal/espcamera/Camera.c +msgid "invalid setting" +msgstr "" + +#: shared-bindings/random/__init__.c +msgid "invalid step" +msgstr "" + +#: py/compile.c py/parse.c +msgid "invalid syntax" +msgstr "" + +#: py/parsenum.c +msgid "invalid syntax for integer" +msgstr "" + +#: py/parsenum.c +#, c-format +msgid "invalid syntax for integer with base %d" +msgstr "" + +#: py/parsenum.c +msgid "invalid syntax for number" +msgstr "" + +#: py/objtype.c +msgid "issubclass() arg 1 must be a class" +msgstr "" + +#: py/objtype.c +msgid "issubclass() arg 2 must be a class or a tuple of classes" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "iterations did not converge" +msgstr "" + +#: py/objstr.c +msgid "join expects a list of str/bytes objects consistent with self object" +msgstr "" + +#: py/argcheck.c +msgid "keyword argument(s) not implemented - use normal args instead" +msgstr "" + +#: py/emitinlinethumb.c py/emitinlinextensa.c +msgid "label '%q' not defined" +msgstr "" + +#: py/compile.c +msgid "label redefined" +msgstr "" + +#: py/objarray.c +msgid "lhs and rhs should be compatible" +msgstr "" + +#: py/emitnative.c +msgid "local '%q' has type '%q' but source is '%q'" +msgstr "" + +#: py/emitnative.c +msgid "local '%q' used before type known" +msgstr "" + +#: py/vm.c +msgid "local variable referenced before assignment" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +msgid "loopback + silent mode not supported by peripheral" +msgstr "" + +#: ports/espressif/common-hal/mdns/Server.c +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "mDNS already initialized" +msgstr "" + +#: ports/espressif/common-hal/mdns/Server.c +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "mDNS only works with built-in WiFi" +msgstr "" + +#: py/parse.c +msgid "malformed f-string" +msgstr "" + +#: shared-bindings/_stage/Layer.c +msgid "map buffer too small" +msgstr "" + +#: py/modmath.c shared-bindings/math/__init__.c +msgid "math domain error" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "matrix is not positive definite" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Descriptor.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Descriptor.c +#, c-format +msgid "max_length must be 0-%d when fixed_length is %s" +msgstr "" + +#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/random/random.c +msgid "maximum number of dimensions is " +msgstr "" + +#: py/runtime.c +msgid "maximum recursion depth exceeded" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "maxiter must be > 0" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "maxiter should be > 0" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "median argument must be an ndarray" +msgstr "" + +#: py/runtime.c +#, c-format +msgid "memory allocation failed, allocating %u bytes" +msgstr "" + +#: py/runtime.c +msgid "memory allocation failed, heap is locked" +msgstr "" + +#: py/objarray.c +msgid "memoryview offset too large" +msgstr "" + +#: py/objarray.c +msgid "memoryview: length is not a multiple of itemsize" +msgstr "" + +#: extmod/modtime.c +msgid "mktime needs a tuple of length 8 or 9" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "mode must be complete, or reduced" +msgstr "" + +#: py/builtinimport.c +msgid "module not found" +msgstr "" + +#: ports/espressif/common-hal/wifi/Monitor.c +msgid "monitor init failed" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "more degrees of freedom than data points" +msgstr "" + +#: py/compile.c +msgid "multiple *x in assignment" +msgstr "" + +#: py/objtype.c +msgid "multiple bases have instance lay-out conflict" +msgstr "" + +#: py/objtype.c +msgid "multiple inheritance not supported" +msgstr "" + +#: py/emitnative.c +msgid "must raise an object" +msgstr "" + +#: py/modbuiltins.c +msgid "must use keyword argument for key function" +msgstr "" + +#: py/runtime.c +msgid "name '%q' isn't defined" +msgstr "" + +#: py/runtime.c +msgid "name not defined" +msgstr "" + +#: py/qstr.c +msgid "name too long" +msgstr "" + +#: py/persistentcode.c +msgid "native code in .mpy unsupported" +msgstr "" + +#: py/asmthumb.c +msgid "native method too big" +msgstr "" + +#: py/emitnative.c +msgid "native yield" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "ndarray length overflows" +msgstr "" + +#: py/runtime.c +#, c-format +msgid "need more than %d values to unpack" +msgstr "" + +#: py/modmath.c +msgid "negative factorial" +msgstr "" + +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c +msgid "negative power with no float support" +msgstr "" + +#: py/objint_mpz.c py/runtime.c +msgid "negative shift count" +msgstr "" + +#: shared-bindings/_pixelmap/PixelMap.c +msgid "nested index must be int" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "no SD card" +msgstr "" + +#: py/vm.c +msgid "no active exception to reraise" +msgstr "" + +#: py/compile.c +msgid "no binding for nonlocal found" +msgstr "" + +#: shared-module/msgpack/__init__.c +msgid "no default packer" +msgstr "" + +#: extmod/modrandom.c extmod/ulab/code/numpy/random/random.c +msgid "no default seed" +msgstr "" + +#: py/builtinimport.c +msgid "no module named '%q'" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "no response from SD card" +msgstr "" + +#: ports/espressif/common-hal/espcamera/Camera.c py/objobject.c py/runtime.c +msgid "no such attribute" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Connection.c +#: ports/nordic/common-hal/_bleio/Connection.c +msgid "non-UUID found in service_uuids_whitelist" +msgstr "" + +#: py/compile.c +msgid "non-default argument follows default argument" +msgstr "" + +#: py/objstr.c +msgid "non-hex digit" +msgstr "" + +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "non-zero timeout must be > 0.01" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "non-zero timeout must be >= interval" +msgstr "" + +#: shared-bindings/_bleio/UUID.c +msgid "not a 128-bit UUID" +msgstr "" + +#: py/parse.c +msgid "not a constant" +msgstr "" + +#: extmod/ulab/code/numpy/carray/carray_tools.c +msgid "not implemented for complex dtype" +msgstr "" + +#: extmod/ulab/code/numpy/bitwise.c +msgid "not supported for input types" +msgstr "" + +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "number of points must be at least 2" +msgstr "" + +#: py/builtinhelp.c +msgid "object " +msgstr "" + +#: py/obj.c +#, c-format +msgid "object '%s' isn't a tuple or list" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + +#: py/obj.c +msgid "object doesn't support item assignment" +msgstr "" + +#: py/obj.c +msgid "object doesn't support item deletion" +msgstr "" + +#: py/obj.c +msgid "object has no len" +msgstr "" + +#: py/obj.c +msgid "object isn't subscriptable" +msgstr "" + +#: py/runtime.c +msgid "object not an iterator" +msgstr "" + +#: py/objtype.c py/runtime.c +msgid "object not callable" +msgstr "" + +#: py/sequence.c shared-bindings/displayio/Group.c +msgid "object not in sequence" +msgstr "" + +#: py/runtime.c +msgid "object not iterable" +msgstr "" + +#: py/obj.c +#, c-format +msgid "object of type '%s' has no len()" +msgstr "" + +#: py/obj.c +msgid "object with buffer protocol required" +msgstr "" + +#: supervisor/shared/web_workflow/web_workflow.c +msgid "off" +msgstr "" + +#: extmod/ulab/code/utils/utils.c +msgid "offset is too large" +msgstr "" + +#: shared-bindings/dualbank/__init__.c +msgid "offset must be >= 0" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "offset must be non-negative and no greater than buffer length" +msgstr "" + +#: ports/nordic/common-hal/audiobusio/PDMIn.c +#: ports/stm/common-hal/audiobusio/PDMIn.c +msgid "only bit_depth=16 is supported" +msgstr "" + +#: ports/stm/common-hal/audiobusio/PDMIn.c +msgid "only mono is supported" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "only ndarrays can be concatenated" +msgstr "" + +#: ports/stm/common-hal/audiobusio/PDMIn.c +msgid "only oversample=64 is supported" +msgstr "" + +#: ports/nordic/common-hal/audiobusio/PDMIn.c +#: ports/stm/common-hal/audiobusio/PDMIn.c +msgid "only sample_rate=16000 is supported" +msgstr "" + +#: py/objarray.c py/objstr.c py/objstrunicode.c py/objtuple.c +#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c +#: shared-bindings/nvm/ByteArray.c +msgid "only slices with step=1 (aka None) are supported" +msgstr "" + +#: py/vm.c +msgid "opcode" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: expecting %q" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: must not be zero" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: out of range" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: undefined label '%q'" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: unknown register" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q': expecting %d arguments" +msgstr "" + +#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/bitwise.c +#: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/vector.c +msgid "operands could not be broadcast together" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "operation is defined for 2D arrays only" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "operation is defined for ndarrays only" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "operation is implemented for 1D Boolean arrays only" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "operation is not implemented on ndarrays" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "operation is not supported for given type" +msgstr "" + +#: extmod/ulab/code/ndarray_operators.c +msgid "operation not supported for the input types" +msgstr "" + +#: py/modbuiltins.c +msgid "ord expects a character" +msgstr "" + +#: py/modbuiltins.c +#, c-format +msgid "ord() expected a character, but string of length %d found" +msgstr "" + +#: extmod/ulab/code/utils/utils.c +msgid "out array is too small" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "out has wrong type" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "out keyword is not supported for complex dtype" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "out keyword is not supported for function" +msgstr "" + +#: extmod/ulab/code/utils/utils.c +msgid "out must be a float dense array" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "out must be an ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "out must be of float dtype" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "out of range of target" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "output array has wrong type" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "output array must be contiguous" +msgstr "" + +#: py/objint_mpz.c +msgid "overflow converting long int to machine word" +msgstr "" + +#: py/modstruct.c +#, c-format +msgid "pack expected %d items for packing (got %d)" +msgstr "" + +#: py/emitinlinerv32.c +msgid "parameters must be registers in sequence a0 to a3" +msgstr "" + +#: py/emitinlinextensa.c +msgid "parameters must be registers in sequence a2 to a5" +msgstr "" + +#: py/emitinlinethumb.c +msgid "parameters must be registers in sequence r0 to r3" +msgstr "" + +#: extmod/vfs_posix_file.c +msgid "poll on file not available on win32" +msgstr "" + +#: ports/espressif/common-hal/pulseio/PulseIn.c +msgid "pop from an empty PulseIn" +msgstr "" + +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +#: ports/cxd56/common-hal/pulseio/PulseIn.c +#: ports/nordic/common-hal/pulseio/PulseIn.c +#: ports/raspberrypi/common-hal/pulseio/PulseIn.c +#: ports/stm/common-hal/pulseio/PulseIn.c py/objdict.c py/objlist.c py/objset.c +#: shared-bindings/ps2io/Ps2.c +msgid "pop from empty %q" +msgstr "" + +#: shared-bindings/socketpool/Socket.c +msgid "port must be >= 0" +msgstr "" + +#: py/compile.c +msgid "positional arg after **" +msgstr "" + +#: py/compile.c +msgid "positional arg after keyword arg" +msgstr "" + +#: py/objint_mpz.c +msgid "pow() 3rd argument cannot be 0" +msgstr "" + +#: py/objint_mpz.c +msgid "pow() with 3 arguments requires integers" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "pull masks conflict with direction masks" +msgstr "" + +#: extmod/ulab/code/numpy/fft/fft_tools.c +msgid "real and imaginary parts must be of equal length" +msgstr "" + +#: py/builtinimport.c +msgid "relative import" +msgstr "" + +#: py/obj.c +#, c-format +msgid "requested length %d but object has length %d" +msgstr "" + +#: extmod/ulab/code/ndarray_operators.c +msgid "results cannot be cast to specified type" +msgstr "" + +#: py/compile.c +msgid "return annotation must be an identifier" +msgstr "" + +#: py/emitnative.c +msgid "return expected '%q' but got '%q'" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "rgb_pins[%d] duplicates another pin assignment" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "rgb_pins[%d] is not on the same port as clock" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "roll argument must be an ndarray" +msgstr "" + +#: py/objstr.c +msgid "rsplit(None,n)" +msgstr "" + +#: shared-bindings/audiofreeverb/Freeverb.c +msgid "samples_signed must be true" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c +msgid "sampling rate out of range" +msgstr "" + +#: py/modmicropython.c +msgid "schedule queue full" +msgstr "" + +#: py/builtinimport.c +msgid "script compilation not supported" +msgstr "" + +#: py/nativeglue.c +msgid "set unsupported" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "shape must be None, and integer or a tuple of integers" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "shape must be integer or tuple of integers" +msgstr "" + +#: shared-module/msgpack/__init__.c +msgid "short read" +msgstr "" + +#: py/objstr.c +msgid "sign not allowed in string format specifier" +msgstr "" + +#: py/objstr.c +msgid "sign not allowed with integer format specifier 'c'" +msgstr "" + +#: extmod/ulab/code/ulab_tools.c +msgid "size is defined for ndarrays only" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "size must match out.shape when used together" +msgstr "" + +#: py/nativeglue.c +msgid "slice unsupported" +msgstr "" + +#: py/objint.c py/sequence.c +msgid "small int overflow" +msgstr "" + +#: main.c +msgid "soft reboot\n" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "sort argument must be an ndarray" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "sos array must be of shape (n_section, 6)" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "sos[:, 3] should be all ones" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "sosfilt requires iterable arguments" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "source palette too large" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "source_bitmap must have value_count of 2 or 65536" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "source_bitmap must have value_count of 65536" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "source_bitmap must have value_count of 8" +msgstr "" + +#: extmod/modre.c +msgid "splitting with sub-captures" +msgstr "" + +#: shared-bindings/random/__init__.c +msgid "stop not reachable from start" +msgstr "" + +#: py/stream.c shared-bindings/getpass/__init__.c +msgid "stream operation not supported" +msgstr "" + +#: py/objarray.c py/objstr.c +msgid "string argument without an encoding" +msgstr "" + +#: py/objstrunicode.c +msgid "string index out of range" +msgstr "" + +#: py/objstrunicode.c +#, c-format +msgid "string indices must be integers, not %s" +msgstr "" + +#: py/objarray.c py/objstr.c +msgid "substring not found" +msgstr "" + +#: py/compile.c +msgid "super() can't find self" +msgstr "" + +#: extmod/modjson.c +msgid "syntax error in JSON" +msgstr "" + +#: extmod/modtime.c +msgid "ticks interval overflow" +msgstr "" + +#: ports/nordic/common-hal/watchdog/WatchDogTimer.c +msgid "timeout duration exceeded the maximum supported value" +msgstr "" + +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "timeout must be < 655.35 secs" +msgstr "" + +#: ports/raspberrypi/common-hal/floppyio/__init__.c +msgid "timeout waiting for flux" +msgstr "" + +#: ports/raspberrypi/common-hal/floppyio/__init__.c +#: shared-module/floppyio/__init__.c +msgid "timeout waiting for index pulse" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "timeout waiting for v1 card" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "timeout waiting for v2 card" +msgstr "" + +#: ports/stm/common-hal/pwmio/PWMOut.c +msgid "timer re-init" +msgstr "" + +#: shared-bindings/time/__init__.c +msgid "timestamp out of range for platform time_t" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "tobytes can be invoked for dense arrays only" +msgstr "" + +#: py/compile.c +msgid "too many args" +msgstr "" + +#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/create.c +msgid "too many dimensions" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "too many indices" +msgstr "" + +#: py/asmthumb.c +msgid "too many locals for native method" +msgstr "" + +#: py/runtime.c +#, c-format +msgid "too many values to unpack (expected %d)" +msgstr "" + +#: extmod/ulab/code/numpy/approx.c +msgid "trapz is defined for 1D arrays of equal length" +msgstr "" + +#: extmod/ulab/code/numpy/approx.c +msgid "trapz is defined for 1D iterables" +msgstr "" + +#: py/obj.c +msgid "tuple/list has wrong length" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +#, c-format +msgid "twai_driver_install returned esp-idf error #%d" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +#, c-format +msgid "twai_start returned esp-idf error #%d" +msgstr "" + +#: shared-bindings/busio/UART.c shared-bindings/canio/CAN.c +msgid "tx and rx cannot both be None" +msgstr "" + +#: py/objtype.c +msgid "type '%q' isn't an acceptable base type" +msgstr "" + +#: py/objtype.c +msgid "type isn't an acceptable base type" +msgstr "" + +#: py/runtime.c +msgid "type object '%q' has no attribute '%q'" +msgstr "" + +#: py/objtype.c +msgid "type takes 1 or 3 arguments" +msgstr "" + +#: py/objint_longlong.c +msgid "ulonglong too large" +msgstr "" + +#: py/parse.c +msgid "unexpected indent" +msgstr "" + +#: py/bc.c +msgid "unexpected keyword argument" +msgstr "" + +#: py/argcheck.c py/bc.c py/objnamedtuple.c +#: shared-bindings/traceback/__init__.c +msgid "unexpected keyword argument '%q'" +msgstr "" + +#: py/lexer.c +msgid "unicode name escapes" +msgstr "" + +#: py/parse.c +msgid "unindent doesn't match any outer indent level" +msgstr "" + +#: py/emitinlinerv32.c +msgid "unknown RV32 instruction '%q'" +msgstr "" + +#: py/objstr.c +#, c-format +msgid "unknown conversion specifier %c" +msgstr "" + +#: py/objstr.c +msgid "unknown format code '%c' for object of type '%q'" +msgstr "" + +#: py/compile.c +msgid "unknown type" +msgstr "" + +#: py/compile.c +msgid "unknown type '%q'" +msgstr "" + +#: py/objstr.c +#, c-format +msgid "unmatched '%c' in format" +msgstr "" + +#: py/objtype.c py/runtime.c +msgid "unreadable attribute" +msgstr "" + +#: shared-bindings/displayio/TileGrid.c shared-bindings/terminalio/Terminal.c +#: shared-bindings/tilepalettemapper/TilePaletteMapper.c +#: shared-bindings/vectorio/VectorShape.c +msgid "unsupported %q type" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "unsupported Thumb instruction '%s' with %d arguments" +msgstr "" + +#: py/emitinlinextensa.c +#, c-format +msgid "unsupported Xtensa instruction '%s' with %d arguments" +msgstr "" + +#: shared-module/bitmapfilter/__init__.c +msgid "unsupported bitmap depth" +msgstr "" + +#: shared-module/gifio/GifWriter.c +msgid "unsupported colorspace for GifWriter" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "unsupported colorspace for dither" +msgstr "" + +#: py/objstr.c +#, c-format +msgid "unsupported format character '%c' (0x%x) at index %d" +msgstr "" + +#: py/runtime.c +msgid "unsupported type for %q: '%s'" +msgstr "" + +#: py/runtime.c +msgid "unsupported type for operator" +msgstr "" + +#: py/runtime.c +msgid "unsupported types for %q: '%q', '%q'" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "usecols is too high" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "usecols keyword must be specified" +msgstr "" + +#: py/objint.c +#, c-format +msgid "value must fit in %d byte(s)" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "value out of range of target" +msgstr "" + +#: extmod/moddeflate.c +msgid "wbits" +msgstr "" + +#: shared-bindings/bitmapfilter/__init__.c +msgid "" +"weights must be a sequence with an odd square number of elements (usually 9 " +"or 25)" +msgstr "" + +#: shared-bindings/bitmapfilter/__init__.c +msgid "weights must be an object of type %q, %q, %q, or %q, not %q " +msgstr "" + +#: shared-bindings/is31fl3741/FrameBuffer.c +msgid "width must be greater than zero" +msgstr "" + +#: ports/raspberrypi/common-hal/wifi/Monitor.c +msgid "wifi.Monitor not available" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "window must be <= interval" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "wrong axis index" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "wrong axis specified" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "wrong dtype" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c +msgid "wrong index type" +msgstr "" + +#: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/io/io.c extmod/ulab/code/numpy/transform.c +#: extmod/ulab/code/numpy/vector.c +msgid "wrong input type" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c +msgid "wrong length of condition array" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c +msgid "wrong length of index array" +msgstr "" + +#: extmod/ulab/code/numpy/create.c py/objarray.c py/objstr.c +msgid "wrong number of arguments" +msgstr "" + +#: py/runtime.c +msgid "wrong number of values to unpack" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "wrong output type" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "zi must be an ndarray" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "zi must be of float type" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "zi must be of shape (n_section, 2)" +msgstr "" From 8bf51dbc821e051301a9a7057e6f2ea1a8a559fe Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 16:58:33 -0700 Subject: [PATCH 440/517] mtm_computer: set DAC gain 2x --- ports/raspberrypi/boards/mtm_computer/board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index 9065ffebcf831..666d584af2c81 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -20,7 +20,7 @@ static mp_obj_t board_dac_factory(void) { &pin_GPIO18, // clock (SCK) &pin_GPIO19, // mosi (SDI) &pin_GPIO21, // cs - 1); // gain 1x + 2); // gain 2x return MP_OBJ_FROM_PTR(dac); } MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); From bde1ee481ba54e15c462083c665518dec5977657 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 18:26:08 -0700 Subject: [PATCH 441/517] Revert "mtm_computer: set DAC gain 2x" This reverts commit 8bf51dbc821e051301a9a7057e6f2ea1a8a559fe. --- ports/raspberrypi/boards/mtm_computer/board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index 666d584af2c81..9065ffebcf831 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -20,7 +20,7 @@ static mp_obj_t board_dac_factory(void) { &pin_GPIO18, // clock (SCK) &pin_GPIO19, // mosi (SDI) &pin_GPIO21, // cs - 2); // gain 2x + 1); // gain 1x return MP_OBJ_FROM_PTR(dac); } MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); From 8529d44c8a1650f1cca9af07afad911977fcfd45 Mon Sep 17 00:00:00 2001 From: Alec Delaney <89490472+tekktrik@users.noreply.github.com> Date: Tue, 24 Mar 2026 12:50:23 -0400 Subject: [PATCH 442/517] Allow building and uploading native-sim executable --- ports/zephyr-cp/boards/native/native_sim/circuitpython.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml b/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml index 3272dd4c5f319..fbda7c563b835 100644 --- a/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml +++ b/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml @@ -1 +1 @@ -CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "exe"] From c2395d98770388ef8c6bfd9efe4e28448b18238a Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 09:51:01 -0700 Subject: [PATCH 443/517] fix zephyr builds by running update_boardnfo.py --- .../adafruit/feather_nrf52840_zephyr/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml | 1 + .../zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml | 1 + .../zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml | 1 + .../boards/renesas/da14695_dk_usb/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml | 1 + 17 files changed, 17 insertions(+) diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 9712f467858eb..277386cd1daf3 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 80b1b4ebf7d6a..dbf98f107686b 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 1bc1b96e6cf5c..06f944e3e90c9 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 7869cca4fafba..1e0cc856db166 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index c2233ddf8b544..a43e3169adabd 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index a1e8de8822b49..90203c4c0ecd1 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index b50b1966ed074..8f05a53587ecd 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 21d55194a1c1c..09f5cc6c58035 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index 90f84ab1586c4..e641099db7693 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index eb5db066c893c..d94b69eb81314 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index d6efa285fe2a4..0874538a40858 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 7600b8bbd151a..b8657eb040a69 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 8e49b95d33416..04e75b39eee0d 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index b28a9481c72d9..e881b8221900f 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 6b0ef8d8480f1..6bdc58b12afed 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index b6f03f3d627c6..ba745440b8dfc 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index 8d1fd9253488b..dd571f0283448 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false From f010f37f314188c441aeb3e0ea84a969b451f09b Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 24 Mar 2026 19:47:09 +0100 Subject: [PATCH 444/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 28 +++++++++------------------- locale/el.po | 28 +++++++++------------------- locale/hi.po | 28 +++++++++------------------- locale/ko.po | 28 +++++++++------------------- locale/ru.po | 37 ++++++++++++++++++------------------- locale/tr.po | 28 +++++++++------------------- 6 files changed, 63 insertions(+), 114 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index 759d65d876004..d23583f4e6b3e 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -124,6 +124,7 @@ msgstr "%q v %q musí být typu %q, ne %q" #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -146,6 +147,7 @@ msgstr "Indexy %q musí být celá čísla, nikoli %s" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "Inicializace %q selhala" @@ -177,8 +179,8 @@ msgstr "Délka %q musí být >= %d" msgid "%q must be %d" msgstr "%q musí být %d" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -470,6 +472,7 @@ msgstr "∗x musí být cíl přiřazení" msgid ", in %q\n" msgstr ", v% q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -660,6 +663,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "Baudrate není podporován periférií" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -682,6 +686,7 @@ msgstr "Bootovací zařízení musí být první (rozhraní #0)." msgid "Both RX and TX required for flow control" msgstr "RX a TX jsou vyžadovány pro kontrolu toku" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" @@ -873,7 +878,7 @@ msgstr "Pole souřadnic mají různé délky" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1040,10 +1045,6 @@ msgstr "Nepodařilo se alokovat paměť pro wifi scan" msgid "Failed to buffer the sample" msgstr "Nepodařilo se nabufferovat sample" -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c #: ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -1163,6 +1164,7 @@ msgstr "Inicializace GNSS" msgid "Generic Failure" msgstr "Základní chyba" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -3264,10 +3266,6 @@ msgstr "" msgid "float unsupported" msgstr "float není podporován" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3349,10 +3347,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "generátor způsobil StopIteration" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "hash je konečný" @@ -4074,10 +4068,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" diff --git a/locale/el.po b/locale/el.po index 872166169dbda..06a24a1486f9e 100644 --- a/locale/el.po +++ b/locale/el.po @@ -128,6 +128,7 @@ msgstr "%q στο %q πρέπει να είναι τύπου %q, όχι %q" #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -150,6 +151,7 @@ msgstr "%q δείκτες πρέπει να είναι ακέραιοι, όχι #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "%q εκκίνηση απέτυχε" @@ -181,8 +183,8 @@ msgstr "%q μήκος πρέπει να είναι >= %d" msgid "%q must be %d" msgstr "%q πρέπει να είναι %d" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -474,6 +476,7 @@ msgstr "*x πρέπει να είναι στόχος ανάθεσης" msgid ", in %q\n" msgstr ", στο %q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -664,6 +667,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "Baudrate δεν υποστηρίζεται από την περιφεριακή συσκευή" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -686,6 +690,7 @@ msgstr "Η συσκευή εκκίνησης πρέπει να επιλεχθε msgid "Both RX and TX required for flow control" msgstr "Και RX και TX απαιτούνται για έλεγχο flow" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" @@ -879,7 +884,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1048,10 +1053,6 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c #: ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -1169,6 +1170,7 @@ msgstr "" msgid "Generic Failure" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -3263,10 +3265,6 @@ msgstr "" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3348,10 +3346,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -4073,10 +4067,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index e0bb53003d7e4..a6d5cf49c0a06 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -115,6 +115,7 @@ msgstr "" #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -137,6 +138,7 @@ msgstr "" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "" @@ -168,8 +170,8 @@ msgstr "" msgid "%q must be %d" msgstr "" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -461,6 +463,7 @@ msgstr "" msgid ", in %q\n" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -649,6 +652,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -671,6 +675,7 @@ msgstr "" msgid "Both RX and TX required for flow control" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" @@ -858,7 +863,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1024,10 +1029,6 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c #: ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -1145,6 +1146,7 @@ msgstr "" msgid "Generic Failure" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -3237,10 +3239,6 @@ msgstr "" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3322,10 +3320,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -4047,10 +4041,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index 09f4776dfde25..cc65afaa7c060 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -126,6 +126,7 @@ msgstr "%q의 %q는 %q가 아니라 %q 유형이어야 합니다" #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -148,6 +149,7 @@ msgstr "%q 인덱스는 %s 가 아닌 정수 여야합니다" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "%q 초기화 실패" @@ -179,8 +181,8 @@ msgstr "%q 길이는 >= %d이어야 합니다" msgid "%q must be %d" msgstr "%q는 %d이어야 합니다" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -489,6 +491,7 @@ msgstr "*x는 할당 대상이어야 합니다" msgid ", in %q\n" msgstr ", 에서 %q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -690,6 +693,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "주변 기기에서 전송 속도가 지원되지 않습니다" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -712,6 +716,7 @@ msgstr "부팅 장치는 첫 번째(인터페이스 #0)여야 합니다." msgid "Both RX and TX required for flow control" msgstr "플로우 제어에 RX와 TX가 모두 필요합니다" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" @@ -902,7 +907,7 @@ msgstr "좌표 배열의 길이가 다릅니다" msgid "Coordinate arrays types have different sizes" msgstr "좌표 배열 유형은 크기가 다릅니다" -#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1072,10 +1077,6 @@ msgstr "wifi 검색 메모리 할당에 실패했습니다" msgid "Failed to buffer the sample" msgstr "샘플 버퍼링에 실패했습니다" -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c #: ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -1196,6 +1197,7 @@ msgstr "GNSS 초기화" msgid "Generic Failure" msgstr "일반 오류" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -3311,10 +3313,6 @@ msgstr "float이 너무 큽니다" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3396,10 +3394,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -4121,10 +4115,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" diff --git a/locale/ru.po b/locale/ru.po index 2c68943f6e45c..7f31c3a73a663 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -128,6 +128,7 @@ msgstr "%q в %q должно быть типа %q, а не %q" #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -150,6 +151,7 @@ msgstr "Индексы %q должны быть целыми числами, а #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "Инициализация %q не удалась" @@ -181,8 +183,8 @@ msgstr "Длинна %q должна быть >= %d" msgid "%q must be %d" msgstr "%q должно быть %d" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -474,6 +476,7 @@ msgstr "*x должно быть целью назначения" msgid ", in %q\n" msgstr ", в %q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -664,6 +667,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "Скорость передачи данных не поддерживается периферийным устройством" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -687,6 +691,7 @@ msgstr "Загрузочное устройство должно быть пер msgid "Both RX and TX required for flow control" msgstr "Для управления потоком требуется как RX так и TX" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" @@ -882,7 +887,7 @@ msgstr "Координатные массивы имеют разные длин msgid "Coordinate arrays types have different sizes" msgstr "Типы массивов координат имеют разные размеры" -#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1053,10 +1058,6 @@ msgstr "Не удалось выделить память для сканиро msgid "Failed to buffer the sample" msgstr "Не удалось выполнить буферизацию образца" -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c #: ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -1180,6 +1181,7 @@ msgstr "Инициализация GNSS" msgid "Generic Failure" msgstr "Общий сбой" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -3318,10 +3320,6 @@ msgstr "Поплавок слишком большой" msgid "float unsupported" msgstr "Плавающий без поддержки" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "Длина шрифта должна составлять 2048 байт" - #: extmod/moddeflate.c msgid "format" msgstr "формат" @@ -3403,10 +3401,6 @@ msgstr "генератор проигнорировал Выход" msgid "generator raised StopIteration" msgstr "генератор поднят Остановить итерацию" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "Длина рисунка должна составлять 2048 байт" - #: extmod/modhashlib.c msgid "hash is final" msgstr "хэш является окончательным" @@ -4136,10 +4130,6 @@ msgstr "переполнение преобразование длинного msgid "pack expected %d items for packing (got %d)" msgstr "Упаковка ожидаемых %d товаров для упаковки (получил %d)" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "Длина палитры должна составлять 32 байта" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" @@ -4671,6 +4661,15 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" +#~ msgid "font must be 2048 bytes long" +#~ msgstr "Длина шрифта должна составлять 2048 байт" + +#~ msgid "graphic must be 2048 bytes long" +#~ msgstr "Длина рисунка должна составлять 2048 байт" + +#~ msgid "palette must be 32 bytes long" +#~ msgstr "Длина палитры должна составлять 32 байта" + #, c-format #~ msgid "Buffer + offset too small %d %d %d" #~ msgstr "Буфер + сдвиг слишком малы %d %d %d" diff --git a/locale/tr.po b/locale/tr.po index d9d90eddfe87a..96037c5426fd2 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -126,6 +126,7 @@ msgstr "" #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -148,6 +149,7 @@ msgstr "%q indeksleri integer olmalı, %s değil" #: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c #: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "%q init başarısız oldu" @@ -179,8 +181,8 @@ msgstr "%q boyutu >= %d olmalıdır" msgid "%q must be %d" msgstr "%q, %d olmalıdır" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -472,6 +474,7 @@ msgstr "*x atama hedefi olmalıdır" msgid ", in %q\n" msgstr ", içinde %q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -662,6 +665,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "Baudhızı, çevre birimi tarafından desteklenmiyor" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -684,6 +688,7 @@ msgstr "" msgid "Both RX and TX required for flow control" msgstr "Hem RX hem de TX akış kontrolü için gerekli" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" @@ -872,7 +877,7 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" -#: shared-module/usb/core/Device.c ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c msgid "Could not allocate DMA capable buffer" msgstr "" @@ -1038,10 +1043,6 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c #: ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -1163,6 +1164,7 @@ msgstr "GNSS init" msgid "Generic Failure" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -3259,10 +3261,6 @@ msgstr "" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3344,10 +3342,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -4069,10 +4063,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" From 2de067b1c08d28a16d7dfae47f1de2efaf8dc990 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 14:46:04 -0700 Subject: [PATCH 445/517] mcp4822 gain argument fix, as per requested --- shared-bindings/mcp4822/MCP4822.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/shared-bindings/mcp4822/MCP4822.c b/shared-bindings/mcp4822/MCP4822.c index c48f5426e6690..f192caf4052a6 100644 --- a/shared-bindings/mcp4822/MCP4822.c +++ b/shared-bindings/mcp4822/MCP4822.c @@ -81,11 +81,7 @@ static mp_obj_t mcp4822_mcp4822_make_new(const mp_obj_type_t *type, size_t n_arg const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); - - mp_int_t gain = args[ARG_gain].u_int; - if (gain != 1 && gain != 2) { - mp_raise_ValueError(MP_ERROR_TEXT("gain must be 1 or 2")); - } + const mp_int_t gain = mp_arg_validate_int_range(args[ARG_gain].u_int, 1, 2, MP_QSTR_gain); mcp4822_mcp4822_obj_t *self = mp_obj_malloc_with_finaliser(mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); common_hal_mcp4822_mcp4822_construct(self, clock, mosi, cs, (uint8_t)gain); From ad21609394514b0227f84da8632e8dcf95a44c15 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 14:46:57 -0700 Subject: [PATCH 446/517] mcp4822 gain argument fix, as per requested --- locale/circuitpython.pot | 4 ---- 1 file changed, 4 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 5d0be48fe3a79..8bfc2d5bde31f 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -3310,10 +3310,6 @@ msgstr "" msgid "function takes %d positional arguments but %d were given" msgstr "" -#: shared-bindings/mcp4822/MCP4822.c -msgid "gain must be 1 or 2" -msgstr "" - #: py/objgenerator.c msgid "generator already executing" msgstr "" From dd1c9572ec2f780a518ed78486331c4989dc51d0 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 14:56:30 -0700 Subject: [PATCH 447/517] mtm_computer: make board.DAC() an actual singleton --- ports/raspberrypi/boards/mtm_computer/board.c | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index 9065ffebcf831..0b1e34eaacd00 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -12,16 +12,22 @@ // board.DAC() — factory function that constructs an mcp4822.MCP4822 with // the MTM Workshop Computer's DAC pins (GP18=SCK, GP19=SDI, GP21=CS). + +static mp_obj_t board_dac_singleton = MP_OBJ_NULL; + static mp_obj_t board_dac_factory(void) { - mcp4822_mcp4822_obj_t *dac = mp_obj_malloc_with_finaliser( - mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); - common_hal_mcp4822_mcp4822_construct( - dac, - &pin_GPIO18, // clock (SCK) - &pin_GPIO19, // mosi (SDI) - &pin_GPIO21, // cs - 1); // gain 1x - return MP_OBJ_FROM_PTR(dac); + if (board_dac_singleton == MP_OBJ_NULL) { + mcp4822_mcp4822_obj_t *dac = mp_obj_malloc_with_finaliser( + mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); + common_hal_mcp4822_mcp4822_construct( + dac, + &pin_GPIO18, // clock (SCK) + &pin_GPIO19, // mosi (SDI) + &pin_GPIO21, // cs + 1); // gain 1x + board_dac_singleton = MP_OBJ_FROM_PTR(dac); + } + return board_dac_singleton; } MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); From 5bd2a4233509830083f42008de8a10cb68ef39c3 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 15:21:02 -0700 Subject: [PATCH 448/517] mtm_computer: make sure board.DAC() gets deallocated on board deinit --- ports/raspberrypi/boards/mtm_computer/board.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index 0b1e34eaacd00..4fec0cd5b8b59 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -31,4 +31,10 @@ static mp_obj_t board_dac_factory(void) { } MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); +void board_deinit(void) { + if (board_dac_singleton != MP_OBJ_NULL) { + common_hal_mcp4822_mcp4822_deinit(MP_OBJ_TO_PTR(board_dac_singleton)); + board_dac_singleton = MP_OBJ_NULL; + } +} // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. From 13d6feac4dfcc58cec6e870bd0dfe1544084f0dd Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 15:21:28 -0700 Subject: [PATCH 449/517] mcp4822 CS/MOSI argument fix, as per requested --- ports/raspberrypi/common-hal/mcp4822/MCP4822.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/raspberrypi/common-hal/mcp4822/MCP4822.c b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c index cb6cddb8343ed..7a8ad7d4df29c 100644 --- a/ports/raspberrypi/common-hal/mcp4822/MCP4822.c +++ b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c @@ -143,8 +143,7 @@ void common_hal_mcp4822_mcp4822_construct(mcp4822_mcp4822_obj_t *self, // The SET pin group spans from MOSI to CS. // MOSI must have a lower GPIO number than CS, gap at most 4. if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { - mp_raise_ValueError( - MP_ERROR_TEXT("cs pin must be 1-4 positions above mosi pin")); + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q and %q"), MP_QSTR_CS, MP_QSTR_MOSI); } uint8_t set_count = cs->number - mosi->number + 1; From 62ffc25927d62a8ddbc3322e6e43fab71185ba08 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 15:21:55 -0700 Subject: [PATCH 450/517] mcp4822 CS/MOSI argument fix, as per requested --- locale/circuitpython.pot | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 8bfc2d5bde31f..57c0a13e991f7 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1297,6 +1297,7 @@ msgstr "" msgid "Invalid %q" msgstr "" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -3040,10 +3041,6 @@ msgstr "" msgid "cross is defined for 1D arrays of length 3" msgstr "" -#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c -msgid "cs pin must be 1-4 positions above mosi pin" -msgstr "" - #: extmod/ulab/code/scipy/optimize/optimize.c msgid "data must be iterable" msgstr "" From bec871e9c608a2364a639b100ae8051ae5f050c9 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Sat, 21 Mar 2026 10:13:02 -0700 Subject: [PATCH 451/517] mtm_computer: Add DAC audio out module --- .../boards/mtm_computer/module/DACOut.c | 276 ++++++++++++++++++ .../boards/mtm_computer/module/DACOut.h | 38 +++ .../boards/mtm_computer/mpconfigboard.mk | 7 + 3 files changed, 321 insertions(+) create mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.c create mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.h diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c new file mode 100644 index 0000000000000..84f4296cb00cd --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c @@ -0,0 +1,276 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT +// +// MCP4822 dual-channel 12-bit SPI DAC driver for the MTM Workshop Computer. +// Uses PIO + DMA for non-blocking audio playback, mirroring audiobusio.I2SOut. + +#include +#include + +#include "mpconfigport.h" + +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "boards/mtm_computer/module/DACOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/audiocore/__init__.h" +#include "bindings/rp2pio/StateMachine.h" + +// ───────────────────────────────────────────────────────────────────────────── +// PIO program for MCP4822 SPI DAC +// ───────────────────────────────────────────────────────────────────────────── +// +// Pin assignment: +// OUT pin (1) = MOSI — serial data out +// SET pins (N) = MOSI through CS — for CS control & command-bit injection +// SIDE-SET pin (1) = SCK — serial clock +// +// On the MTM Workshop Computer: MOSI=GP19, CS=GP21, SCK=GP18. +// The SET group spans GP19..GP21 (3 pins). GP20 is unused and driven low. +// +// SET PINS bit mapping (bit0=MOSI/GP19, bit1=GP20, bit2=CS/GP21): +// 0 = CS low, MOSI low 1 = CS low, MOSI high 4 = CS high, MOSI low +// +// SIDE-SET (1 pin, SCK): side 0 = SCK low, side 1 = SCK high +// +// MCP4822 16-bit command word: +// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x) +// [12] output enable (1) [11:0] 12-bit data +// +// DMA feeds unsigned 16-bit audio samples. RP2040 narrow-write replication +// fills both halves of the 32-bit PIO FIFO entry with the same value, +// giving mono→stereo for free. +// +// The PIO pulls 32 bits, then sends two SPI transactions: +// Channel A: cmd nibble 0b0011, then all 16 sample bits from upper half-word +// Channel B: cmd nibble 0b1011, then all 16 sample bits from lower half-word +// The MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data), +// so only the top 12 of the 16 sample bits become DAC data. The bottom +// 4 sample bits clock out harmlessly after the DAC has latched. +// This gives correct 16-bit → 12-bit scaling (effectively sample >> 4). +// +// PIO instruction encoding with .side_set 1 (no opt): +// [15:13] opcode [12] side-set [11:8] delay [7:0] operands +// +// Total: 26 instructions, 86 PIO clocks per audio sample. +// ───────────────────────────────────────────────────────────────────────────── + +static const uint16_t mcp4822_pio_program[] = { + // side SCK + // 0: pull noblock side 0 ; Get 32 bits or keep X if FIFO empty + 0x8080, + // 1: mov x, osr side 0 ; Save for pull-noblock fallback + 0xA027, + + // ── Channel A: command nibble 0b0011 ────────────────────────────────── + // Send 4 cmd bits via SET, then all 16 sample bits via OUT. + // MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data); + // the extra 4 clocks shift out the LSBs which the DAC ignores. + // This gives correct 16→12 bit scaling (top 12 bits become DAC data). + // 2: set pins, 0 side 0 ; CS low, MOSI=0 (bit15=0: channel A) + 0xE000, + // 3: nop side 1 ; SCK high — latch bit 15 + 0xB042, + // 4: set pins, 0 side 0 ; MOSI=0 (bit14=0: don't care) + 0xE000, + // 5: nop side 1 ; SCK high + 0xB042, + // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + 0xE001, + // 7: nop side 1 ; SCK high + 0xB042, + // 8: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) + 0xE001, + // 9: nop side 1 ; SCK high + 0xB042, + // 10: set y, 15 side 0 ; Loop counter: 16 sample bits + 0xE04F, + // 11: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopA) + 0x6001, + // 12: jmp y--, 11 side 1 ; SCK high, loop back + 0x108B, + // 13: set pins, 4 side 0 ; CS high — DAC A latches + 0xE004, + + // ── Channel B: command nibble 0b1011 ────────────────────────────────── + // 14: set pins, 1 side 0 ; CS low, MOSI=1 (bit15=1: channel B) + 0xE001, + // 15: nop side 1 ; SCK high + 0xB042, + // 16: set pins, 0 side 0 ; MOSI=0 (bit14=0) + 0xE000, + // 17: nop side 1 ; SCK high + 0xB042, + // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) + 0xE001, + // 19: nop side 1 ; SCK high + 0xB042, + // 20: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) + 0xE001, + // 21: nop side 1 ; SCK high + 0xB042, + // 22: set y, 15 side 0 ; Loop counter: 16 sample bits + 0xE04F, + // 23: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopB) + 0x6001, + // 24: jmp y--, 23 side 1 ; SCK high, loop back + 0x1097, + // 25: set pins, 4 side 0 ; CS high — DAC B latches + 0xE004, +}; + +// Clocks per sample: 2 (pull+mov) + 42 (chanA) + 42 (chanB) = 86 +// Per channel: 8(4 cmd bits × 2 clks) + 1(set y) + 32(16 bits × 2 clks) + 1(cs high) = 42 +#define MCP4822_CLOCKS_PER_SAMPLE 86 + + +void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs) { + + // SET pins span from MOSI to CS. MOSI must have a lower GPIO number + // than CS, with at most 4 pins between them (SET count max is 5). + if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { + mp_raise_ValueError( + MP_COMPRESSED_ROM_TEXT("cs pin must be 1-4 positions above mosi pin")); + } + + uint8_t set_count = cs->number - mosi->number + 1; + + // Initial SET pin state: CS high (bit at CS position), others low + uint32_t cs_bit_position = cs->number - mosi->number; + pio_pinmask32_t initial_set_state = PIO_PINMASK32_FROM_VALUE(1u << cs_bit_position); + pio_pinmask32_t initial_set_dir = PIO_PINMASK32_FROM_VALUE((1u << set_count) - 1); + + common_hal_rp2pio_statemachine_construct( + &self->state_machine, + mcp4822_pio_program, MP_ARRAY_SIZE(mcp4822_pio_program), + 44100 * MCP4822_CLOCKS_PER_SAMPLE, // Initial frequency; play() adjusts + NULL, 0, // No init program + NULL, 0, // No may_exec + mosi, 1, // OUT: MOSI, 1 pin + PIO_PINMASK32_NONE, PIO_PINMASK32_ALL, // OUT state=low, dir=output + NULL, 0, // IN: none + PIO_PINMASK32_NONE, PIO_PINMASK32_NONE, // IN pulls: none + mosi, set_count, // SET: MOSI..CS + initial_set_state, initial_set_dir, // SET state (CS high), dir=output + clock, 1, false, // SIDE-SET: SCK, 1 pin, not pindirs + PIO_PINMASK32_NONE, // SIDE-SET state: SCK low + PIO_PINMASK32_FROM_VALUE(0x1), // SIDE-SET dir: output + false, // No sideset enable + NULL, PULL_NONE, // No jump pin + PIO_PINMASK_NONE, // No wait GPIO + true, // Exclusive pin use + false, 32, false, // OUT shift: no autopull, 32-bit, shift left + false, // Don't wait for txstall + false, 32, false, // IN shift (unused) + false, // Not user-interruptible + 0, -1, // Wrap: whole program + PIO_ANY_OFFSET, + PIO_FIFO_TYPE_DEFAULT, + PIO_MOV_STATUS_DEFAULT, + PIO_MOV_N_DEFAULT + ); + + self->playing = false; + audio_dma_init(&self->dma); +} + +bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self) { + return common_hal_rp2pio_statemachine_deinited(&self->state_machine); +} + +void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self) { + if (common_hal_mtm_hardware_dacout_deinited(self)) { + return; + } + if (common_hal_mtm_hardware_dacout_get_playing(self)) { + common_hal_mtm_hardware_dacout_stop(self); + } + common_hal_rp2pio_statemachine_deinit(&self->state_machine); + audio_dma_deinit(&self->dma); +} + +void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, + mp_obj_t sample, bool loop) { + + if (common_hal_mtm_hardware_dacout_get_playing(self)) { + common_hal_mtm_hardware_dacout_stop(self); + } + + uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); + if (bits_per_sample < 16) { + bits_per_sample = 16; + } + + uint32_t sample_rate = audiosample_get_sample_rate(sample); + uint8_t channel_count = audiosample_get_channel_count(sample); + if (channel_count > 2) { + mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("Too many channels in sample.")); + } + + // PIO clock = sample_rate × clocks_per_sample + common_hal_rp2pio_statemachine_set_frequency( + &self->state_machine, + (uint32_t)sample_rate * MCP4822_CLOCKS_PER_SAMPLE); + common_hal_rp2pio_statemachine_restart(&self->state_machine); + + // DMA feeds unsigned 16-bit samples. The PIO discards the top 4 bits + // of each 16-bit half and uses the remaining 12 as DAC data. + // RP2040 narrow-write replication: 16-bit DMA write → same value in + // both 32-bit FIFO halves → mono-to-stereo for free. + audio_dma_result result = audio_dma_setup_playback( + &self->dma, + sample, + loop, + false, // single_channel_output + 0, // audio_channel + false, // output_signed = false (unsigned for MCP4822) + bits_per_sample, // output_resolution + (uint32_t)&self->state_machine.pio->txf[self->state_machine.state_machine], + self->state_machine.tx_dreq, + false); // swap_channel + + if (result == AUDIO_DMA_DMA_BUSY) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("No DMA channel found")); + } else if (result == AUDIO_DMA_MEMORY_ERROR) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Unable to allocate buffers for signed conversion")); + } else if (result == AUDIO_DMA_SOURCE_ERROR) { + common_hal_mtm_hardware_dacout_stop(self); + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Audio source error")); + } + + self->playing = true; +} + +void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self) { + audio_dma_pause(&self->dma); +} + +void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self) { + audio_dma_resume(&self->dma); +} + +bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self) { + return audio_dma_get_paused(&self->dma); +} + +void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self) { + audio_dma_stop(&self->dma); + common_hal_rp2pio_statemachine_stop(&self->state_machine); + self->playing = false; +} + +bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self) { + bool playing = audio_dma_get_playing(&self->dma); + if (!playing && self->playing) { + common_hal_mtm_hardware_dacout_stop(self); + } + return playing; +} diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h new file mode 100644 index 0000000000000..f7c0c9eb8062c --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h @@ -0,0 +1,38 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/rp2pio/StateMachine.h" + +#include "audio_dma.h" +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + rp2pio_statemachine_obj_t state_machine; + audio_dma_t dma; + bool playing; +} mtm_hardware_dacout_obj_t; + +void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs); + +void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); + +void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, + mp_obj_t sample, bool loop); +void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self); + +void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self); +void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self); +bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self); + +extern const mp_obj_type_t mtm_hardware_dacout_type; diff --git a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk index 45c99711d72a3..0383e0777faff 100644 --- a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk +++ b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk @@ -11,4 +11,11 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY_AUDIOEFFECTS = 1 CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_PICODVI = 0 +<<<<<<< HEAD CIRCUITPY_MCP4822 = 1 +======= + +SRC_C += \ + boards/$(BOARD)/module/mtm_hardware.c \ + boards/$(BOARD)/module/DACOut.c +>>>>>>> d8bbe2a87f (mtm_computer: Add DAC audio out module) From 7e66d51d93b43a12e7b32ca2ba76d522256384f3 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Sat, 21 Mar 2026 10:33:40 -0700 Subject: [PATCH 452/517] mtm_hardware.c added --- .../boards/mtm_computer/module/mtm_hardware.c | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c new file mode 100644 index 0000000000000..a3dafbcf9415a --- /dev/null +++ b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c @@ -0,0 +1,267 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT +// +// Python bindings for the mtm_hardware module. +// Provides DACOut: a non-blocking audio player for the MCP4822 SPI DAC. + +#include + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "boards/mtm_computer/module/DACOut.h" + +// ───────────────────────────────────────────────────────────────────────────── +// DACOut class +// ───────────────────────────────────────────────────────────────────────────── + +//| class DACOut: +//| """Output audio to the MCP4822 dual-channel 12-bit SPI DAC.""" +//| +//| def __init__( +//| self, +//| clock: microcontroller.Pin, +//| mosi: microcontroller.Pin, +//| cs: microcontroller.Pin, +//| ) -> None: +//| """Create a DACOut object associated with the given SPI pins. +//| +//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin +//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin +//| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| +//| Simple 8ksps 440 Hz sine wave:: +//| +//| import mtm_hardware +//| import audiocore +//| import board +//| import array +//| import time +//| import math +//| +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import mtm_hardware +//| +//| f = open("sound.wav", "rb") +//| wav = audiocore.WaveFile(f) +//| +//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(wav) +//| while dac.playing: +//| pass""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_clock, ARG_mosi, ARG_cs }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + + mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); + common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs); + + return MP_OBJ_FROM_PTR(self); +} + +static void check_for_deinit(mtm_hardware_dacout_obj_t *self) { + if (common_hal_mtm_hardware_dacout_deinited(self)) { + raise_deinited_error(); + } +} + +//| def deinit(self) -> None: +//| """Deinitialises the DACOut and releases any hardware resources for reuse.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_deinit(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_mtm_hardware_dacout_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_deinit_obj, mtm_hardware_dacout_deinit); + +//| def __enter__(self) -> DACOut: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +// Provided by context manager helper. + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 8 bit or 16 bit samples.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_mtm_hardware_dacout_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mtm_hardware_dacout_play_obj, 1, mtm_hardware_dacout_obj_play); + +//| def stop(self) -> None: +//| """Stops playback.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_stop(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_mtm_hardware_dacout_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_stop_obj, mtm_hardware_dacout_obj_stop); + +//| playing: bool +//| """True when the audio sample is being output. (read-only)""" +//| +static mp_obj_t mtm_hardware_dacout_obj_get_playing(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_playing_obj, mtm_hardware_dacout_obj_get_playing); + +MP_PROPERTY_GETTER(mtm_hardware_dacout_playing_obj, + (mp_obj_t)&mtm_hardware_dacout_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_pause(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (!common_hal_mtm_hardware_dacout_get_playing(self)) { + mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Not playing")); + } + common_hal_mtm_hardware_dacout_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_pause_obj, mtm_hardware_dacout_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +static mp_obj_t mtm_hardware_dacout_obj_resume(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_mtm_hardware_dacout_get_paused(self)) { + common_hal_mtm_hardware_dacout_resume(self); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_resume_obj, mtm_hardware_dacout_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +static mp_obj_t mtm_hardware_dacout_obj_get_paused(mp_obj_t self_in) { + mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_paused_obj, mtm_hardware_dacout_obj_get_paused); + +MP_PROPERTY_GETTER(mtm_hardware_dacout_paused_obj, + (mp_obj_t)&mtm_hardware_dacout_get_paused_obj); + +// ── DACOut type definition ─────────────────────────────────────────────────── + +static const mp_rom_map_elem_t mtm_hardware_dacout_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mtm_hardware_dacout_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mtm_hardware_dacout_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mtm_hardware_dacout_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mtm_hardware_dacout_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mtm_hardware_dacout_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mtm_hardware_dacout_paused_obj) }, +}; +static MP_DEFINE_CONST_DICT(mtm_hardware_dacout_locals_dict, mtm_hardware_dacout_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mtm_hardware_dacout_type, + MP_QSTR_DACOut, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, mtm_hardware_dacout_make_new, + locals_dict, &mtm_hardware_dacout_locals_dict + ); + +// ───────────────────────────────────────────────────────────────────────────── +// mtm_hardware module definition +// ───────────────────────────────────────────────────────────────────────────── + +//| """Hardware interface to Music Thing Modular Workshop Computer peripherals. +//| +//| Provides the `DACOut` class for non-blocking audio output via the +//| MCP4822 dual-channel 12-bit SPI DAC. +//| """ + +static const mp_rom_map_elem_t mtm_hardware_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mtm_hardware) }, + { MP_ROM_QSTR(MP_QSTR_DACOut), MP_ROM_PTR(&mtm_hardware_dacout_type) }, +}; + +static MP_DEFINE_CONST_DICT(mtm_hardware_module_globals, mtm_hardware_module_globals_table); + +const mp_obj_module_t mtm_hardware_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mtm_hardware_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mtm_hardware, mtm_hardware_module); From eb3fd7080b8651156427649fd4975f876083073d Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 12:58:15 -0700 Subject: [PATCH 453/517] mtm_hardware.dacout: add gain=1 or gain=2 argument --- .../boards/mtm_computer/module/DACOut.c | 21 +++++++++++++++++-- .../boards/mtm_computer/module/DACOut.h | 2 +- .../boards/mtm_computer/module/mtm_hardware.c | 13 ++++++++++-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c index 84f4296cb00cd..fb4ce37d4d0ff 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c @@ -128,9 +128,19 @@ static const uint16_t mcp4822_pio_program[] = { #define MCP4822_CLOCKS_PER_SAMPLE 86 +// MCP4822 gain bit (bit 13) position in the PIO program: +// Instruction 6 = channel A gain bit +// Instruction 18 = channel B gain bit +// 1x gain: set pins, 1 (0xE001) — bit 13 = 1 +// 2x gain: set pins, 0 (0xE000) — bit 13 = 0 +#define MCP4822_PIO_GAIN_INSTR_A 6 +#define MCP4822_PIO_GAIN_INSTR_B 18 +#define MCP4822_PIO_GAIN_1X 0xE001 // set pins, 1 +#define MCP4822_PIO_GAIN_2X 0xE000 // set pins, 0 + void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs) { + const mcu_pin_obj_t *cs, uint8_t gain) { // SET pins span from MOSI to CS. MOSI must have a lower GPIO number // than CS, with at most 4 pins between them (SET count max is 5). @@ -141,6 +151,13 @@ void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, uint8_t set_count = cs->number - mosi->number + 1; + // Build a mutable copy of the PIO program and patch the gain bit + uint16_t program[MP_ARRAY_SIZE(mcp4822_pio_program)]; + memcpy(program, mcp4822_pio_program, sizeof(mcp4822_pio_program)); + uint16_t gain_instr = (gain == 2) ? MCP4822_PIO_GAIN_2X : MCP4822_PIO_GAIN_1X; + program[MCP4822_PIO_GAIN_INSTR_A] = gain_instr; + program[MCP4822_PIO_GAIN_INSTR_B] = gain_instr; + // Initial SET pin state: CS high (bit at CS position), others low uint32_t cs_bit_position = cs->number - mosi->number; pio_pinmask32_t initial_set_state = PIO_PINMASK32_FROM_VALUE(1u << cs_bit_position); @@ -148,7 +165,7 @@ void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, common_hal_rp2pio_statemachine_construct( &self->state_machine, - mcp4822_pio_program, MP_ARRAY_SIZE(mcp4822_pio_program), + program, MP_ARRAY_SIZE(mcp4822_pio_program), 44100 * MCP4822_CLOCKS_PER_SAMPLE, // Initial frequency; play() adjusts NULL, 0, // No init program NULL, 0, // No may_exec diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h index f7c0c9eb8062c..f9b5dd60108cf 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h +++ b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h @@ -21,7 +21,7 @@ typedef struct { void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs); + const mcu_pin_obj_t *cs, uint8_t gain); void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c index a3dafbcf9415a..8f875496f6745 100644 --- a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c +++ b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c @@ -29,12 +29,15 @@ //| clock: microcontroller.Pin, //| mosi: microcontroller.Pin, //| cs: microcontroller.Pin, +//| *, +//| gain: int = 1, //| ) -> None: //| """Create a DACOut object associated with the given SPI pins. //| //| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin //| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin //| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. //| //| Simple 8ksps 440 Hz sine wave:: //| @@ -72,11 +75,12 @@ //| ... //| static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_clock, ARG_mosi, ARG_cs }; + enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); @@ -85,8 +89,13 @@ static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + mp_int_t gain = args[ARG_gain].u_int; + if (gain != 1 && gain != 2) { + mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("gain must be 1 or 2")); + } + mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); - common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs); + common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs, (uint8_t)gain); return MP_OBJ_FROM_PTR(self); } From 8d282d59d8dac1193b0d586e5519b2da86b31c36 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 16:42:55 -0700 Subject: [PATCH 454/517] rework mcp4822 module from mtm_hardware.DACOut --- .../boards/mtm_computer/module/DACOut.c | 293 ------------------ .../boards/mtm_computer/module/DACOut.h | 38 --- .../boards/mtm_computer/module/mtm_hardware.c | 276 ----------------- .../boards/mtm_computer/mpconfigboard.mk | 7 - 4 files changed, 614 deletions(-) delete mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.c delete mode 100644 ports/raspberrypi/boards/mtm_computer/module/DACOut.h delete mode 100644 ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c b/ports/raspberrypi/boards/mtm_computer/module/DACOut.c deleted file mode 100644 index fb4ce37d4d0ff..0000000000000 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.c +++ /dev/null @@ -1,293 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT -// -// MCP4822 dual-channel 12-bit SPI DAC driver for the MTM Workshop Computer. -// Uses PIO + DMA for non-blocking audio playback, mirroring audiobusio.I2SOut. - -#include -#include - -#include "mpconfigport.h" - -#include "py/gc.h" -#include "py/mperrno.h" -#include "py/runtime.h" -#include "boards/mtm_computer/module/DACOut.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-module/audiocore/__init__.h" -#include "bindings/rp2pio/StateMachine.h" - -// ───────────────────────────────────────────────────────────────────────────── -// PIO program for MCP4822 SPI DAC -// ───────────────────────────────────────────────────────────────────────────── -// -// Pin assignment: -// OUT pin (1) = MOSI — serial data out -// SET pins (N) = MOSI through CS — for CS control & command-bit injection -// SIDE-SET pin (1) = SCK — serial clock -// -// On the MTM Workshop Computer: MOSI=GP19, CS=GP21, SCK=GP18. -// The SET group spans GP19..GP21 (3 pins). GP20 is unused and driven low. -// -// SET PINS bit mapping (bit0=MOSI/GP19, bit1=GP20, bit2=CS/GP21): -// 0 = CS low, MOSI low 1 = CS low, MOSI high 4 = CS high, MOSI low -// -// SIDE-SET (1 pin, SCK): side 0 = SCK low, side 1 = SCK high -// -// MCP4822 16-bit command word: -// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x) -// [12] output enable (1) [11:0] 12-bit data -// -// DMA feeds unsigned 16-bit audio samples. RP2040 narrow-write replication -// fills both halves of the 32-bit PIO FIFO entry with the same value, -// giving mono→stereo for free. -// -// The PIO pulls 32 bits, then sends two SPI transactions: -// Channel A: cmd nibble 0b0011, then all 16 sample bits from upper half-word -// Channel B: cmd nibble 0b1011, then all 16 sample bits from lower half-word -// The MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data), -// so only the top 12 of the 16 sample bits become DAC data. The bottom -// 4 sample bits clock out harmlessly after the DAC has latched. -// This gives correct 16-bit → 12-bit scaling (effectively sample >> 4). -// -// PIO instruction encoding with .side_set 1 (no opt): -// [15:13] opcode [12] side-set [11:8] delay [7:0] operands -// -// Total: 26 instructions, 86 PIO clocks per audio sample. -// ───────────────────────────────────────────────────────────────────────────── - -static const uint16_t mcp4822_pio_program[] = { - // side SCK - // 0: pull noblock side 0 ; Get 32 bits or keep X if FIFO empty - 0x8080, - // 1: mov x, osr side 0 ; Save for pull-noblock fallback - 0xA027, - - // ── Channel A: command nibble 0b0011 ────────────────────────────────── - // Send 4 cmd bits via SET, then all 16 sample bits via OUT. - // MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data); - // the extra 4 clocks shift out the LSBs which the DAC ignores. - // This gives correct 16→12 bit scaling (top 12 bits become DAC data). - // 2: set pins, 0 side 0 ; CS low, MOSI=0 (bit15=0: channel A) - 0xE000, - // 3: nop side 1 ; SCK high — latch bit 15 - 0xB042, - // 4: set pins, 0 side 0 ; MOSI=0 (bit14=0: don't care) - 0xE000, - // 5: nop side 1 ; SCK high - 0xB042, - // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) - 0xE001, - // 7: nop side 1 ; SCK high - 0xB042, - // 8: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) - 0xE001, - // 9: nop side 1 ; SCK high - 0xB042, - // 10: set y, 15 side 0 ; Loop counter: 16 sample bits - 0xE04F, - // 11: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopA) - 0x6001, - // 12: jmp y--, 11 side 1 ; SCK high, loop back - 0x108B, - // 13: set pins, 4 side 0 ; CS high — DAC A latches - 0xE004, - - // ── Channel B: command nibble 0b1011 ────────────────────────────────── - // 14: set pins, 1 side 0 ; CS low, MOSI=1 (bit15=1: channel B) - 0xE001, - // 15: nop side 1 ; SCK high - 0xB042, - // 16: set pins, 0 side 0 ; MOSI=0 (bit14=0) - 0xE000, - // 17: nop side 1 ; SCK high - 0xB042, - // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) - 0xE001, - // 19: nop side 1 ; SCK high - 0xB042, - // 20: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) - 0xE001, - // 21: nop side 1 ; SCK high - 0xB042, - // 22: set y, 15 side 0 ; Loop counter: 16 sample bits - 0xE04F, - // 23: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopB) - 0x6001, - // 24: jmp y--, 23 side 1 ; SCK high, loop back - 0x1097, - // 25: set pins, 4 side 0 ; CS high — DAC B latches - 0xE004, -}; - -// Clocks per sample: 2 (pull+mov) + 42 (chanA) + 42 (chanB) = 86 -// Per channel: 8(4 cmd bits × 2 clks) + 1(set y) + 32(16 bits × 2 clks) + 1(cs high) = 42 -#define MCP4822_CLOCKS_PER_SAMPLE 86 - - -// MCP4822 gain bit (bit 13) position in the PIO program: -// Instruction 6 = channel A gain bit -// Instruction 18 = channel B gain bit -// 1x gain: set pins, 1 (0xE001) — bit 13 = 1 -// 2x gain: set pins, 0 (0xE000) — bit 13 = 0 -#define MCP4822_PIO_GAIN_INSTR_A 6 -#define MCP4822_PIO_GAIN_INSTR_B 18 -#define MCP4822_PIO_GAIN_1X 0xE001 // set pins, 1 -#define MCP4822_PIO_GAIN_2X 0xE000 // set pins, 0 - -void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, - const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs, uint8_t gain) { - - // SET pins span from MOSI to CS. MOSI must have a lower GPIO number - // than CS, with at most 4 pins between them (SET count max is 5). - if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { - mp_raise_ValueError( - MP_COMPRESSED_ROM_TEXT("cs pin must be 1-4 positions above mosi pin")); - } - - uint8_t set_count = cs->number - mosi->number + 1; - - // Build a mutable copy of the PIO program and patch the gain bit - uint16_t program[MP_ARRAY_SIZE(mcp4822_pio_program)]; - memcpy(program, mcp4822_pio_program, sizeof(mcp4822_pio_program)); - uint16_t gain_instr = (gain == 2) ? MCP4822_PIO_GAIN_2X : MCP4822_PIO_GAIN_1X; - program[MCP4822_PIO_GAIN_INSTR_A] = gain_instr; - program[MCP4822_PIO_GAIN_INSTR_B] = gain_instr; - - // Initial SET pin state: CS high (bit at CS position), others low - uint32_t cs_bit_position = cs->number - mosi->number; - pio_pinmask32_t initial_set_state = PIO_PINMASK32_FROM_VALUE(1u << cs_bit_position); - pio_pinmask32_t initial_set_dir = PIO_PINMASK32_FROM_VALUE((1u << set_count) - 1); - - common_hal_rp2pio_statemachine_construct( - &self->state_machine, - program, MP_ARRAY_SIZE(mcp4822_pio_program), - 44100 * MCP4822_CLOCKS_PER_SAMPLE, // Initial frequency; play() adjusts - NULL, 0, // No init program - NULL, 0, // No may_exec - mosi, 1, // OUT: MOSI, 1 pin - PIO_PINMASK32_NONE, PIO_PINMASK32_ALL, // OUT state=low, dir=output - NULL, 0, // IN: none - PIO_PINMASK32_NONE, PIO_PINMASK32_NONE, // IN pulls: none - mosi, set_count, // SET: MOSI..CS - initial_set_state, initial_set_dir, // SET state (CS high), dir=output - clock, 1, false, // SIDE-SET: SCK, 1 pin, not pindirs - PIO_PINMASK32_NONE, // SIDE-SET state: SCK low - PIO_PINMASK32_FROM_VALUE(0x1), // SIDE-SET dir: output - false, // No sideset enable - NULL, PULL_NONE, // No jump pin - PIO_PINMASK_NONE, // No wait GPIO - true, // Exclusive pin use - false, 32, false, // OUT shift: no autopull, 32-bit, shift left - false, // Don't wait for txstall - false, 32, false, // IN shift (unused) - false, // Not user-interruptible - 0, -1, // Wrap: whole program - PIO_ANY_OFFSET, - PIO_FIFO_TYPE_DEFAULT, - PIO_MOV_STATUS_DEFAULT, - PIO_MOV_N_DEFAULT - ); - - self->playing = false; - audio_dma_init(&self->dma); -} - -bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self) { - return common_hal_rp2pio_statemachine_deinited(&self->state_machine); -} - -void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self) { - if (common_hal_mtm_hardware_dacout_deinited(self)) { - return; - } - if (common_hal_mtm_hardware_dacout_get_playing(self)) { - common_hal_mtm_hardware_dacout_stop(self); - } - common_hal_rp2pio_statemachine_deinit(&self->state_machine); - audio_dma_deinit(&self->dma); -} - -void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, - mp_obj_t sample, bool loop) { - - if (common_hal_mtm_hardware_dacout_get_playing(self)) { - common_hal_mtm_hardware_dacout_stop(self); - } - - uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); - if (bits_per_sample < 16) { - bits_per_sample = 16; - } - - uint32_t sample_rate = audiosample_get_sample_rate(sample); - uint8_t channel_count = audiosample_get_channel_count(sample); - if (channel_count > 2) { - mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("Too many channels in sample.")); - } - - // PIO clock = sample_rate × clocks_per_sample - common_hal_rp2pio_statemachine_set_frequency( - &self->state_machine, - (uint32_t)sample_rate * MCP4822_CLOCKS_PER_SAMPLE); - common_hal_rp2pio_statemachine_restart(&self->state_machine); - - // DMA feeds unsigned 16-bit samples. The PIO discards the top 4 bits - // of each 16-bit half and uses the remaining 12 as DAC data. - // RP2040 narrow-write replication: 16-bit DMA write → same value in - // both 32-bit FIFO halves → mono-to-stereo for free. - audio_dma_result result = audio_dma_setup_playback( - &self->dma, - sample, - loop, - false, // single_channel_output - 0, // audio_channel - false, // output_signed = false (unsigned for MCP4822) - bits_per_sample, // output_resolution - (uint32_t)&self->state_machine.pio->txf[self->state_machine.state_machine], - self->state_machine.tx_dreq, - false); // swap_channel - - if (result == AUDIO_DMA_DMA_BUSY) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("No DMA channel found")); - } else if (result == AUDIO_DMA_MEMORY_ERROR) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Unable to allocate buffers for signed conversion")); - } else if (result == AUDIO_DMA_SOURCE_ERROR) { - common_hal_mtm_hardware_dacout_stop(self); - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Audio source error")); - } - - self->playing = true; -} - -void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self) { - audio_dma_pause(&self->dma); -} - -void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self) { - audio_dma_resume(&self->dma); -} - -bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self) { - return audio_dma_get_paused(&self->dma); -} - -void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self) { - audio_dma_stop(&self->dma); - common_hal_rp2pio_statemachine_stop(&self->state_machine); - self->playing = false; -} - -bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self) { - bool playing = audio_dma_get_playing(&self->dma); - if (!playing && self->playing) { - common_hal_mtm_hardware_dacout_stop(self); - } - return playing; -} diff --git a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h b/ports/raspberrypi/boards/mtm_computer/module/DACOut.h deleted file mode 100644 index f9b5dd60108cf..0000000000000 --- a/ports/raspberrypi/boards/mtm_computer/module/DACOut.h +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "common-hal/microcontroller/Pin.h" -#include "common-hal/rp2pio/StateMachine.h" - -#include "audio_dma.h" -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; - rp2pio_statemachine_obj_t state_machine; - audio_dma_t dma; - bool playing; -} mtm_hardware_dacout_obj_t; - -void common_hal_mtm_hardware_dacout_construct(mtm_hardware_dacout_obj_t *self, - const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *cs, uint8_t gain); - -void common_hal_mtm_hardware_dacout_deinit(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_deinited(mtm_hardware_dacout_obj_t *self); - -void common_hal_mtm_hardware_dacout_play(mtm_hardware_dacout_obj_t *self, - mp_obj_t sample, bool loop); -void common_hal_mtm_hardware_dacout_stop(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_get_playing(mtm_hardware_dacout_obj_t *self); - -void common_hal_mtm_hardware_dacout_pause(mtm_hardware_dacout_obj_t *self); -void common_hal_mtm_hardware_dacout_resume(mtm_hardware_dacout_obj_t *self); -bool common_hal_mtm_hardware_dacout_get_paused(mtm_hardware_dacout_obj_t *self); - -extern const mp_obj_type_t mtm_hardware_dacout_type; diff --git a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c b/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c deleted file mode 100644 index 8f875496f6745..0000000000000 --- a/ports/raspberrypi/boards/mtm_computer/module/mtm_hardware.c +++ /dev/null @@ -1,276 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT -// -// Python bindings for the mtm_hardware module. -// Provides DACOut: a non-blocking audio player for the MCP4822 SPI DAC. - -#include - -#include "shared/runtime/context_manager_helpers.h" -#include "py/binary.h" -#include "py/objproperty.h" -#include "py/runtime.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/util.h" -#include "boards/mtm_computer/module/DACOut.h" - -// ───────────────────────────────────────────────────────────────────────────── -// DACOut class -// ───────────────────────────────────────────────────────────────────────────── - -//| class DACOut: -//| """Output audio to the MCP4822 dual-channel 12-bit SPI DAC.""" -//| -//| def __init__( -//| self, -//| clock: microcontroller.Pin, -//| mosi: microcontroller.Pin, -//| cs: microcontroller.Pin, -//| *, -//| gain: int = 1, -//| ) -> None: -//| """Create a DACOut object associated with the given SPI pins. -//| -//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin -//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin -//| :param ~microcontroller.Pin cs: The chip select (CS) pin -//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. -//| -//| Simple 8ksps 440 Hz sine wave:: -//| -//| import mtm_hardware -//| import audiocore -//| import board -//| import array -//| import time -//| import math -//| -//| length = 8000 // 440 -//| sine_wave = array.array("H", [0] * length) -//| for i in range(length): -//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) -//| -//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) -//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) -//| dac.play(sine_wave, loop=True) -//| time.sleep(1) -//| dac.stop() -//| -//| Playing a wave file from flash:: -//| -//| import board -//| import audiocore -//| import mtm_hardware -//| -//| f = open("sound.wav", "rb") -//| wav = audiocore.WaveFile(f) -//| -//| dac = mtm_hardware.DACOut(clock=board.GP18, mosi=board.GP19, cs=board.GP21) -//| dac.play(wav) -//| while dac.playing: -//| pass""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, - { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, - }; - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); - const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); - const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); - - mp_int_t gain = args[ARG_gain].u_int; - if (gain != 1 && gain != 2) { - mp_raise_ValueError(MP_COMPRESSED_ROM_TEXT("gain must be 1 or 2")); - } - - mtm_hardware_dacout_obj_t *self = mp_obj_malloc_with_finaliser(mtm_hardware_dacout_obj_t, &mtm_hardware_dacout_type); - common_hal_mtm_hardware_dacout_construct(self, clock, mosi, cs, (uint8_t)gain); - - return MP_OBJ_FROM_PTR(self); -} - -static void check_for_deinit(mtm_hardware_dacout_obj_t *self) { - if (common_hal_mtm_hardware_dacout_deinited(self)) { - raise_deinited_error(); - } -} - -//| def deinit(self) -> None: -//| """Deinitialises the DACOut and releases any hardware resources for reuse.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_deinit(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_mtm_hardware_dacout_deinit(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_deinit_obj, mtm_hardware_dacout_deinit); - -//| def __enter__(self) -> DACOut: -//| """No-op used by Context Managers.""" -//| ... -//| -// Provided by context manager helper. - -//| def __exit__(self) -> None: -//| """Automatically deinitializes the hardware when exiting a context. See -//| :ref:`lifetime-and-contextmanagers` for more info.""" -//| ... -//| -// Provided by context manager helper. - -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: -//| """Plays the sample once when loop=False and continuously when loop=True. -//| Does not block. Use `playing` to block. -//| -//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. -//| -//| The sample itself should consist of 8 bit or 16 bit samples.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sample, ARG_loop }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, - }; - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - check_for_deinit(self); - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - mp_obj_t sample = args[ARG_sample].u_obj; - common_hal_mtm_hardware_dacout_play(self, sample, args[ARG_loop].u_bool); - - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(mtm_hardware_dacout_play_obj, 1, mtm_hardware_dacout_obj_play); - -//| def stop(self) -> None: -//| """Stops playback.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_stop(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - common_hal_mtm_hardware_dacout_stop(self); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_stop_obj, mtm_hardware_dacout_obj_stop); - -//| playing: bool -//| """True when the audio sample is being output. (read-only)""" -//| -static mp_obj_t mtm_hardware_dacout_obj_get_playing(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_playing(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_playing_obj, mtm_hardware_dacout_obj_get_playing); - -MP_PROPERTY_GETTER(mtm_hardware_dacout_playing_obj, - (mp_obj_t)&mtm_hardware_dacout_get_playing_obj); - -//| def pause(self) -> None: -//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_pause(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - if (!common_hal_mtm_hardware_dacout_get_playing(self)) { - mp_raise_RuntimeError(MP_COMPRESSED_ROM_TEXT("Not playing")); - } - common_hal_mtm_hardware_dacout_pause(self); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_pause_obj, mtm_hardware_dacout_obj_pause); - -//| def resume(self) -> None: -//| """Resumes sample playback after :py:func:`pause`.""" -//| ... -//| -static mp_obj_t mtm_hardware_dacout_obj_resume(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - if (common_hal_mtm_hardware_dacout_get_paused(self)) { - common_hal_mtm_hardware_dacout_resume(self); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_resume_obj, mtm_hardware_dacout_obj_resume); - -//| paused: bool -//| """True when playback is paused. (read-only)""" -//| -static mp_obj_t mtm_hardware_dacout_obj_get_paused(mp_obj_t self_in) { - mtm_hardware_dacout_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - return mp_obj_new_bool(common_hal_mtm_hardware_dacout_get_paused(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(mtm_hardware_dacout_get_paused_obj, mtm_hardware_dacout_obj_get_paused); - -MP_PROPERTY_GETTER(mtm_hardware_dacout_paused_obj, - (mp_obj_t)&mtm_hardware_dacout_get_paused_obj); - -// ── DACOut type definition ─────────────────────────────────────────────────── - -static const mp_rom_map_elem_t mtm_hardware_dacout_locals_dict_table[] = { - // Methods - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mtm_hardware_dacout_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, - { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mtm_hardware_dacout_play_obj) }, - { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mtm_hardware_dacout_stop_obj) }, - { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mtm_hardware_dacout_pause_obj) }, - { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mtm_hardware_dacout_resume_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mtm_hardware_dacout_playing_obj) }, - { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mtm_hardware_dacout_paused_obj) }, -}; -static MP_DEFINE_CONST_DICT(mtm_hardware_dacout_locals_dict, mtm_hardware_dacout_locals_dict_table); - -MP_DEFINE_CONST_OBJ_TYPE( - mtm_hardware_dacout_type, - MP_QSTR_DACOut, - MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, - make_new, mtm_hardware_dacout_make_new, - locals_dict, &mtm_hardware_dacout_locals_dict - ); - -// ───────────────────────────────────────────────────────────────────────────── -// mtm_hardware module definition -// ───────────────────────────────────────────────────────────────────────────── - -//| """Hardware interface to Music Thing Modular Workshop Computer peripherals. -//| -//| Provides the `DACOut` class for non-blocking audio output via the -//| MCP4822 dual-channel 12-bit SPI DAC. -//| """ - -static const mp_rom_map_elem_t mtm_hardware_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mtm_hardware) }, - { MP_ROM_QSTR(MP_QSTR_DACOut), MP_ROM_PTR(&mtm_hardware_dacout_type) }, -}; - -static MP_DEFINE_CONST_DICT(mtm_hardware_module_globals, mtm_hardware_module_globals_table); - -const mp_obj_module_t mtm_hardware_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&mtm_hardware_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_mtm_hardware, mtm_hardware_module); diff --git a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk index 0383e0777faff..45c99711d72a3 100644 --- a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk +++ b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk @@ -11,11 +11,4 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY_AUDIOEFFECTS = 1 CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_PICODVI = 0 -<<<<<<< HEAD CIRCUITPY_MCP4822 = 1 -======= - -SRC_C += \ - boards/$(BOARD)/module/mtm_hardware.c \ - boards/$(BOARD)/module/DACOut.c ->>>>>>> d8bbe2a87f (mtm_computer: Add DAC audio out module) From d0e286a7fdd519dd536d45b3ba29fea54cc24315 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 16:51:02 -0700 Subject: [PATCH 455/517] restore deleted circuitpython.pot, update translation --- locale/circuitpython.pot | 4575 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 4575 insertions(+) create mode 100644 locale/circuitpython.pot diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot new file mode 100644 index 0000000000000..5d0be48fe3a79 --- /dev/null +++ b/locale/circuitpython.pot @@ -0,0 +1,4575 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: main.c +msgid "" +"\n" +"Code done running.\n" +msgstr "" + +#: main.c +msgid "" +"\n" +"Code stopped by auto-reload. Reloading soon.\n" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "" +"\n" +"Please file an issue with your program at github.com/adafruit/circuitpython/" +"issues." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "" +"\n" +"Press reset to exit safe mode.\n" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "" +"\n" +"You are in safe mode because:\n" +msgstr "" + +#: py/obj.c +msgid " File \"%q\"" +msgstr "" + +#: py/obj.c +msgid " File \"%q\", line %d" +msgstr "" + +#: py/builtinhelp.c +msgid " is of type %q\n" +msgstr "" + +#: main.c +msgid " not found.\n" +msgstr "" + +#: main.c +msgid " output:\n" +msgstr "" + +#: py/objstr.c +#, c-format +msgid "%%c needs int or char" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "" +"%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" +msgstr "" + +#: shared-bindings/microcontroller/Pin.c +msgid "%q and %q contain duplicate pins" +msgstr "" + +#: shared-bindings/audioio/AudioOut.c +msgid "%q and %q must be different" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +msgid "%q and %q must share a clock unit" +msgstr "" + +#: ports/nordic/common-hal/watchdog/WatchDogTimer.c +msgid "%q cannot be changed once mode is set to %q" +msgstr "" + +#: shared-bindings/microcontroller/Pin.c +msgid "%q contains duplicate pins" +msgstr "" + +#: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "%q failure: %d" +msgstr "" + +#: shared-module/audiodelays/MultiTapDelay.c +msgid "%q in %q must be of type %q or %q, not %q" +msgstr "" + +#: py/argcheck.c shared-module/audiofilters/Filter.c +msgid "%q in %q must be of type %q, not %q" +msgstr "" + +#: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c +#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c +#: ports/mimxrt10xx/common-hal/usb_host/Port.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +#: ports/raspberrypi/common-hal/usb_host/Port.c +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c +msgid "%q in use" +msgstr "" + +#: py/objstr.c +msgid "%q index out of range" +msgstr "" + +#: py/obj.c +msgid "%q indices must be integers, not %s" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c +msgid "%q init failed" +msgstr "" + +#: ports/espressif/bindings/espnow/Peer.c shared-bindings/dualbank/__init__.c +msgid "%q is %q" +msgstr "" + +#: ports/raspberrypi/common-hal/wifi/Radio.c +msgid "%q is read-only for this board" +msgstr "" + +#: py/argcheck.c shared-bindings/usb_hid/Device.c +msgid "%q length must be %d" +msgstr "" + +#: py/argcheck.c +msgid "%q length must be %d-%d" +msgstr "" + +#: py/argcheck.c +msgid "%q length must be <= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q length must be >= %d" +msgstr "" + +#: py/argcheck.c +msgid "%q must be %d" +msgstr "" + +#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/displayio/Bitmap.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/is31fl3741/FrameBuffer.c +#: shared-bindings/rgbmatrix/RGBMatrix.c +msgid "%q must be %d-%d" +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +msgid "%q must be 1 when %q is True" +msgstr "" + +#: py/argcheck.c shared-bindings/gifio/GifWriter.c +#: shared-module/gifio/OnDiskGif.c +msgid "%q must be <= %d" +msgstr "" + +#: ports/espressif/common-hal/watchdog/WatchDogTimer.c +msgid "%q must be <= %u" +msgstr "" + +#: py/argcheck.c +msgid "%q must be >= %d" +msgstr "" + +#: shared-bindings/analogbufio/BufferedIn.c +msgid "%q must be a bytearray or array of type 'H' or 'B'" +msgstr "" + +#: shared-bindings/audiocore/RawSample.c +msgid "%q must be a bytearray or array of type 'h', 'H', 'b', or 'B'" +msgstr "" + +#: shared-bindings/warnings/__init__.c +msgid "%q must be a subclass of %q" +msgstr "" + +#: ports/espressif/common-hal/analogbufio/BufferedIn.c +msgid "%q must be array of type 'H'" +msgstr "" + +#: shared-module/synthio/__init__.c +msgid "%q must be array of type 'h'" +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "%q must be multiple of 8." +msgstr "" + +#: ports/raspberrypi/bindings/cyw43/__init__.c py/argcheck.c py/objexcept.c +#: shared-bindings/bitmapfilter/__init__.c shared-bindings/canio/CAN.c +#: shared-bindings/digitalio/Pull.c shared-bindings/supervisor/__init__.c +#: shared-module/audiofilters/Filter.c shared-module/displayio/__init__.c +#: shared-module/synthio/Synthesizer.c +msgid "%q must be of type %q or %q, not %q" +msgstr "" + +#: shared-bindings/jpegio/JpegDecoder.c +msgid "%q must be of type %q, %q, or %q, not %q" +msgstr "" + +#: py/argcheck.c py/runtime.c shared-bindings/bitmapfilter/__init__.c +#: shared-module/audiodelays/MultiTapDelay.c shared-module/synthio/Note.c +#: shared-module/synthio/__init__.c +msgid "%q must be of type %q, not %q" +msgstr "" + +#: ports/atmel-samd/common-hal/busio/UART.c +msgid "%q must be power of 2" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + +#: shared-bindings/wifi/Monitor.c +msgid "%q out of bounds" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +#: ports/cxd56/common-hal/pulseio/PulseIn.c +#: ports/nordic/common-hal/pulseio/PulseIn.c +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +#: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c +msgid "%q out of range" +msgstr "" + +#: py/objmodule.c +msgid "%q renamed %q" +msgstr "" + +#: py/objrange.c py/objslice.c shared-bindings/random/__init__.c +msgid "%q step cannot be zero" +msgstr "" + +#: shared-module/bitbangio/I2C.c +msgid "%q too long" +msgstr "" + +#: py/bc.c py/objnamedtuple.c +msgid "%q() takes %d positional arguments but %d were given" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "%q() without %q()" +msgstr "" + +#: shared-bindings/usb_hid/Device.c +msgid "%q, %q, and %q must all be the same length" +msgstr "" + +#: py/objint.c shared-bindings/_bleio/Connection.c +#: shared-bindings/storage/__init__.c +msgid "%q=%q" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "%q[%u] shifts in more bits than pin count" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "%q[%u] shifts out more bits than pin count" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "%q[%u] uses extra pin" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "%q[%u] waits on input outside of count" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +#, c-format +msgid "%s error 0x%x" +msgstr "" + +#: py/argcheck.c +msgid "'%q' argument required" +msgstr "" + +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "'%q' object does not support '%q'" +msgstr "" + +#: py/runtime.c +msgid "'%q' object isn't an iterator" +msgstr "" + +#: py/objtype.c py/runtime.c shared-module/atexit/__init__.c +msgid "'%q' object isn't callable" +msgstr "" + +#: py/runtime.c +msgid "'%q' object isn't iterable" +msgstr "" + +#: py/emitinlinethumb.c py/emitinlinextensa.c +#, c-format +msgid "'%s' expects a label" +msgstr "" + +#: py/emitinlinethumb.c py/emitinlinextensa.c +#, c-format +msgid "'%s' expects a register" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects a special register" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects an FPU register" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects an address of the form [a, b]" +msgstr "" + +#: py/emitinlinethumb.c py/emitinlinextensa.c +#, c-format +msgid "'%s' expects an integer" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects at most r%d" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' expects {r0, r1, ...}" +msgstr "" + +#: py/emitinlinextensa.c +#, c-format +msgid "'%s' integer %d isn't within range %d..%d" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "'%s' integer 0x%x doesn't fit in mask 0x%x" +msgstr "" + +#: py/obj.c +#, c-format +msgid "'%s' object doesn't support item assignment" +msgstr "" + +#: py/obj.c +#, c-format +msgid "'%s' object doesn't support item deletion" +msgstr "" + +#: py/runtime.c +msgid "'%s' object has no attribute '%q'" +msgstr "" + +#: py/obj.c +#, c-format +msgid "'%s' object isn't subscriptable" +msgstr "" + +#: py/objstr.c +msgid "'=' alignment not allowed in string format specifier" +msgstr "" + +#: shared-module/struct/__init__.c +msgid "'S' and 'O' are not supported format types" +msgstr "" + +#: py/compile.c +msgid "'align' requires 1 argument" +msgstr "" + +#: py/compile.c +msgid "'await' outside function" +msgstr "" + +#: py/compile.c +msgid "'break'/'continue' outside loop" +msgstr "" + +#: py/compile.c +msgid "'data' requires at least 2 arguments" +msgstr "" + +#: py/compile.c +msgid "'data' requires integer arguments" +msgstr "" + +#: py/compile.c +msgid "'label' requires 1 argument" +msgstr "" + +#: py/emitnative.c +msgid "'not' not implemented" +msgstr "" + +#: py/compile.c +msgid "'return' outside function" +msgstr "" + +#: py/compile.c +msgid "'yield from' inside async function" +msgstr "" + +#: py/compile.c +msgid "'yield' outside function" +msgstr "" + +#: py/compile.c +msgid "* arg after **" +msgstr "" + +#: py/compile.c +msgid "*x must be assignment target" +msgstr "" + +#: py/obj.c +msgid ", in %q\n" +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +msgid ".show(x) removed. Use .root_group = x" +msgstr "" + +#: py/objcomplex.c +msgid "0.0 to a complex power" +msgstr "" + +#: py/modbuiltins.c +msgid "3-arg pow() not supported" +msgstr "" + +#: ports/raspberrypi/common-hal/wifi/Radio.c +msgid "AP could not be started" +msgstr "" + +#: shared-bindings/ipaddress/IPv4Address.c +#, c-format +msgid "Address must be %d bytes long" +msgstr "" + +#: ports/espressif/common-hal/memorymap/AddressRange.c +#: ports/nordic/common-hal/memorymap/AddressRange.c +#: ports/raspberrypi/common-hal/memorymap/AddressRange.c +msgid "Address range not allowed" +msgstr "" + +#: shared-bindings/memorymap/AddressRange.c +msgid "Address range wraps around" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +msgid "All CAN peripherals are in use" +msgstr "" + +#: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/i2ctarget/I2CTarget.c +#: ports/nordic/common-hal/busio/I2C.c +msgid "All I2C peripherals are in use" +msgstr "" + +#: ports/atmel-samd/common-hal/canio/Listener.c +#: ports/espressif/common-hal/canio/Listener.c +#: ports/stm/common-hal/canio/Listener.c +msgid "All RX FIFOs in use" +msgstr "" + +#: ports/espressif/common-hal/busio/SPI.c ports/nordic/common-hal/busio/SPI.c +msgid "All SPI peripherals are in use" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c +msgid "All UART peripherals are in use" +msgstr "" + +#: ports/nordic/common-hal/countio/Counter.c +#: ports/nordic/common-hal/pulseio/PulseIn.c +#: ports/nordic/common-hal/rotaryio/IncrementalEncoder.c +msgid "All channels in use" +msgstr "" + +#: ports/raspberrypi/common-hal/usb_host/Port.c +msgid "All dma channels in use" +msgstr "" + +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +msgid "All event channels in use" +msgstr "" + +#: ports/raspberrypi/common-hal/floppyio/__init__.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +#: ports/raspberrypi/common-hal/usb_host/Port.c +msgid "All state machines in use" +msgstr "" + +#: ports/atmel-samd/audio_dma.c +msgid "All sync event channels in use" +msgstr "" + +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c +msgid "All timers for this pin are in use" +msgstr "" + +#: ports/atmel-samd/common-hal/_pew/PewPew.c +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +#: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/cxd56/common-hal/pulseio/PulseOut.c +#: ports/nordic/common-hal/audiopwmio/PWMAudioOut.c +#: ports/nordic/common-hal/pulseio/PulseIn.c +#: ports/nordic/peripherals/nrf/timers.c +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +msgid "All timers in use" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Already advertising." +msgstr "" + +#: ports/atmel-samd/common-hal/canio/Listener.c +msgid "Already have all-matches listener" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +msgid "Already in progress" +msgstr "" + +#: ports/espressif/bindings/espnow/ESPNow.c +#: ports/espressif/common-hal/espulp/ULP.c +#: shared-module/memorymonitor/AllocationAlarm.c +#: shared-module/memorymonitor/AllocationSize.c +msgid "Already running" +msgstr "" + +#: ports/espressif/common-hal/wifi/Radio.c +#: ports/raspberrypi/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "Already scanning for wifi networks" +msgstr "" + +#: supervisor/shared/settings.c +#, c-format +msgid "An error occurred while retrieving '%s':\n" +msgstr "" + +#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c +msgid "Another PWMAudioOut is already active" +msgstr "" + +#: ports/atmel-samd/common-hal/pulseio/PulseOut.c +#: ports/cxd56/common-hal/pulseio/PulseOut.c +msgid "Another send is already active" +msgstr "" + +#: shared-bindings/pulseio/PulseOut.c +msgid "Array must contain halfwords (type 'H')" +msgstr "" + +#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c +#: shared-bindings/nvm/ByteArray.c +msgid "Array values should be single bytes." +msgstr "" + +#: ports/atmel-samd/common-hal/spitarget/SPITarget.c +msgid "Async SPI transfer in progress on this bus, keep awaiting." +msgstr "" + +#: shared-module/memorymonitor/AllocationAlarm.c +#, c-format +msgid "Attempt to allocate %d blocks" +msgstr "" + +#: ports/raspberrypi/audio_dma.c +msgid "Audio conversion not implemented" +msgstr "" + +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "Audio source error" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "AuthMode.OPEN is not used with password" +msgstr "" + +#: shared-bindings/wifi/Radio.c supervisor/shared/web_workflow/web_workflow.c +msgid "Authentication failure" +msgstr "" + +#: main.c +msgid "Auto-reload is off.\n" +msgstr "" + +#: main.c +msgid "" +"Auto-reload is on. Simply save files over USB to run them or enter REPL to " +"disable.\n" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +msgid "Baudrate not supported by peripheral" +msgstr "" + +#: shared-module/busdisplay/BusDisplay.c +#: shared-module/framebufferio/FramebufferDisplay.c +msgid "Below minimum frame rate" +msgstr "" + +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +msgid "Bit clock and word select must be sequential GPIO pins" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "Bitmap size and bits per value must match" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Boot device must be first (interface #0)." +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/UART.c +msgid "Both RX and TX required for flow control" +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +msgid "Brightness not adjustable" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "Buffer elements must be 4 bytes long or less" +msgstr "" + +#: shared-bindings/framebufferio/FramebufferDisplay.c +msgid "Buffer is not a bytearray." +msgstr "" + +#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c +#, c-format +msgid "Buffer length %d too big. It must be less than %d" +msgstr "" + +#: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/cxd56/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/sdioio/SDCard.c +#: ports/stm/common-hal/sdioio/SDCard.c shared-bindings/floppyio/__init__.c +#: shared-module/sdcardio/SDCard.c +#, c-format +msgid "Buffer must be a multiple of %d bytes" +msgstr "" + +#: shared-bindings/_bleio/PacketBuffer.c +#, c-format +msgid "Buffer too short by %d bytes" +msgstr "" + +#: ports/cxd56/common-hal/camera/Camera.c +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/struct/__init__.c shared-module/struct/__init__.c +msgid "Buffer too small" +msgstr "" + +#: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/raspberrypi/common-hal/paralleldisplaybus/ParallelBus.c +#, c-format +msgid "Bus pin %d is already in use" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "CBC blocks must be multiples of 16 bytes" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "CIRCUITPY drive could not be found or created." +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "CRC or checksum was invalid" +msgstr "" + +#: py/objtype.c +msgid "Call super().__init__() before accessing native object." +msgstr "" + +#: ports/cxd56/common-hal/camera/Camera.c +msgid "Camera init" +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Can only alarm on RTC IO from deep sleep." +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Can only alarm on one low pin while others alarm high from deep sleep." +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Can only alarm on two low pins from deep sleep." +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Can't construct AudioOut because continuous channel already open" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +msgid "Can't set CCCD on local Characteristic" +msgstr "" + +#: shared-bindings/storage/__init__.c shared-bindings/usb_cdc/__init__.c +#: shared-bindings/usb_hid/__init__.c shared-bindings/usb_midi/__init__.c +#: shared-bindings/usb_video/__init__.c +msgid "Cannot change USB devices now" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Cannot create a new Adapter; use _bleio.adapter;" +msgstr "" + +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + +#: shared-bindings/displayio/Bitmap.c +#: shared-bindings/memorymonitor/AllocationSize.c +#: shared-bindings/pulseio/PulseIn.c +msgid "Cannot delete values" +msgstr "" + +#: ports/atmel-samd/common-hal/digitalio/DigitalInOut.c +#: ports/mimxrt10xx/common-hal/digitalio/DigitalInOut.c +#: ports/nordic/common-hal/digitalio/DigitalInOut.c +#: ports/raspberrypi/common-hal/digitalio/DigitalInOut.c +msgid "Cannot get pull while in output mode" +msgstr "" + +#: ports/nordic/common-hal/microcontroller/Processor.c +msgid "Cannot get temperature" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Cannot have scan responses for extended, connectable advertisements." +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Cannot pull on input-only pin." +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "Cannot record to a file" +msgstr "" + +#: shared-module/storage/__init__.c +msgid "Cannot remount path when visible via USB." +msgstr "" + +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c +msgid "Cannot set value when direction is input." +msgstr "" + +#: ports/espressif/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/UART.c +msgid "Cannot specify RTS or CTS in RS485 mode" +msgstr "" + +#: py/objslice.c +msgid "Cannot subclass slice" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Cannot use GPIO0..15 together with GPIO32..47" +msgstr "" + +#: ports/nordic/common-hal/alarm/pin/PinAlarm.c +msgid "Cannot wake on pin edge, only level" +msgstr "" + +#: ports/espressif/common-hal/alarm/pin/PinAlarm.c +msgid "Cannot wake on pin edge. Only level." +msgstr "" + +#: shared-bindings/_bleio/CharacteristicBuffer.c +msgid "CharacteristicBuffer writing not provided" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "CircuitPython core code crashed hard. Whoops!\n" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +msgid "Clock unit in use" +msgstr "" + +#: shared-bindings/_bleio/Connection.c +msgid "" +"Connection has been disconnected and can no longer be used. Create a new " +"connection." +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "Coordinate arrays have different lengths" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "Coordinate arrays types have different sizes" +msgstr "" + +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/Publisher.c +msgid "Could not publish to ROS topic" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Could not set address" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "Could not start interrupt, RX busy" +msgstr "" + +#: shared-module/audiomp3/MP3Decoder.c +msgid "Couldn't allocate decoder" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +#, c-format +msgid "Critical ROS failure during soft reboot, reset required: %d" +msgstr "" + +#: ports/stm/common-hal/analogio/AnalogOut.c +msgid "DAC Channel Init Error" +msgstr "" + +#: ports/stm/common-hal/analogio/AnalogOut.c +msgid "DAC Device Init Error" +msgstr "" + +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +msgid "DAC already in use" +msgstr "" + +#: ports/atmel-samd/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/nordic/common-hal/paralleldisplaybus/ParallelBus.c +msgid "Data 0 pin must be byte aligned" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Data format error (may be broken data)" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Data not supported with directed advertising" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Data too large for advertisement packet" +msgstr "" + +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Deep sleep pins must use a rising edge with pulldown" +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "Destination capacity is smaller than destination_length." +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Device error or wrong termination of input stream" +msgstr "" + +#: ports/nordic/common-hal/audiobusio/I2SOut.c +msgid "Device in use" +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +msgid "Display must have a 16 bit colorspace." +msgstr "" + +#: shared-bindings/busdisplay/BusDisplay.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c +msgid "Display rotation must be in 90 degree increments" +msgstr "" + +#: main.c +msgid "Done" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c +msgid "Drive mode not used when direction is input." +msgstr "" + +#: py/obj.c +msgid "During handling of the above exception, another exception occurred:" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "ECB only operates on 16 bytes at a time" +msgstr "" + +#: ports/espressif/common-hal/busio/SPI.c +#: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "ESP-IDF memory allocation failed" +msgstr "" + +#: extmod/modre.c +msgid "Error in regex" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Error in safemode.py." +msgstr "" + +#: shared-bindings/alarm/__init__.c +msgid "Expected a kind of %q" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Extended advertisements with scan response not supported." +msgstr "" + +#: extmod/ulab/code/numpy/fft/fft_tools.c +msgid "FFT is defined for ndarrays only" +msgstr "" + +#: extmod/ulab/code/numpy/fft/fft_tools.c +msgid "FFT is implemented for linear arrays only" +msgstr "" + +#: shared-bindings/ps2io/Ps2.c +msgid "Failed sending command." +msgstr "" + +#: ports/nordic/sd_mutex.c +#, c-format +msgid "Failed to acquire mutex, err 0x%04x" +msgstr "" + +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "Failed to add service TXT record" +msgstr "" + +#: shared-bindings/mdns/Server.c +msgid "" +"Failed to add service TXT record; non-string or bytes found in txt_records" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c +msgid "Failed to allocate %q buffer" +msgstr "" + +#: ports/espressif/common-hal/wifi/__init__.c +msgid "Failed to allocate Wifi memory" +msgstr "" + +#: ports/espressif/common-hal/wifi/ScannedNetworks.c +msgid "Failed to allocate wifi scan memory" +msgstr "" + +#: ports/stm/common-hal/audiopwmio/PWMAudioOut.c +msgid "Failed to buffer the sample" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect: internal error" +msgstr "" + +#: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Failed to connect: timeout" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to create continuous channels: invalid arg" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to create continuous channels: invalid state" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to create continuous channels: no mem" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to create continuous channels: not found" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to enable continuous" +msgstr "" + +#: shared-module/audiomp3/MP3Decoder.c +msgid "Failed to parse MP3 file" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to register continuous events callback" +msgstr "" + +#: ports/nordic/sd_mutex.c +#, c-format +msgid "Failed to release mutex, err 0x%04x" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "Failed to set hostname" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "Failed to start async audio" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Failed to write internal flash." +msgstr "" + +#: py/moderrno.c +msgid "File exists" +msgstr "" + +#: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c +msgid "File not found" +msgstr "" + +#: ports/atmel-samd/common-hal/canio/Listener.c +#: ports/espressif/common-hal/canio/Listener.c +#: ports/mimxrt10xx/common-hal/canio/Listener.c +#: ports/stm/common-hal/canio/Listener.c +msgid "Filters too complex" +msgstr "" + +#: ports/espressif/common-hal/dualbank/__init__.c +msgid "Firmware is duplicate" +msgstr "" + +#: ports/espressif/common-hal/dualbank/__init__.c +msgid "Firmware is invalid" +msgstr "" + +#: ports/espressif/common-hal/dualbank/__init__.c +msgid "Firmware is too big" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "For L8 colorspace, input bitmap must have 8 bits per pixel" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "For RGB colorspaces, input bitmap must have 16 bits per pixel" +msgstr "" + +#: ports/cxd56/common-hal/camera/Camera.c shared-module/audiocore/WaveFile.c +msgid "Format not supported" +msgstr "" + +#: ports/mimxrt10xx/common-hal/microcontroller/Processor.c +msgid "" +"Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" +msgstr "" + +#: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c +#: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c +msgid "Function requires lock" +msgstr "" + +#: ports/cxd56/common-hal/gnss/GNSS.c +msgid "GNSS init" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Generic Failure" +msgstr "" + +#: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-module/busdisplay/BusDisplay.c +#: shared-module/framebufferio/FramebufferDisplay.c +msgid "Group already used" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Hard fault: memory access or instruction error." +msgstr "" + +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/UART.c ports/stm/common-hal/busio/I2C.c +#: ports/stm/common-hal/busio/SPI.c ports/stm/common-hal/busio/UART.c +#: ports/stm/common-hal/canio/CAN.c ports/stm/common-hal/sdioio/SDCard.c +msgid "Hardware in use, try alternative pins" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Heap allocation when VM not running." +msgstr "" + +#: extmod/vfs_posix_file.c py/objstringio.c +msgid "I/O operation on closed file" +msgstr "" + +#: ports/stm/common-hal/busio/I2C.c +msgid "I2C init error" +msgstr "" + +#: ports/raspberrypi/common-hal/busio/I2C.c +#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c +msgid "I2C peripheral in use" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "In-buffer elements must be <= 4 bytes long" +msgstr "" + +#: shared-bindings/_pew/PewPew.c +msgid "Incorrect buffer size" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "Init program size invalid" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Initial set pin direction conflicts with initial out pin direction" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Initial set pin state conflicts with initial out pin state" +msgstr "" + +#: shared-bindings/bitops/__init__.c +#, c-format +msgid "Input buffer length (%d) must be a multiple of the strand count (%d)" +msgstr "" + +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +msgid "Input taking too long" +msgstr "" + +#: py/moderrno.c +msgid "Input/output error" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Insufficient authentication" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Insufficient encryption" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Insufficient memory pool for the image" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Insufficient stream input buffer" +msgstr "" + +#: ports/espressif/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "Interface must be started" +msgstr "" + +#: ports/atmel-samd/audio_dma.c ports/raspberrypi/audio_dma.c +msgid "Internal audio buffer too small" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "Internal define error" +msgstr "" + +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c +msgid "Internal error" +msgstr "" + +#: shared-module/rgbmatrix/RGBMatrix.c +#, c-format +msgid "Internal error #%d" +msgstr "" + +#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c +#: ports/atmel-samd/common-hal/countio/Counter.c +#: ports/atmel-samd/common-hal/frequencyio/FrequencyIn.c +#: ports/atmel-samd/common-hal/max3421e/Max3421E.c +#: ports/atmel-samd/common-hal/ps2io/Ps2.c +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +#: ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c +#: ports/cxd56/common-hal/pulseio/PulseIn.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +#: shared-bindings/pwmio/PWMOut.c +msgid "Internal resource(s) in use" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Internal watchdog timer expired." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Interrupt error." +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Interrupted by output function" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c +#: ports/espressif/common-hal/_bleio/Service.c +#: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c +#: ports/mimxrt10xx/common-hal/audiobusio/__init__.c +#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c +#: ports/raspberrypi/bindings/picodvi/Framebuffer.c +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c +#: shared-module/aurora_epaper/aurora_framebuffer.c +#: shared-module/lvfontio/OnDiskFont.c +msgid "Invalid %q" +msgstr "" + +#: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +#: shared-module/aurora_epaper/aurora_framebuffer.c +msgid "Invalid %q and %q" +msgstr "" + +#: ports/atmel-samd/common-hal/microcontroller/Pin.c +#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c +#: ports/mimxrt10xx/common-hal/microcontroller/Pin.c +#: shared-bindings/microcontroller/Pin.c +msgid "Invalid %q pin" +msgstr "" + +#: ports/stm/common-hal/analogio/AnalogIn.c +msgid "Invalid ADC Unit value" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Invalid BLE parameter" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "Invalid BSSID" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "Invalid MAC address" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +msgid "Invalid ROS domain ID" +msgstr "" + +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c +msgid "Invalid argument" +msgstr "" + +#: shared-module/displayio/Bitmap.c +msgid "Invalid bits per value" +msgstr "" + +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +#, c-format +msgid "Invalid data_pins[%d]" +msgstr "" + +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c +msgid "Invalid format" +msgstr "" + +#: shared-module/audiocore/WaveFile.c +msgid "Invalid format chunk size" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "Invalid hex password" +msgstr "" + +#: ports/espressif/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "Invalid multicast MAC address" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Invalid size" +msgstr "" + +#: shared-module/ssl/SSLSocket.c +msgid "Invalid socket for TLS" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +#: ports/espressif/common-hal/espidf/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Invalid state" +msgstr "" + +#: supervisor/shared/settings.c +msgid "Invalid unicode escape" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "Key must be 16, 24, or 32 bytes long" +msgstr "" + +#: shared-module/is31fl3741/FrameBuffer.c +msgid "LED mappings must match display size" +msgstr "" + +#: py/compile.c +msgid "LHS of keyword arg must be an id" +msgstr "" + +#: shared-module/displayio/Group.c +msgid "Layer already in a group" +msgstr "" + +#: shared-module/displayio/Group.c +msgid "Layer must be a Group or TileGrid subclass" +msgstr "" + +#: shared-bindings/audiocore/RawSample.c +msgid "Length of %q must be an even multiple of channel_count * type_size" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "MAC address was invalid" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/espressif/common-hal/_bleio/Descriptor.c +msgid "MITM security not supported" +msgstr "" + +#: ports/stm/common-hal/sdioio/SDCard.c +#, c-format +msgid "MMC/SDIO Clock Error %x" +msgstr "" + +#: shared-bindings/is31fl3741/IS31FL3741.c +msgid "Mapping must be a tuple" +msgstr "" + +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Mismatched data size" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Mismatched swap flag" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_in_pin. %q[%u] reads pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_in_pin. %q[%u] shifts in from pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_in_pin. %q[%u] waits based on pin" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_out_pin. %q[%u] shifts out to pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_out_pin. %q[%u] writes pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing first_set_pin. %q[%u] sets pin(s)" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Missing jmp_pin. %q[%u] jumps on pin" +msgstr "" + +#: shared-module/storage/__init__.c +msgid "Mount point directory missing" +msgstr "" + +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c +msgid "Must be a %q subclass." +msgstr "" + +#: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c +msgid "Must provide 5/6/5 RGB pins" +msgstr "" + +#: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c +msgid "Must provide MISO or MOSI pin" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "Must use a multiple of 6 rgb pins, not %d" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "NLR jump failed. Likely memory corruption." +msgstr "" + +#: ports/espressif/common-hal/nvm/ByteArray.c +msgid "NVS Error" +msgstr "" + +#: shared-bindings/socketpool/SocketPool.c +msgid "Name or service not known" +msgstr "" + +#: shared-bindings/displayio/TileGrid.c +msgid "New bitmap must be same size as old bitmap" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +msgid "Nimble out of memory" +msgstr "" + +#: ports/atmel-samd/common-hal/busio/UART.c +#: ports/espressif/common-hal/busio/SPI.c +#: ports/espressif/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/SPI.c +#: ports/mimxrt10xx/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c +#: ports/raspberrypi/common-hal/busio/UART.c ports/stm/common-hal/busio/SPI.c +#: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c +#: shared-bindings/i2cdisplaybus/I2CDisplayBus.c +#: shared-bindings/paralleldisplaybus/ParallelBus.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c +msgid "No %q pin" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +msgid "No CCCD for this Characteristic" +msgstr "" + +#: ports/atmel-samd/common-hal/analogio/AnalogOut.c +#: ports/stm/common-hal/analogio/AnalogOut.c +msgid "No DAC on chip" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "No DMA channel found" +msgstr "" + +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +msgid "No DMA pacing timer found" +msgstr "" + +#: shared-module/adafruit_bus_device/i2c_device/I2CDevice.c +#, c-format +msgid "No I2C device at address: 0x%x" +msgstr "" + +#: supervisor/shared/web_workflow/web_workflow.c +msgid "No IP" +msgstr "" + +#: ports/atmel-samd/common-hal/microcontroller/__init__.c +#: ports/cxd56/common-hal/microcontroller/__init__.c +#: ports/mimxrt10xx/common-hal/microcontroller/__init__.c +msgid "No bootloader present" +msgstr "" + +#: shared-module/usb/core/Device.c +msgid "No configuration set" +msgstr "" + +#: shared-bindings/_bleio/PacketBuffer.c +msgid "No connection: length cannot be determined" +msgstr "" + +#: shared-bindings/board/__init__.c +msgid "No default %q bus" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +#: ports/atmel-samd/common-hal/touchio/TouchIn.c +msgid "No free GCLKs" +msgstr "" + +#: shared-bindings/os/__init__.c +msgid "No hardware random available" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "No in in program" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "No in or out in program" +msgstr "" + +#: py/objint.c shared-bindings/time/__init__.c +msgid "No long integer support" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "No network with that ssid" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "No out in program" +msgstr "" + +#: ports/atmel-samd/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/I2C.c +#: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nordic/common-hal/busio/I2C.c +#: ports/raspberrypi/common-hal/busio/I2C.c +msgid "No pull up found on SDA or SCL; check your wiring" +msgstr "" + +#: shared-module/touchio/TouchIn.c +msgid "No pulldown on pin; 1Mohm recommended" +msgstr "" + +#: shared-module/touchio/TouchIn.c +msgid "No pullup on pin; 1Mohm recommended" +msgstr "" + +#: py/moderrno.c +msgid "No space left on device" +msgstr "" + +#: py/moderrno.c +msgid "No such device" +msgstr "" + +#: py/moderrno.c +msgid "No such file/directory" +msgstr "" + +#: shared-module/rgbmatrix/RGBMatrix.c +msgid "No timer available" +msgstr "" + +#: shared-module/usb/core/Device.c +msgid "No usb host port initialized" +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "Nordic system firmware out of memory" +msgstr "" + +#: shared-bindings/ipaddress/IPv4Address.c shared-bindings/ipaddress/__init__.c +msgid "Not a valid IP string" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#: ports/nordic/common-hal/_bleio/__init__.c +#: shared-bindings/_bleio/CharacteristicBuffer.c +msgid "Not connected" +msgstr "" + +#: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c +msgid "Not playing" +msgstr "" + +#: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c +#: ports/espressif/common-hal/sdioio/SDCard.c +#, c-format +msgid "Number of data_pins must be %d or %d, not %d" +msgstr "" + +#: shared-bindings/util.c +msgid "" +"Object has been deinitialized and can no longer be used. Create a new object." +msgstr "" + +#: ports/nordic/common-hal/busio/UART.c +msgid "Odd parity is not supported" +msgstr "" + +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Off" +msgstr "" + +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Ok" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c +#, c-format +msgid "Only 8 or 16 bit mono with %dx oversampling supported." +msgstr "" + +#: ports/espressif/common-hal/wifi/__init__.c +#: ports/raspberrypi/common-hal/wifi/__init__.c +msgid "Only IPv4 addresses supported" +msgstr "" + +#: ports/raspberrypi/common-hal/socketpool/Socket.c +msgid "Only IPv4 sockets supported" +msgstr "" + +#: shared-module/displayio/OnDiskBitmap.c +#, c-format +msgid "" +"Only Windows format, uncompressed BMP supported: given header size is %d" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Only connectable advertisements can be directed" +msgstr "" + +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Only edge detection is available on this hardware" +msgstr "" + +#: shared-bindings/ipaddress/__init__.c +msgid "Only int or string supported for ip" +msgstr "" + +#: ports/espressif/common-hal/alarm/touch/TouchAlarm.c +msgid "Only one %q can be set in deep sleep." +msgstr "" + +#: ports/espressif/common-hal/espulp/ULPAlarm.c +msgid "Only one %q can be set." +msgstr "" + +#: ports/espressif/common-hal/i2ctarget/I2CTarget.c +#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c +msgid "Only one address is allowed" +msgstr "" + +#: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c +#: ports/nordic/common-hal/alarm/time/TimeAlarm.c +#: ports/stm/common-hal/alarm/time/TimeAlarm.c +msgid "Only one alarm.time alarm can be set" +msgstr "" + +#: ports/espressif/common-hal/alarm/time/TimeAlarm.c +#: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c +msgid "Only one alarm.time alarm can be set." +msgstr "" + +#: shared-module/displayio/ColorConverter.c +msgid "Only one color can be transparent at a time" +msgstr "" + +#: py/moderrno.c +msgid "Operation not permitted" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Operation or feature not supported" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c +msgid "Operation timed out" +msgstr "" + +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "Out of MDNS service slots" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Out of memory" +msgstr "" + +#: ports/espressif/common-hal/socketpool/Socket.c +#: ports/raspberrypi/common-hal/socketpool/Socket.c +#: ports/zephyr-cp/common-hal/socketpool/Socket.c +msgid "Out of sockets" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "Out-buffer elements must be <= 4 bytes long" +msgstr "" + +#: ports/stm/common-hal/pwmio/PWMOut.c +msgid "PWM restart" +msgstr "" + +#: ports/raspberrypi/common-hal/countio/Counter.c +msgid "PWM slice already in use" +msgstr "" + +#: ports/raspberrypi/common-hal/countio/Counter.c +msgid "PWM slice channel A already in use" +msgstr "" + +#: shared-bindings/spitarget/SPITarget.c +msgid "Packet buffers for an SPI transfer must have the same length." +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Parameter error" +msgstr "" + +#: ports/espressif/common-hal/audiobusio/__init__.c +msgid "Peripheral in use" +msgstr "" + +#: py/moderrno.c +msgid "Permission denied" +msgstr "" + +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +msgid "Pin cannot wake from Deep Sleep" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Pin count too large" +msgstr "" + +#: ports/stm/common-hal/alarm/pin/PinAlarm.c +#: ports/stm/common-hal/pulseio/PulseIn.c +msgid "Pin interrupt already in use" +msgstr "" + +#: shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c +msgid "Pin is input only" +msgstr "" + +#: ports/raspberrypi/common-hal/countio/Counter.c +msgid "Pin must be on PWM Channel B" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "" +"Pinout uses %d bytes per element, which consumes more than the ideal %d " +"bytes. If this cannot be avoided, pass allow_inefficient=True to the " +"constructor" +msgstr "" + +#: ports/raspberrypi/common-hal/imagecapture/ParallelImageCapture.c +msgid "Pins must be sequential" +msgstr "" + +#: ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c +msgid "Pins must be sequential GPIO pins" +msgstr "" + +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +msgid "Pins must share PWM slice" +msgstr "" + +#: shared-module/usb/core/Device.c +msgid "Pipe error" +msgstr "" + +#: py/builtinhelp.c +msgid "Plus any modules on the filesystem\n" +msgstr "" + +#: shared-module/vectorio/Polygon.c +msgid "Polygon needs at least 3 points" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Power dipped. Make sure you are providing enough power." +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "Prefix buffer must be on the heap" +msgstr "" + +#: main.c +msgid "Press any key to enter the REPL. Use CTRL-D to reload.\n" +msgstr "" + +#: main.c +msgid "Pretending to deep sleep until alarm, CTRL-C or file write.\n" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Program does IN without loading ISR" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "Program does OUT without loading OSR" +msgstr "" + +#: ports/raspberrypi/bindings/rp2pio/StateMachine.c +msgid "Program size invalid" +msgstr "" + +#: ports/espressif/common-hal/espulp/ULP.c +msgid "Program too long" +msgstr "" + +#: shared-bindings/rclcpy/Publisher.c +msgid "Publishers can only be created from a parent node" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c +msgid "Pull not used when direction is output." +msgstr "" + +#: ports/raspberrypi/common-hal/countio/Counter.c +msgid "RISE_AND_FALL not available on this chip" +msgstr "" + +#: shared-module/displayio/OnDiskBitmap.c +msgid "RLE-compressed BMP not supported" +msgstr "" + +#: ports/stm/common-hal/os/__init__.c +msgid "RNG DeInit Error" +msgstr "" + +#: ports/stm/common-hal/os/__init__.c +msgid "RNG Init Error" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +msgid "ROS failed to initialize. Is agent connected?" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +msgid "ROS internal setup failure" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/__init__.c +msgid "ROS memory allocator failure" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/Node.c +msgid "ROS node failed to initialize" +msgstr "" + +#: ports/espressif/common-hal/rclcpy/Publisher.c +msgid "ROS topic failed to initialize" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +#: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c +msgid "RS485" +msgstr "" + +#: ports/espressif/common-hal/busio/UART.c +#: ports/mimxrt10xx/common-hal/busio/UART.c +msgid "RS485 inversion specified when not in RS485 mode" +msgstr "" + +#: shared-bindings/alarm/time/TimeAlarm.c shared-bindings/time/__init__.c +msgid "RTC is not supported on this board" +msgstr "" + +#: ports/stm/common-hal/os/__init__.c +msgid "Random number generation error" +msgstr "" + +#: shared-bindings/_bleio/__init__.c +#: shared-bindings/memorymonitor/AllocationSize.c +#: shared-bindings/pulseio/PulseIn.c shared-module/bitmaptools/__init__.c +#: shared-module/displayio/Bitmap.c shared-module/displayio/Group.c +msgid "Read-only" +msgstr "" + +#: extmod/vfs_fat.c py/moderrno.c +msgid "Read-only filesystem" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Received response was invalid" +msgstr "" + +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Reconnecting" +msgstr "" + +#: shared-bindings/epaperdisplay/EPaperDisplay.c +msgid "Refresh too soon" +msgstr "" + +#: shared-bindings/canio/RemoteTransmissionRequest.c +msgid "RemoteTransmissionRequests limited to 8 bytes" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "Requested AES mode is unsupported" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Requested resource not found" +msgstr "" + +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +msgid "Right channel unsupported" +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Right format but not supported" +msgstr "" + +#: main.c +msgid "Running in safe mode! Not running saved code.\n" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "SD card CSD format not supported" +msgstr "" + +#: ports/cxd56/common-hal/sdioio/SDCard.c +msgid "SDCard init" +msgstr "" + +#: ports/stm/common-hal/sdioio/SDCard.c +#, c-format +msgid "SDIO GetCardInfo Error %d" +msgstr "" + +#: ports/espressif/common-hal/sdioio/SDCard.c +#: ports/stm/common-hal/sdioio/SDCard.c +#, c-format +msgid "SDIO Init Error %x" +msgstr "" + +#: ports/espressif/common-hal/busio/SPI.c +msgid "SPI configuration failed" +msgstr "" + +#: ports/stm/common-hal/busio/SPI.c +msgid "SPI init error" +msgstr "" + +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + +#: ports/raspberrypi/common-hal/busio/SPI.c +msgid "SPI peripheral in use" +msgstr "" + +#: ports/stm/common-hal/busio/SPI.c +msgid "SPI re-init" +msgstr "" + +#: shared-bindings/is31fl3741/FrameBuffer.c +msgid "Scale dimensions must divide by 3" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "Scan already in progress. Stop with stop_scan." +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +msgid "Serializer in use" +msgstr "" + +#: shared-bindings/ssl/SSLContext.c +msgid "Server side context cannot have hostname" +msgstr "" + +#: ports/cxd56/common-hal/camera/Camera.c +msgid "Size not supported" +msgstr "" + +#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c +#: shared-bindings/nvm/ByteArray.c +msgid "Slice and value different lengths." +msgstr "" + +#: shared-bindings/displayio/Bitmap.c shared-bindings/displayio/Group.c +#: shared-bindings/displayio/TileGrid.c +#: shared-bindings/memorymonitor/AllocationSize.c +#: shared-bindings/pulseio/PulseIn.c +#: shared-bindings/tilepalettemapper/TilePaletteMapper.c +msgid "Slices not supported" +msgstr "" + +#: ports/espressif/common-hal/socketpool/SocketPool.c +#: ports/raspberrypi/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio" +msgstr "" + +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + +#: shared-bindings/aesio/aes.c +msgid "Source and destination buffers must be the same length" +msgstr "" + +#: shared-bindings/paralleldisplaybus/ParallelBus.c +msgid "Specify exactly one of data0 or data_pins" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Stack overflow. Increase stack size." +msgstr "" + +#: shared-bindings/alarm/time/TimeAlarm.c +msgid "Supply one of monotonic_time or epoch_time" +msgstr "" + +#: shared-bindings/gnss/GNSS.c +msgid "System entry must be gnss.SatelliteSystem" +msgstr "" + +#: ports/stm/common-hal/microcontroller/Processor.c +msgid "Temperature read timed out" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "The `microcontroller` module was used to boot into safe mode." +msgstr "" + +#: py/obj.c +msgid "The above exception was the direct cause of the following exception:" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +msgid "The length of rgb_pins must be 6, 12, 18, 24, or 30" +msgstr "" + +#: shared-module/audiocore/__init__.c +msgid "The sample's %q does not match" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Third-party firmware fatal error." +msgstr "" + +#: shared-module/imagecapture/ParallelImageCapture.c +msgid "This microcontroller does not support continuous capture." +msgstr "" + +#: shared-module/paralleldisplaybus/ParallelBus.c +msgid "" +"This microcontroller only supports data0=, not data_pins=, because it " +"requires contiguous pins." +msgstr "" + +#: shared-bindings/displayio/TileGrid.c +msgid "Tile height must exactly divide bitmap height" +msgstr "" + +#: shared-bindings/displayio/TileGrid.c +#: shared-bindings/tilepalettemapper/TilePaletteMapper.c +#: shared-module/displayio/TileGrid.c +msgid "Tile index out of bounds" +msgstr "" + +#: shared-bindings/displayio/TileGrid.c +msgid "Tile width must exactly divide bitmap width" +msgstr "" + +#: shared-module/tilepalettemapper/TilePaletteMapper.c +msgid "TilePaletteMapper may only be bound to a TileGrid once" +msgstr "" + +#: shared-bindings/alarm/time/TimeAlarm.c +msgid "Time is in the past." +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/nordic/common-hal/_bleio/Adapter.c +#, c-format +msgid "Timeout is too long: Maximum timeout length is %d seconds" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +msgid "Too many channels in sample" +msgstr "" + +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "Too many channels in sample." +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +msgid "Too many descriptors" +msgstr "" + +#: shared-module/displayio/__init__.c +msgid "Too many display busses; forgot displayio.release_displays() ?" +msgstr "" + +#: shared-module/displayio/__init__.c +msgid "Too many displays" +msgstr "" + +#: ports/espressif/common-hal/_bleio/PacketBuffer.c +#: ports/nordic/common-hal/_bleio/PacketBuffer.c +msgid "Total data to write is larger than %q" +msgstr "" + +#: ports/raspberrypi/common-hal/alarm/touch/TouchAlarm.c +#: ports/stm/common-hal/alarm/touch/TouchAlarm.c +msgid "Touch alarms not available" +msgstr "" + +#: py/obj.c +msgid "Traceback (most recent call last):\n" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "UART de-init" +msgstr "" + +#: ports/cxd56/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/stm/common-hal/busio/UART.c +msgid "UART init" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + +#: ports/raspberrypi/common-hal/busio/UART.c +msgid "UART peripheral in use" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "UART re-init" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + +#: ports/stm/common-hal/busio/UART.c +msgid "UART write" +msgstr "" + +#: main.c +msgid "UID:" +msgstr "" + +#: shared-module/usb_hid/Device.c +msgid "USB busy" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "USB devices need more endpoints than are available." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "USB devices specify too many interface names." +msgstr "" + +#: shared-module/usb_hid/Device.c +msgid "USB error" +msgstr "" + +#: shared-bindings/_bleio/UUID.c +msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" +msgstr "" + +#: shared-bindings/_bleio/UUID.c +msgid "UUID value is not str, int or byte buffer" +msgstr "" + +#: ports/raspberrypi/common-hal/memorymap/AddressRange.c +msgid "Unable to access unaligned IO register" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/I2SOut.c +#: ports/atmel-samd/common-hal/audioio/AudioOut.c +#: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "Unable to allocate buffers for signed conversion" +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "Unable to allocate to the heap." +msgstr "" + +#: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c +msgid "Unable to create lock" +msgstr "" + +#: shared-module/i2cdisplaybus/I2CDisplayBus.c +#: shared-module/is31fl3741/IS31FL3741.c +#, c-format +msgid "Unable to find I2C Display at %x" +msgstr "" + +#: py/parse.c +msgid "Unable to init parser" +msgstr "" + +#: shared-module/displayio/OnDiskBitmap.c +msgid "Unable to read color palette data" +msgstr "" + +#: ports/mimxrt10xx/common-hal/canio/CAN.c +msgid "Unable to send CAN Message: all Tx message buffers are busy" +msgstr "" + +#: ports/espressif/common-hal/mdns/Server.c +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "Unable to start mDNS query" +msgstr "" + +#: shared-bindings/nvm/ByteArray.c +msgid "Unable to write to nvm." +msgstr "" + +#: ports/raspberrypi/common-hal/memorymap/AddressRange.c +msgid "Unable to write to read-only memory" +msgstr "" + +#: shared-bindings/alarm/SleepMemory.c +msgid "Unable to write to sleep_memory." +msgstr "" + +#: ports/nordic/common-hal/_bleio/UUID.c +msgid "Unexpected nrfx uuid type" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown BLE error at %s:%d: %d" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown BLE error: %d" +msgstr "" + +#: ports/espressif/common-hal/max3421e/Max3421E.c +#: ports/raspberrypi/common-hal/wifi/__init__.c +#, c-format +msgid "Unknown error code %d" +msgstr "" + +#: shared-bindings/wifi/Radio.c +#, c-format +msgid "Unknown failure %d" +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown gatt error: 0x%04x" +msgstr "" + +#: ports/atmel-samd/common-hal/alarm/pin/PinAlarm.c +#: supervisor/shared/safe_mode.c +msgid "Unknown reason." +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown security error: 0x%04x" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown system firmware error at %s:%d: %d" +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown system firmware error: %04x" +msgstr "" + +#: ports/espressif/common-hal/_bleio/__init__.c +#, c-format +msgid "Unknown system firmware error: %d" +msgstr "" + +#: shared-bindings/adafruit_pixelbuf/PixelBuf.c +#: shared-module/_pixelmap/PixelMap.c +#, c-format +msgid "Unmatched number of items on RHS (expected %d, got %d)." +msgstr "" + +#: ports/nordic/common-hal/_bleio/__init__.c +msgid "" +"Unspecified issue. Can be that the pairing prompt on the other device was " +"declined or ignored." +msgstr "" + +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "Unsupported colorspace" +msgstr "" + +#: shared-module/displayio/bus_core.c +msgid "Unsupported display bus type" +msgstr "" + +#: shared-bindings/hashlib/__init__.c +msgid "Unsupported hash algorithm" +msgstr "" + +#: ports/espressif/common-hal/socketpool/Socket.c +#: ports/zephyr-cp/common-hal/socketpool/Socket.c +msgid "Unsupported socket type" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Adapter.c +#: ports/espressif/common-hal/dualbank/__init__.c +msgid "Update failed" +msgstr "" + +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Descriptor.c +msgid "Value length != required fixed length" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Descriptor.c +msgid "Value length > max_length" +msgstr "" + +#: ports/espressif/common-hal/espidf/__init__.c +msgid "Version was invalid" +msgstr "" + +#: ports/stm/common-hal/microcontroller/Processor.c +msgid "Voltage read timed out" +msgstr "" + +#: main.c +msgid "WARNING: Your code filename has two extensions\n" +msgstr "" + +#: ports/nordic/common-hal/watchdog/WatchDogTimer.c +msgid "WatchDogTimer cannot be deinitialized once mode is set to RESET" +msgstr "" + +#: py/builtinhelp.c +#, c-format +msgid "" +"Welcome to Adafruit CircuitPython %s!\n" +"\n" +"Visit circuitpython.org for more information.\n" +"\n" +"To list built-in modules type `help(\"modules\")`.\n" +msgstr "" + +#: supervisor/shared/web_workflow/web_workflow.c +msgid "Wi-Fi: " +msgstr "" + +#: ports/espressif/common-hal/wifi/Radio.c +#: ports/raspberrypi/common-hal/wifi/Radio.c +#: ports/zephyr-cp/common-hal/wifi/Radio.c +msgid "WiFi is not enabled" +msgstr "" + +#: main.c +msgid "Woken up by alarm.\n" +msgstr "" + +#: ports/espressif/common-hal/_bleio/PacketBuffer.c +#: ports/nordic/common-hal/_bleio/PacketBuffer.c +msgid "Writes not supported on Characteristic" +msgstr "" + +#: ports/atmel-samd/boards/circuitplayground_express/mpconfigboard.h +#: ports/atmel-samd/boards/circuitplayground_express_crickit/mpconfigboard.h +#: ports/atmel-samd/boards/circuitplayground_express_displayio/mpconfigboard.h +#: ports/atmel-samd/boards/meowmeow/mpconfigboard.h +msgid "You pressed both buttons at start up." +msgstr "" + +#: ports/espressif/boards/m5stack_core_basic/mpconfigboard.h +#: ports/espressif/boards/m5stack_core_fire/mpconfigboard.h +#: ports/espressif/boards/m5stack_stick_c/mpconfigboard.h +#: ports/espressif/boards/m5stack_stick_c_plus/mpconfigboard.h +#: ports/espressif/boards/m5stack_stick_c_plus2/mpconfigboard.h +msgid "You pressed button A at start up." +msgstr "" + +#: ports/espressif/boards/m5stack_m5paper/mpconfigboard.h +msgid "You pressed button DOWN at start up." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "You pressed the BOOT button at start up" +msgstr "" + +#: ports/espressif/boards/adafruit_feather_esp32c6_4mbflash_nopsram/mpconfigboard.h +#: ports/espressif/boards/adafruit_itsybitsy_esp32/mpconfigboard.h +#: ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/mpconfigboard.h +msgid "You pressed the BOOT button at start up." +msgstr "" + +#: ports/espressif/boards/adafruit_huzzah32_breakout/mpconfigboard.h +msgid "You pressed the GPIO0 button at start up." +msgstr "" + +#: ports/espressif/boards/espressif_esp32_lyrat/mpconfigboard.h +msgid "You pressed the Rec button at start up." +msgstr "" + +#: ports/espressif/boards/adafruit_feather_esp32_v2/mpconfigboard.h +msgid "You pressed the SW38 button at start up." +msgstr "" + +#: ports/espressif/boards/hardkernel_odroid_go/mpconfigboard.h +#: ports/espressif/boards/vidi_x/mpconfigboard.h +msgid "You pressed the VOLUME button at start up." +msgstr "" + +#: ports/espressif/boards/m5stack_atom_echo/mpconfigboard.h +#: ports/espressif/boards/m5stack_atom_lite/mpconfigboard.h +#: ports/espressif/boards/m5stack_atom_matrix/mpconfigboard.h +#: ports/espressif/boards/m5stack_atom_u/mpconfigboard.h +msgid "You pressed the central button at start up." +msgstr "" + +#: ports/nordic/boards/aramcon2_badge/mpconfigboard.h +msgid "You pressed the left button at start up." +msgstr "" + +#: supervisor/shared/safe_mode.c +msgid "You pressed the reset button during boot." +msgstr "" + +#: supervisor/shared/micropython.c +msgid "[truncated due to length]" +msgstr "" + +#: py/objtype.c +msgid "__init__() should return None" +msgstr "" + +#: py/objtype.c +#, c-format +msgid "__init__() should return None, not '%s'" +msgstr "" + +#: py/objobject.c +msgid "__new__ arg must be a user-type" +msgstr "" + +#: extmod/modbinascii.c extmod/modhashlib.c py/objarray.c +msgid "a bytes-like object is required" +msgstr "" + +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + +#: shared-bindings/i2ctarget/I2CTarget.c +msgid "addresses is empty" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "already playing" +msgstr "" + +#: py/compile.c +msgid "annotation must be an identifier" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "arange: cannot compute length" +msgstr "" + +#: py/modbuiltins.c +msgid "arg is an empty sequence" +msgstr "" + +#: py/objobject.c +msgid "arg must be user-type" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "argsort argument must be an ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "argsort is not implemented for flattened arrays" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "argument must be None, an integer or a tuple of integers" +msgstr "" + +#: py/compile.c +msgid "argument name reused" +msgstr "" + +#: py/argcheck.c shared-bindings/_stage/__init__.c +#: shared-bindings/digitalio/DigitalInOut.c +msgid "argument num/types mismatch" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/numpy/transform.c +msgid "arguments must be ndarrays" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "array and index length must be equal" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "array has too many dimensions" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "array is too big" +msgstr "" + +#: py/objarray.c shared-bindings/alarm/SleepMemory.c +#: shared-bindings/memorymap/AddressRange.c shared-bindings/nvm/ByteArray.c +msgid "array/bytes required on right side" +msgstr "" + +#: py/asmxtensa.c +msgid "asm overflow" +msgstr "" + +#: py/compile.c +msgid "async for/with outside async function" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "attempt to get (arg)min/(arg)max of empty sequence" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "attempt to get argmin/argmax of an empty sequence" +msgstr "" + +#: py/objstr.c +msgid "attributes not supported" +msgstr "" + +#: ports/espressif/common-hal/audioio/AudioOut.c +msgid "audio format not supported" +msgstr "" + +#: extmod/ulab/code/ulab_tools.c +msgid "axis is out of bounds" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c +msgid "axis must be None, or an integer" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "axis too long" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "background value out of range of target" +msgstr "" + +#: py/builtinevex.c +msgid "bad compile mode" +msgstr "" + +#: py/objstr.c +msgid "bad conversion specifier" +msgstr "" + +#: py/objstr.c +msgid "bad format string" +msgstr "" + +#: py/binary.c py/objarray.c +msgid "bad typecode" +msgstr "" + +#: py/emitnative.c +msgid "binary op %q not implemented" +msgstr "" + +#: ports/espressif/common-hal/audiobusio/PDMIn.c +msgid "bit_depth must be 8, 16, 24, or 32." +msgstr "" + +#: shared-module/bitmapfilter/__init__.c +msgid "bitmap size and depth must match" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "bitmap sizes must match" +msgstr "" + +#: extmod/modrandom.c +msgid "bits must be 32 or less" +msgstr "" + +#: shared-bindings/audiofreeverb/Freeverb.c +msgid "bits_per_sample must be 16" +msgstr "" + +#: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c +#: shared-bindings/audiodelays/MultiTapDelay.c +#: shared-bindings/audiodelays/PitchShift.c +#: shared-bindings/audiofilters/Distortion.c +#: shared-bindings/audiofilters/Filter.c shared-bindings/audiofilters/Phaser.c +#: shared-bindings/audiomixer/Mixer.c +msgid "bits_per_sample must be 8 or 16" +msgstr "" + +#: py/emitinlinethumb.c +msgid "branch not in range" +msgstr "" + +#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c +msgid "buffer is smaller than requested size" +msgstr "" + +#: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c +msgid "buffer size must be a multiple of element size" +msgstr "" + +#: shared-module/struct/__init__.c +msgid "buffer size must match format" +msgstr "" + +#: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c +msgid "buffer slices must be of equal length" +msgstr "" + +#: py/modstruct.c shared-module/struct/__init__.c +msgid "buffer too small" +msgstr "" + +#: shared-bindings/socketpool/Socket.c shared-bindings/ssl/SSLSocket.c +msgid "buffer too small for requested bytes" +msgstr "" + +#: py/emitbc.c +msgid "bytecode overflow" +msgstr "" + +#: py/objarray.c +msgid "bytes length not a multiple of item size" +msgstr "" + +#: py/objstr.c +msgid "bytes value out of range" +msgstr "" + +#: ports/atmel-samd/bindings/samd/Clock.c +msgid "calibration is out of range" +msgstr "" + +#: ports/atmel-samd/bindings/samd/Clock.c +msgid "calibration is read only" +msgstr "" + +#: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c +#: shared-module/vectorio/Rectangle.c +msgid "can only have one parent" +msgstr "" + +#: py/emitinlinerv32.c +msgid "can only have up to 4 parameters for RV32 assembly" +msgstr "" + +#: py/emitinlinethumb.c +msgid "can only have up to 4 parameters to Thumb assembly" +msgstr "" + +#: py/emitinlinextensa.c +msgid "can only have up to 4 parameters to Xtensa assembly" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "can only specify one unknown dimension" +msgstr "" + +#: py/objtype.c +msgid "can't add special method to already-subclassed class" +msgstr "" + +#: py/compile.c +msgid "can't assign to expression" +msgstr "" + +#: extmod/modasyncio.c +msgid "can't cancel self" +msgstr "" + +#: py/obj.c shared-module/adafruit_pixelbuf/PixelBuf.c +msgid "can't convert %q to %q" +msgstr "" + +#: py/obj.c +#, c-format +msgid "can't convert %s to complex" +msgstr "" + +#: py/obj.c +#, c-format +msgid "can't convert %s to float" +msgstr "" + +#: py/objint.c py/runtime.c +#, c-format +msgid "can't convert %s to int" +msgstr "" + +#: py/objstr.c +msgid "can't convert '%q' object to %q implicitly" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "can't convert complex to float" +msgstr "" + +#: py/obj.c +msgid "can't convert to complex" +msgstr "" + +#: py/obj.c +msgid "can't convert to float" +msgstr "" + +#: py/runtime.c +msgid "can't convert to int" +msgstr "" + +#: py/objstr.c +msgid "can't convert to str implicitly" +msgstr "" + +#: py/objtype.c +msgid "can't create '%q' instances" +msgstr "" + +#: py/compile.c +msgid "can't declare nonlocal in outer code" +msgstr "" + +#: py/compile.c +msgid "can't delete expression" +msgstr "" + +#: py/emitnative.c +msgid "can't do binary op between '%q' and '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't do unary op of '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't implicitly convert '%q' to 'bool'" +msgstr "" + +#: py/runtime.c +msgid "can't import name %q" +msgstr "" + +#: py/emitnative.c +msgid "can't load from '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't load with '%q' index" +msgstr "" + +#: py/builtinimport.c +msgid "can't perform relative import" +msgstr "" + +#: py/objgenerator.c +msgid "can't send non-None value to a just-started generator" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "can't set 512 block size" +msgstr "" + +#: py/objexcept.c py/objnamedtuple.c +msgid "can't set attribute" +msgstr "" + +#: py/runtime.c +msgid "can't set attribute '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't store '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't store to '%q'" +msgstr "" + +#: py/emitnative.c +msgid "can't store with '%q' index" +msgstr "" + +#: py/objstr.c +msgid "" +"can't switch from automatic field numbering to manual field specification" +msgstr "" + +#: py/objstr.c +msgid "" +"can't switch from manual field specification to automatic field numbering" +msgstr "" + +#: py/objcomplex.c +msgid "can't truncate-divide a complex number" +msgstr "" + +#: extmod/modasyncio.c +msgid "can't wait" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot assign new shape" +msgstr "" + +#: extmod/ulab/code/ndarray_operators.c +msgid "cannot cast output with casting rule" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot convert complex to dtype" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot convert complex type" +msgstr "" + +#: py/objtype.c +msgid "cannot create instance" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot delete array elements" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "cannot reshape array" +msgstr "" + +#: py/emitnative.c +msgid "casting" +msgstr "" + +#: ports/stm/common-hal/pwmio/PWMOut.c +msgid "channel re-init" +msgstr "" + +#: shared-bindings/_stage/Text.c +msgid "chars buffer too small" +msgstr "" + +#: py/modbuiltins.c +msgid "chr() arg not in range(0x110000)" +msgstr "" + +#: py/modbuiltins.c +msgid "chr() arg not in range(256)" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "clip point must be (x,y) tuple" +msgstr "" + +#: shared-bindings/msgpack/ExtType.c +msgid "code outside range 0~127" +msgstr "" + +#: shared-bindings/displayio/Palette.c +msgid "color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)" +msgstr "" + +#: shared-bindings/displayio/Palette.c +msgid "color buffer must be a buffer, tuple, list, or int" +msgstr "" + +#: shared-bindings/displayio/Palette.c +msgid "color buffer must be a bytearray or array of type 'b' or 'B'" +msgstr "" + +#: shared-bindings/displayio/Palette.c +msgid "color must be between 0x000000 and 0xffffff" +msgstr "" + +#: py/emitnative.c +msgid "comparison of int and uint" +msgstr "" + +#: py/objcomplex.c +msgid "complex divide by zero" +msgstr "" + +#: py/objfloat.c py/parsenum.c +msgid "complex values not supported" +msgstr "" + +#: extmod/modzlib.c +msgid "compression header" +msgstr "" + +#: py/emitnative.c +msgid "conversion to object" +msgstr "" + +#: extmod/ulab/code/numpy/filter.c +msgid "convolve arguments must be linear arrays" +msgstr "" + +#: extmod/ulab/code/numpy/filter.c +msgid "convolve arguments must be ndarrays" +msgstr "" + +#: extmod/ulab/code/numpy/filter.c +msgid "convolve arguments must not be empty" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "corrupted file" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "could not invert Vandermonde matrix" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "couldn't determine SD card version" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "cross is defined for 1D arrays of length 3" +msgstr "" + +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c +msgid "cs pin must be 1-4 positions above mosi pin" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "data must be iterable" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "data must be of equal length" +msgstr "" + +#: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +#, c-format +msgid "data pin #%d in use" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "data type not understood" +msgstr "" + +#: py/parsenum.c +msgid "decimal numbers not supported" +msgstr "" + +#: py/compile.c +msgid "default 'except' must be last" +msgstr "" + +#: shared-bindings/msgpack/__init__.c +msgid "default is not a function" +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "" +"destination buffer must be a bytearray or array of type 'B' for bit_depth = 8" +msgstr "" + +#: shared-bindings/audiobusio/PDMIn.c +msgid "destination buffer must be an array of type 'H' for bit_depth = 16" +msgstr "" + +#: py/objdict.c +msgid "dict update sequence has wrong length" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "diff argument must be an ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "differentiation order out of range" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c +msgid "dimensions do not match" +msgstr "" + +#: py/emitnative.c +msgid "div/mod not implemented for uint" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "divide by zero" +msgstr "" + +#: py/runtime.c +msgid "division by zero" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "dtype must be float, or complex" +msgstr "" + +#: extmod/ulab/code/ndarray_operators.c +msgid "dtype of int32 is not supported" +msgstr "" + +#: py/objdeque.c +msgid "empty" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "empty file" +msgstr "" + +#: extmod/modasyncio.c extmod/modheapq.c +msgid "empty heap" +msgstr "" + +#: py/objstr.c +msgid "empty separator" +msgstr "" + +#: shared-bindings/random/__init__.c +msgid "empty sequence" +msgstr "" + +#: py/objstr.c +msgid "end of format while looking for conversion specifier" +msgstr "" + +#: shared-bindings/alarm/time/TimeAlarm.c +msgid "epoch_time not supported on this board" +msgstr "" + +#: ports/nordic/common-hal/busio/UART.c +#, c-format +msgid "error = 0x%08lX" +msgstr "" + +#: py/runtime.c +msgid "exceptions must derive from BaseException" +msgstr "" + +#: py/objstr.c +msgid "expected ':' after format specifier" +msgstr "" + +#: py/obj.c +msgid "expected tuple/list" +msgstr "" + +#: py/modthread.c +msgid "expecting a dict for keyword args" +msgstr "" + +#: py/compile.c +msgid "expecting an assembler instruction" +msgstr "" + +#: py/compile.c +msgid "expecting just a value for set" +msgstr "" + +#: py/compile.c +msgid "expecting key:value for dict" +msgstr "" + +#: shared-bindings/msgpack/__init__.c +msgid "ext_hook is not a function" +msgstr "" + +#: py/argcheck.c +msgid "extra keyword arguments given" +msgstr "" + +#: py/argcheck.c +msgid "extra positional arguments given" +msgstr "" + +#: shared-bindings/audiocore/WaveFile.c shared-bindings/audiomp3/MP3Decoder.c +#: shared-bindings/displayio/OnDiskBitmap.c shared-bindings/gifio/OnDiskGif.c +#: shared-bindings/synthio/__init__.c shared-module/gifio/GifWriter.c +msgid "file must be a file opened in byte mode" +msgstr "" + +#: shared-bindings/traceback/__init__.c +msgid "file write is not available" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "first argument must be a callable" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "first argument must be a function" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "first argument must be a tuple of ndarrays" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c extmod/ulab/code/numpy/vector.c +msgid "first argument must be an ndarray" +msgstr "" + +#: py/objtype.c +msgid "first argument to super() must be type" +msgstr "" + +#: extmod/ulab/code/scipy/linalg/linalg.c +msgid "first two arguments must be ndarrays" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "flattening order must be either 'C', or 'F'" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "flip argument must be an ndarray" +msgstr "" + +#: py/objint.c +msgid "float too big" +msgstr "" + +#: py/nativeglue.c +msgid "float unsupported" +msgstr "" + +#: extmod/moddeflate.c +msgid "format" +msgstr "" + +#: py/objstr.c +msgid "format needs a dict" +msgstr "" + +#: py/objstr.c +msgid "format string didn't convert all arguments" +msgstr "" + +#: py/objstr.c +msgid "format string needs more arguments" +msgstr "" + +#: py/objdeque.c +msgid "full" +msgstr "" + +#: py/argcheck.c +msgid "function doesn't take keyword arguments" +msgstr "" + +#: py/argcheck.c +#, c-format +msgid "function expected at most %d arguments, got %d" +msgstr "" + +#: py/bc.c py/objnamedtuple.c +msgid "function got multiple values for argument '%q'" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "function has the same sign at the ends of interval" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "function is defined for ndarrays only" +msgstr "" + +#: extmod/ulab/code/numpy/carray/carray.c +msgid "function is implemented for ndarrays only" +msgstr "" + +#: py/argcheck.c +#, c-format +msgid "function missing %d required positional arguments" +msgstr "" + +#: py/bc.c +msgid "function missing keyword-only argument" +msgstr "" + +#: py/bc.c +msgid "function missing required keyword argument '%q'" +msgstr "" + +#: py/bc.c +#, c-format +msgid "function missing required positional argument #%d" +msgstr "" + +#: py/argcheck.c py/bc.c py/objnamedtuple.c shared-bindings/_eve/__init__.c +#: shared-bindings/time/__init__.c +#, c-format +msgid "function takes %d positional arguments but %d were given" +msgstr "" + +#: shared-bindings/mcp4822/MCP4822.c +msgid "gain must be 1 or 2" +msgstr "" + +#: py/objgenerator.c +msgid "generator already executing" +msgstr "" + +#: py/objgenerator.c +msgid "generator ignored GeneratorExit" +msgstr "" + +#: py/objgenerator.c py/runtime.c +msgid "generator raised StopIteration" +msgstr "" + +#: extmod/modhashlib.c +msgid "hash is final" +msgstr "" + +#: extmod/modheapq.c +msgid "heap must be a list" +msgstr "" + +#: py/compile.c +msgid "identifier redefined as global" +msgstr "" + +#: py/compile.c +msgid "identifier redefined as nonlocal" +msgstr "" + +#: py/compile.c +msgid "import * not at module level" +msgstr "" + +#: py/persistentcode.c +msgid "incompatible .mpy arch" +msgstr "" + +#: py/persistentcode.c +msgid "incompatible .mpy file" +msgstr "" + +#: py/objstr.c +msgid "incomplete format" +msgstr "" + +#: py/objstr.c +msgid "incomplete format key" +msgstr "" + +#: extmod/modbinascii.c +msgid "incorrect padding" +msgstr "" + +#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/transform.c +msgid "index is out of bounds" +msgstr "" + +#: shared-bindings/_pixelmap/PixelMap.c +msgid "index must be tuple or int" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c +#: ports/espressif/common-hal/pulseio/PulseIn.c +#: shared-bindings/bitmaptools/__init__.c +msgid "index out of range" +msgstr "" + +#: py/obj.c +msgid "indices must be integers" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "indices must be integers, slices, or Boolean lists" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "initial values must be iterable" +msgstr "" + +#: py/compile.c +msgid "inline assembler must be a function" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "input and output dimensions differ" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "input and output shapes differ" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "input argument must be an integer, a tuple, or a list" +msgstr "" + +#: extmod/ulab/code/numpy/fft/fft_tools.c +msgid "input array length must be power of 2" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "input arrays are not compatible" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "input data must be an iterable" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "input dtype must be float or complex" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "input is not iterable" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "input matrix is asymmetric" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +#: extmod/ulab/code/scipy/linalg/linalg.c +msgid "input matrix is singular" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "input must be 1- or 2-d" +msgstr "" + +#: extmod/ulab/code/numpy/carray/carray.c +msgid "input must be a 1D ndarray" +msgstr "" + +#: extmod/ulab/code/scipy/linalg/linalg.c extmod/ulab/code/user/user.c +msgid "input must be a dense ndarray" +msgstr "" + +#: extmod/ulab/code/user/user.c shared-bindings/_eve/__init__.c +msgid "input must be an ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/carray/carray.c +msgid "input must be an ndarray, or a scalar" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "input must be one-dimensional" +msgstr "" + +#: extmod/ulab/code/ulab_tools.c +msgid "input must be square matrix" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "input must be tuple, list, range, or ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "input vectors must be of equal length" +msgstr "" + +#: extmod/ulab/code/numpy/approx.c +msgid "interp is defined for 1D iterables of equal length" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +#, c-format +msgid "interval must be in range %s-%s" +msgstr "" + +#: py/compile.c +msgid "invalid arch" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +#, c-format +msgid "invalid bits_per_pixel %d, must be, 1, 2, 4, 8, 16, 24, or 32" +msgstr "" + +#: shared-module/ssl/SSLSocket.c +msgid "invalid cert" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +#, c-format +msgid "invalid element size %d for bits_per_pixel %d\n" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +#, c-format +msgid "invalid element_size %d, must be, 1, 2, or 4" +msgstr "" + +#: shared-bindings/traceback/__init__.c +msgid "invalid exception" +msgstr "" + +#: py/objstr.c +msgid "invalid format specifier" +msgstr "" + +#: shared-bindings/wifi/Radio.c +msgid "invalid hostname" +msgstr "" + +#: shared-module/ssl/SSLSocket.c +msgid "invalid key" +msgstr "" + +#: py/compile.c +msgid "invalid micropython decorator" +msgstr "" + +#: ports/espressif/common-hal/espcamera/Camera.c +msgid "invalid setting" +msgstr "" + +#: shared-bindings/random/__init__.c +msgid "invalid step" +msgstr "" + +#: py/compile.c py/parse.c +msgid "invalid syntax" +msgstr "" + +#: py/parsenum.c +msgid "invalid syntax for integer" +msgstr "" + +#: py/parsenum.c +#, c-format +msgid "invalid syntax for integer with base %d" +msgstr "" + +#: py/parsenum.c +msgid "invalid syntax for number" +msgstr "" + +#: py/objtype.c +msgid "issubclass() arg 1 must be a class" +msgstr "" + +#: py/objtype.c +msgid "issubclass() arg 2 must be a class or a tuple of classes" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "iterations did not converge" +msgstr "" + +#: py/objstr.c +msgid "join expects a list of str/bytes objects consistent with self object" +msgstr "" + +#: py/argcheck.c +msgid "keyword argument(s) not implemented - use normal args instead" +msgstr "" + +#: py/emitinlinethumb.c py/emitinlinextensa.c +msgid "label '%q' not defined" +msgstr "" + +#: py/compile.c +msgid "label redefined" +msgstr "" + +#: py/objarray.c +msgid "lhs and rhs should be compatible" +msgstr "" + +#: py/emitnative.c +msgid "local '%q' has type '%q' but source is '%q'" +msgstr "" + +#: py/emitnative.c +msgid "local '%q' used before type known" +msgstr "" + +#: py/vm.c +msgid "local variable referenced before assignment" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +msgid "loopback + silent mode not supported by peripheral" +msgstr "" + +#: ports/espressif/common-hal/mdns/Server.c +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "mDNS already initialized" +msgstr "" + +#: ports/espressif/common-hal/mdns/Server.c +#: ports/raspberrypi/common-hal/mdns/Server.c +msgid "mDNS only works with built-in WiFi" +msgstr "" + +#: py/parse.c +msgid "malformed f-string" +msgstr "" + +#: shared-bindings/_stage/Layer.c +msgid "map buffer too small" +msgstr "" + +#: py/modmath.c shared-bindings/math/__init__.c +msgid "math domain error" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "matrix is not positive definite" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Descriptor.c +#: ports/nordic/common-hal/_bleio/Characteristic.c +#: ports/nordic/common-hal/_bleio/Descriptor.c +#, c-format +msgid "max_length must be 0-%d when fixed_length is %s" +msgstr "" + +#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/random/random.c +msgid "maximum number of dimensions is " +msgstr "" + +#: py/runtime.c +msgid "maximum recursion depth exceeded" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "maxiter must be > 0" +msgstr "" + +#: extmod/ulab/code/scipy/optimize/optimize.c +msgid "maxiter should be > 0" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "median argument must be an ndarray" +msgstr "" + +#: py/runtime.c +#, c-format +msgid "memory allocation failed, allocating %u bytes" +msgstr "" + +#: py/runtime.c +msgid "memory allocation failed, heap is locked" +msgstr "" + +#: py/objarray.c +msgid "memoryview offset too large" +msgstr "" + +#: py/objarray.c +msgid "memoryview: length is not a multiple of itemsize" +msgstr "" + +#: extmod/modtime.c +msgid "mktime needs a tuple of length 8 or 9" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "mode must be complete, or reduced" +msgstr "" + +#: py/builtinimport.c +msgid "module not found" +msgstr "" + +#: ports/espressif/common-hal/wifi/Monitor.c +msgid "monitor init failed" +msgstr "" + +#: extmod/ulab/code/numpy/poly.c +msgid "more degrees of freedom than data points" +msgstr "" + +#: py/compile.c +msgid "multiple *x in assignment" +msgstr "" + +#: py/objtype.c +msgid "multiple bases have instance lay-out conflict" +msgstr "" + +#: py/objtype.c +msgid "multiple inheritance not supported" +msgstr "" + +#: py/emitnative.c +msgid "must raise an object" +msgstr "" + +#: py/modbuiltins.c +msgid "must use keyword argument for key function" +msgstr "" + +#: py/runtime.c +msgid "name '%q' isn't defined" +msgstr "" + +#: py/runtime.c +msgid "name not defined" +msgstr "" + +#: py/qstr.c +msgid "name too long" +msgstr "" + +#: py/persistentcode.c +msgid "native code in .mpy unsupported" +msgstr "" + +#: py/asmthumb.c +msgid "native method too big" +msgstr "" + +#: py/emitnative.c +msgid "native yield" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "ndarray length overflows" +msgstr "" + +#: py/runtime.c +#, c-format +msgid "need more than %d values to unpack" +msgstr "" + +#: py/modmath.c +msgid "negative factorial" +msgstr "" + +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c +msgid "negative power with no float support" +msgstr "" + +#: py/objint_mpz.c py/runtime.c +msgid "negative shift count" +msgstr "" + +#: shared-bindings/_pixelmap/PixelMap.c +msgid "nested index must be int" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "no SD card" +msgstr "" + +#: py/vm.c +msgid "no active exception to reraise" +msgstr "" + +#: py/compile.c +msgid "no binding for nonlocal found" +msgstr "" + +#: shared-module/msgpack/__init__.c +msgid "no default packer" +msgstr "" + +#: extmod/modrandom.c extmod/ulab/code/numpy/random/random.c +msgid "no default seed" +msgstr "" + +#: py/builtinimport.c +msgid "no module named '%q'" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "no response from SD card" +msgstr "" + +#: ports/espressif/common-hal/espcamera/Camera.c py/objobject.c py/runtime.c +msgid "no such attribute" +msgstr "" + +#: ports/espressif/common-hal/_bleio/Connection.c +#: ports/nordic/common-hal/_bleio/Connection.c +msgid "non-UUID found in service_uuids_whitelist" +msgstr "" + +#: py/compile.c +msgid "non-default argument follows default argument" +msgstr "" + +#: py/objstr.c +msgid "non-hex digit" +msgstr "" + +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "non-zero timeout must be > 0.01" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "non-zero timeout must be >= interval" +msgstr "" + +#: shared-bindings/_bleio/UUID.c +msgid "not a 128-bit UUID" +msgstr "" + +#: py/parse.c +msgid "not a constant" +msgstr "" + +#: extmod/ulab/code/numpy/carray/carray_tools.c +msgid "not implemented for complex dtype" +msgstr "" + +#: extmod/ulab/code/numpy/bitwise.c +msgid "not supported for input types" +msgstr "" + +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "number of points must be at least 2" +msgstr "" + +#: py/builtinhelp.c +msgid "object " +msgstr "" + +#: py/obj.c +#, c-format +msgid "object '%s' isn't a tuple or list" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + +#: py/obj.c +msgid "object doesn't support item assignment" +msgstr "" + +#: py/obj.c +msgid "object doesn't support item deletion" +msgstr "" + +#: py/obj.c +msgid "object has no len" +msgstr "" + +#: py/obj.c +msgid "object isn't subscriptable" +msgstr "" + +#: py/runtime.c +msgid "object not an iterator" +msgstr "" + +#: py/objtype.c py/runtime.c +msgid "object not callable" +msgstr "" + +#: py/sequence.c shared-bindings/displayio/Group.c +msgid "object not in sequence" +msgstr "" + +#: py/runtime.c +msgid "object not iterable" +msgstr "" + +#: py/obj.c +#, c-format +msgid "object of type '%s' has no len()" +msgstr "" + +#: py/obj.c +msgid "object with buffer protocol required" +msgstr "" + +#: supervisor/shared/web_workflow/web_workflow.c +msgid "off" +msgstr "" + +#: extmod/ulab/code/utils/utils.c +msgid "offset is too large" +msgstr "" + +#: shared-bindings/dualbank/__init__.c +msgid "offset must be >= 0" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "offset must be non-negative and no greater than buffer length" +msgstr "" + +#: ports/nordic/common-hal/audiobusio/PDMIn.c +#: ports/stm/common-hal/audiobusio/PDMIn.c +msgid "only bit_depth=16 is supported" +msgstr "" + +#: ports/stm/common-hal/audiobusio/PDMIn.c +msgid "only mono is supported" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "only ndarrays can be concatenated" +msgstr "" + +#: ports/stm/common-hal/audiobusio/PDMIn.c +msgid "only oversample=64 is supported" +msgstr "" + +#: ports/nordic/common-hal/audiobusio/PDMIn.c +#: ports/stm/common-hal/audiobusio/PDMIn.c +msgid "only sample_rate=16000 is supported" +msgstr "" + +#: py/objarray.c py/objstr.c py/objstrunicode.c py/objtuple.c +#: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c +#: shared-bindings/nvm/ByteArray.c +msgid "only slices with step=1 (aka None) are supported" +msgstr "" + +#: py/vm.c +msgid "opcode" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: expecting %q" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: must not be zero" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: out of range" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: undefined label '%q'" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q' argument %d: unknown register" +msgstr "" + +#: py/emitinlinerv32.c +msgid "opcode '%q': expecting %d arguments" +msgstr "" + +#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/bitwise.c +#: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/vector.c +msgid "operands could not be broadcast together" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "operation is defined for 2D arrays only" +msgstr "" + +#: extmod/ulab/code/numpy/linalg/linalg.c +msgid "operation is defined for ndarrays only" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "operation is implemented for 1D Boolean arrays only" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "operation is not implemented on ndarrays" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "operation is not supported for given type" +msgstr "" + +#: extmod/ulab/code/ndarray_operators.c +msgid "operation not supported for the input types" +msgstr "" + +#: py/modbuiltins.c +msgid "ord expects a character" +msgstr "" + +#: py/modbuiltins.c +#, c-format +msgid "ord() expected a character, but string of length %d found" +msgstr "" + +#: extmod/ulab/code/utils/utils.c +msgid "out array is too small" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "out has wrong type" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "out keyword is not supported for complex dtype" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "out keyword is not supported for function" +msgstr "" + +#: extmod/ulab/code/utils/utils.c +msgid "out must be a float dense array" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "out must be an ndarray" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "out must be of float dtype" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "out of range of target" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "output array has wrong type" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "output array must be contiguous" +msgstr "" + +#: py/objint_mpz.c +msgid "overflow converting long int to machine word" +msgstr "" + +#: py/modstruct.c +#, c-format +msgid "pack expected %d items for packing (got %d)" +msgstr "" + +#: py/emitinlinerv32.c +msgid "parameters must be registers in sequence a0 to a3" +msgstr "" + +#: py/emitinlinextensa.c +msgid "parameters must be registers in sequence a2 to a5" +msgstr "" + +#: py/emitinlinethumb.c +msgid "parameters must be registers in sequence r0 to r3" +msgstr "" + +#: extmod/vfs_posix_file.c +msgid "poll on file not available on win32" +msgstr "" + +#: ports/espressif/common-hal/pulseio/PulseIn.c +msgid "pop from an empty PulseIn" +msgstr "" + +#: ports/atmel-samd/common-hal/pulseio/PulseIn.c +#: ports/cxd56/common-hal/pulseio/PulseIn.c +#: ports/nordic/common-hal/pulseio/PulseIn.c +#: ports/raspberrypi/common-hal/pulseio/PulseIn.c +#: ports/stm/common-hal/pulseio/PulseIn.c py/objdict.c py/objlist.c py/objset.c +#: shared-bindings/ps2io/Ps2.c +msgid "pop from empty %q" +msgstr "" + +#: shared-bindings/socketpool/Socket.c +msgid "port must be >= 0" +msgstr "" + +#: py/compile.c +msgid "positional arg after **" +msgstr "" + +#: py/compile.c +msgid "positional arg after keyword arg" +msgstr "" + +#: py/objint_mpz.c +msgid "pow() 3rd argument cannot be 0" +msgstr "" + +#: py/objint_mpz.c +msgid "pow() with 3 arguments requires integers" +msgstr "" + +#: ports/raspberrypi/common-hal/rp2pio/StateMachine.c +msgid "pull masks conflict with direction masks" +msgstr "" + +#: extmod/ulab/code/numpy/fft/fft_tools.c +msgid "real and imaginary parts must be of equal length" +msgstr "" + +#: py/builtinimport.c +msgid "relative import" +msgstr "" + +#: py/obj.c +#, c-format +msgid "requested length %d but object has length %d" +msgstr "" + +#: extmod/ulab/code/ndarray_operators.c +msgid "results cannot be cast to specified type" +msgstr "" + +#: py/compile.c +msgid "return annotation must be an identifier" +msgstr "" + +#: py/emitnative.c +msgid "return expected '%q' but got '%q'" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "rgb_pins[%d] duplicates another pin assignment" +msgstr "" + +#: shared-bindings/rgbmatrix/RGBMatrix.c +#, c-format +msgid "rgb_pins[%d] is not on the same port as clock" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "roll argument must be an ndarray" +msgstr "" + +#: py/objstr.c +msgid "rsplit(None,n)" +msgstr "" + +#: shared-bindings/audiofreeverb/Freeverb.c +msgid "samples_signed must be true" +msgstr "" + +#: ports/atmel-samd/common-hal/audiobusio/PDMIn.c +#: ports/raspberrypi/common-hal/audiobusio/PDMIn.c +msgid "sampling rate out of range" +msgstr "" + +#: py/modmicropython.c +msgid "schedule queue full" +msgstr "" + +#: py/builtinimport.c +msgid "script compilation not supported" +msgstr "" + +#: py/nativeglue.c +msgid "set unsupported" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "shape must be None, and integer or a tuple of integers" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "shape must be integer or tuple of integers" +msgstr "" + +#: shared-module/msgpack/__init__.c +msgid "short read" +msgstr "" + +#: py/objstr.c +msgid "sign not allowed in string format specifier" +msgstr "" + +#: py/objstr.c +msgid "sign not allowed with integer format specifier 'c'" +msgstr "" + +#: extmod/ulab/code/ulab_tools.c +msgid "size is defined for ndarrays only" +msgstr "" + +#: extmod/ulab/code/numpy/random/random.c +msgid "size must match out.shape when used together" +msgstr "" + +#: py/nativeglue.c +msgid "slice unsupported" +msgstr "" + +#: py/objint.c py/sequence.c +msgid "small int overflow" +msgstr "" + +#: main.c +msgid "soft reboot\n" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "sort argument must be an ndarray" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "sos array must be of shape (n_section, 6)" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "sos[:, 3] should be all ones" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "sosfilt requires iterable arguments" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "source palette too large" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "source_bitmap must have value_count of 2 or 65536" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "source_bitmap must have value_count of 65536" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "source_bitmap must have value_count of 8" +msgstr "" + +#: extmod/modre.c +msgid "splitting with sub-captures" +msgstr "" + +#: shared-bindings/random/__init__.c +msgid "stop not reachable from start" +msgstr "" + +#: py/stream.c shared-bindings/getpass/__init__.c +msgid "stream operation not supported" +msgstr "" + +#: py/objarray.c py/objstr.c +msgid "string argument without an encoding" +msgstr "" + +#: py/objstrunicode.c +msgid "string index out of range" +msgstr "" + +#: py/objstrunicode.c +#, c-format +msgid "string indices must be integers, not %s" +msgstr "" + +#: py/objarray.c py/objstr.c +msgid "substring not found" +msgstr "" + +#: py/compile.c +msgid "super() can't find self" +msgstr "" + +#: extmod/modjson.c +msgid "syntax error in JSON" +msgstr "" + +#: extmod/modtime.c +msgid "ticks interval overflow" +msgstr "" + +#: ports/nordic/common-hal/watchdog/WatchDogTimer.c +msgid "timeout duration exceeded the maximum supported value" +msgstr "" + +#: ports/nordic/common-hal/_bleio/Adapter.c +msgid "timeout must be < 655.35 secs" +msgstr "" + +#: ports/raspberrypi/common-hal/floppyio/__init__.c +msgid "timeout waiting for flux" +msgstr "" + +#: ports/raspberrypi/common-hal/floppyio/__init__.c +#: shared-module/floppyio/__init__.c +msgid "timeout waiting for index pulse" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "timeout waiting for v1 card" +msgstr "" + +#: shared-module/sdcardio/SDCard.c +msgid "timeout waiting for v2 card" +msgstr "" + +#: ports/stm/common-hal/pwmio/PWMOut.c +msgid "timer re-init" +msgstr "" + +#: shared-bindings/time/__init__.c +msgid "timestamp out of range for platform time_t" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "tobytes can be invoked for dense arrays only" +msgstr "" + +#: py/compile.c +msgid "too many args" +msgstr "" + +#: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/create.c +msgid "too many dimensions" +msgstr "" + +#: extmod/ulab/code/ndarray.c +msgid "too many indices" +msgstr "" + +#: py/asmthumb.c +msgid "too many locals for native method" +msgstr "" + +#: py/runtime.c +#, c-format +msgid "too many values to unpack (expected %d)" +msgstr "" + +#: extmod/ulab/code/numpy/approx.c +msgid "trapz is defined for 1D arrays of equal length" +msgstr "" + +#: extmod/ulab/code/numpy/approx.c +msgid "trapz is defined for 1D iterables" +msgstr "" + +#: py/obj.c +msgid "tuple/list has wrong length" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +#, c-format +msgid "twai_driver_install returned esp-idf error #%d" +msgstr "" + +#: ports/espressif/common-hal/canio/CAN.c +#, c-format +msgid "twai_start returned esp-idf error #%d" +msgstr "" + +#: shared-bindings/busio/UART.c shared-bindings/canio/CAN.c +msgid "tx and rx cannot both be None" +msgstr "" + +#: py/objtype.c +msgid "type '%q' isn't an acceptable base type" +msgstr "" + +#: py/objtype.c +msgid "type isn't an acceptable base type" +msgstr "" + +#: py/runtime.c +msgid "type object '%q' has no attribute '%q'" +msgstr "" + +#: py/objtype.c +msgid "type takes 1 or 3 arguments" +msgstr "" + +#: py/objint_longlong.c +msgid "ulonglong too large" +msgstr "" + +#: py/parse.c +msgid "unexpected indent" +msgstr "" + +#: py/bc.c +msgid "unexpected keyword argument" +msgstr "" + +#: py/argcheck.c py/bc.c py/objnamedtuple.c +#: shared-bindings/traceback/__init__.c +msgid "unexpected keyword argument '%q'" +msgstr "" + +#: py/lexer.c +msgid "unicode name escapes" +msgstr "" + +#: py/parse.c +msgid "unindent doesn't match any outer indent level" +msgstr "" + +#: py/emitinlinerv32.c +msgid "unknown RV32 instruction '%q'" +msgstr "" + +#: py/objstr.c +#, c-format +msgid "unknown conversion specifier %c" +msgstr "" + +#: py/objstr.c +msgid "unknown format code '%c' for object of type '%q'" +msgstr "" + +#: py/compile.c +msgid "unknown type" +msgstr "" + +#: py/compile.c +msgid "unknown type '%q'" +msgstr "" + +#: py/objstr.c +#, c-format +msgid "unmatched '%c' in format" +msgstr "" + +#: py/objtype.c py/runtime.c +msgid "unreadable attribute" +msgstr "" + +#: shared-bindings/displayio/TileGrid.c shared-bindings/terminalio/Terminal.c +#: shared-bindings/tilepalettemapper/TilePaletteMapper.c +#: shared-bindings/vectorio/VectorShape.c +msgid "unsupported %q type" +msgstr "" + +#: py/emitinlinethumb.c +#, c-format +msgid "unsupported Thumb instruction '%s' with %d arguments" +msgstr "" + +#: py/emitinlinextensa.c +#, c-format +msgid "unsupported Xtensa instruction '%s' with %d arguments" +msgstr "" + +#: shared-module/bitmapfilter/__init__.c +msgid "unsupported bitmap depth" +msgstr "" + +#: shared-module/gifio/GifWriter.c +msgid "unsupported colorspace for GifWriter" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "unsupported colorspace for dither" +msgstr "" + +#: py/objstr.c +#, c-format +msgid "unsupported format character '%c' (0x%x) at index %d" +msgstr "" + +#: py/runtime.c +msgid "unsupported type for %q: '%s'" +msgstr "" + +#: py/runtime.c +msgid "unsupported type for operator" +msgstr "" + +#: py/runtime.c +msgid "unsupported types for %q: '%q', '%q'" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "usecols is too high" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "usecols keyword must be specified" +msgstr "" + +#: py/objint.c +#, c-format +msgid "value must fit in %d byte(s)" +msgstr "" + +#: shared-bindings/bitmaptools/__init__.c +msgid "value out of range of target" +msgstr "" + +#: extmod/moddeflate.c +msgid "wbits" +msgstr "" + +#: shared-bindings/bitmapfilter/__init__.c +msgid "" +"weights must be a sequence with an odd square number of elements (usually 9 " +"or 25)" +msgstr "" + +#: shared-bindings/bitmapfilter/__init__.c +msgid "weights must be an object of type %q, %q, %q, or %q, not %q " +msgstr "" + +#: shared-bindings/is31fl3741/FrameBuffer.c +msgid "width must be greater than zero" +msgstr "" + +#: ports/raspberrypi/common-hal/wifi/Monitor.c +msgid "wifi.Monitor not available" +msgstr "" + +#: shared-bindings/_bleio/Adapter.c +msgid "window must be <= interval" +msgstr "" + +#: extmod/ulab/code/numpy/numerical.c +msgid "wrong axis index" +msgstr "" + +#: extmod/ulab/code/numpy/create.c +msgid "wrong axis specified" +msgstr "" + +#: extmod/ulab/code/numpy/io/io.c +msgid "wrong dtype" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c +msgid "wrong index type" +msgstr "" + +#: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/io/io.c extmod/ulab/code/numpy/transform.c +#: extmod/ulab/code/numpy/vector.c +msgid "wrong input type" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c +msgid "wrong length of condition array" +msgstr "" + +#: extmod/ulab/code/numpy/transform.c +msgid "wrong length of index array" +msgstr "" + +#: extmod/ulab/code/numpy/create.c py/objarray.c py/objstr.c +msgid "wrong number of arguments" +msgstr "" + +#: py/runtime.c +msgid "wrong number of values to unpack" +msgstr "" + +#: extmod/ulab/code/numpy/vector.c +msgid "wrong output type" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "zi must be an ndarray" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "zi must be of float type" +msgstr "" + +#: extmod/ulab/code/scipy/signal/signal.c +msgid "zi must be of shape (n_section, 2)" +msgstr "" From a20f1f9401519b427c5b57b7723fd28cd23a8133 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 16:58:33 -0700 Subject: [PATCH 456/517] mtm_computer: set DAC gain 2x --- ports/raspberrypi/boards/mtm_computer/board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index 9065ffebcf831..666d584af2c81 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -20,7 +20,7 @@ static mp_obj_t board_dac_factory(void) { &pin_GPIO18, // clock (SCK) &pin_GPIO19, // mosi (SDI) &pin_GPIO21, // cs - 1); // gain 1x + 2); // gain 2x return MP_OBJ_FROM_PTR(dac); } MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); From 2793baa87b5fb251a282e0c02b5234246026b2d5 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 23 Mar 2026 18:26:08 -0700 Subject: [PATCH 457/517] Revert "mtm_computer: set DAC gain 2x" This reverts commit 8bf51dbc821e051301a9a7057e6f2ea1a8a559fe. --- ports/raspberrypi/boards/mtm_computer/board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index 666d584af2c81..9065ffebcf831 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -20,7 +20,7 @@ static mp_obj_t board_dac_factory(void) { &pin_GPIO18, // clock (SCK) &pin_GPIO19, // mosi (SDI) &pin_GPIO21, // cs - 2); // gain 2x + 1); // gain 1x return MP_OBJ_FROM_PTR(dac); } MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); From 32165ce455ca39acbefd7c1513d1bbd854215e10 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 09:51:01 -0700 Subject: [PATCH 458/517] fix zephyr builds by running update_boardnfo.py --- .../adafruit/feather_nrf52840_zephyr/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml | 1 + .../zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml | 1 + .../zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml | 1 + .../boards/renesas/da14695_dk_usb/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml | 1 + 17 files changed, 17 insertions(+) diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 9712f467858eb..277386cd1daf3 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 80b1b4ebf7d6a..dbf98f107686b 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 1bc1b96e6cf5c..06f944e3e90c9 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 7869cca4fafba..1e0cc856db166 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index c2233ddf8b544..a43e3169adabd 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index a1e8de8822b49..90203c4c0ecd1 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index b50b1966ed074..8f05a53587ecd 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 21d55194a1c1c..09f5cc6c58035 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index 90f84ab1586c4..e641099db7693 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index eb5db066c893c..d94b69eb81314 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index d6efa285fe2a4..0874538a40858 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 7600b8bbd151a..b8657eb040a69 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 8e49b95d33416..04e75b39eee0d 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index b28a9481c72d9..e881b8221900f 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 6b0ef8d8480f1..6bdc58b12afed 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index b6f03f3d627c6..ba745440b8dfc 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index 8d1fd9253488b..dd571f0283448 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false From b66baecb2614acee05c2740e3752d33248c2c9d0 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 14:46:04 -0700 Subject: [PATCH 459/517] mcp4822 gain argument fix, as per requested --- shared-bindings/mcp4822/MCP4822.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/shared-bindings/mcp4822/MCP4822.c b/shared-bindings/mcp4822/MCP4822.c index c48f5426e6690..f192caf4052a6 100644 --- a/shared-bindings/mcp4822/MCP4822.c +++ b/shared-bindings/mcp4822/MCP4822.c @@ -81,11 +81,7 @@ static mp_obj_t mcp4822_mcp4822_make_new(const mp_obj_type_t *type, size_t n_arg const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); - - mp_int_t gain = args[ARG_gain].u_int; - if (gain != 1 && gain != 2) { - mp_raise_ValueError(MP_ERROR_TEXT("gain must be 1 or 2")); - } + const mp_int_t gain = mp_arg_validate_int_range(args[ARG_gain].u_int, 1, 2, MP_QSTR_gain); mcp4822_mcp4822_obj_t *self = mp_obj_malloc_with_finaliser(mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); common_hal_mcp4822_mcp4822_construct(self, clock, mosi, cs, (uint8_t)gain); From 16285c158db2225188217b927bb81df704b87619 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 14:46:57 -0700 Subject: [PATCH 460/517] mcp4822 gain argument fix, as per requested --- locale/circuitpython.pot | 4 ---- 1 file changed, 4 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 5d0be48fe3a79..8bfc2d5bde31f 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -3310,10 +3310,6 @@ msgstr "" msgid "function takes %d positional arguments but %d were given" msgstr "" -#: shared-bindings/mcp4822/MCP4822.c -msgid "gain must be 1 or 2" -msgstr "" - #: py/objgenerator.c msgid "generator already executing" msgstr "" From afb64e0f3c913b69596ef3c722cf9becf7a9bf52 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 14:56:30 -0700 Subject: [PATCH 461/517] mtm_computer: make board.DAC() an actual singleton --- ports/raspberrypi/boards/mtm_computer/board.c | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index 9065ffebcf831..0b1e34eaacd00 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -12,16 +12,22 @@ // board.DAC() — factory function that constructs an mcp4822.MCP4822 with // the MTM Workshop Computer's DAC pins (GP18=SCK, GP19=SDI, GP21=CS). + +static mp_obj_t board_dac_singleton = MP_OBJ_NULL; + static mp_obj_t board_dac_factory(void) { - mcp4822_mcp4822_obj_t *dac = mp_obj_malloc_with_finaliser( - mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); - common_hal_mcp4822_mcp4822_construct( - dac, - &pin_GPIO18, // clock (SCK) - &pin_GPIO19, // mosi (SDI) - &pin_GPIO21, // cs - 1); // gain 1x - return MP_OBJ_FROM_PTR(dac); + if (board_dac_singleton == MP_OBJ_NULL) { + mcp4822_mcp4822_obj_t *dac = mp_obj_malloc_with_finaliser( + mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); + common_hal_mcp4822_mcp4822_construct( + dac, + &pin_GPIO18, // clock (SCK) + &pin_GPIO19, // mosi (SDI) + &pin_GPIO21, // cs + 1); // gain 1x + board_dac_singleton = MP_OBJ_FROM_PTR(dac); + } + return board_dac_singleton; } MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); From 072c27785580ddd8db1144164718a4edddc322e6 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 15:21:02 -0700 Subject: [PATCH 462/517] mtm_computer: make sure board.DAC() gets deallocated on board deinit --- ports/raspberrypi/boards/mtm_computer/board.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index 0b1e34eaacd00..4fec0cd5b8b59 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -31,4 +31,10 @@ static mp_obj_t board_dac_factory(void) { } MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); +void board_deinit(void) { + if (board_dac_singleton != MP_OBJ_NULL) { + common_hal_mcp4822_mcp4822_deinit(MP_OBJ_TO_PTR(board_dac_singleton)); + board_dac_singleton = MP_OBJ_NULL; + } +} // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. From 8309a23932fd0f67c1b64d7a25687bd4c5536190 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 15:21:28 -0700 Subject: [PATCH 463/517] mcp4822 CS/MOSI argument fix, as per requested --- ports/raspberrypi/common-hal/mcp4822/MCP4822.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ports/raspberrypi/common-hal/mcp4822/MCP4822.c b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c index cb6cddb8343ed..7a8ad7d4df29c 100644 --- a/ports/raspberrypi/common-hal/mcp4822/MCP4822.c +++ b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c @@ -143,8 +143,7 @@ void common_hal_mcp4822_mcp4822_construct(mcp4822_mcp4822_obj_t *self, // The SET pin group spans from MOSI to CS. // MOSI must have a lower GPIO number than CS, gap at most 4. if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { - mp_raise_ValueError( - MP_ERROR_TEXT("cs pin must be 1-4 positions above mosi pin")); + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q and %q"), MP_QSTR_CS, MP_QSTR_MOSI); } uint8_t set_count = cs->number - mosi->number + 1; From 74aee24aa9bee0ff61324d062cc09181620fe5aa Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Tue, 24 Mar 2026 15:21:55 -0700 Subject: [PATCH 464/517] mcp4822 CS/MOSI argument fix, as per requested --- locale/circuitpython.pot | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 8bfc2d5bde31f..57c0a13e991f7 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1297,6 +1297,7 @@ msgstr "" msgid "Invalid %q" msgstr "" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -3040,10 +3041,6 @@ msgstr "" msgid "cross is defined for 1D arrays of length 3" msgstr "" -#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c -msgid "cs pin must be 1-4 positions above mosi pin" -msgstr "" - #: extmod/ulab/code/scipy/optimize/optimize.c msgid "data must be iterable" msgstr "" From 0f690b7009c08a3854a7f6e1b3a5b485c9848f55 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Wed, 25 Mar 2026 09:22:27 -0700 Subject: [PATCH 465/517] fix zephyr builds by running update_boardnfo.py --- ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml index a8c03d5a4e8f5..cb705c48af51a 100644 --- a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -64,6 +64,7 @@ locale = false lvfontio = true # Zephyr board has busio math = false max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false From 14c1815654f59ac9088faf5b801047db88f680e1 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 27 Mar 2026 13:49:25 -0400 Subject: [PATCH 466/517] wip --- .gitattributes | 1 + LICENSE_MicroPython | 2 +- docs/library/collections.rst | 16 + docs/library/platform.rst | 8 + docs/library/re.rst | 14 +- docs/library/sys.rst | 18 +- docs/reference/glossary.rst | 7 + extmod/cyw43_config_common.h | 126 +++ extmod/extmod.mk | 4 +- extmod/littlefs-include/lfs2_defines.h | 12 + extmod/lwip-include/arch/cc.h | 41 - extmod/lwip-include/arch/perf.h | 7 - extmod/lwip-include/lwipopts.h | 36 - extmod/lwip-include/lwipopts_common.h | 3 + extmod/modjson.c | 3 +- extmod/modplatform.c | 38 + extmod/modre.c | 48 +- extmod/modtime.c | 30 +- extmod/vfs.c | 2 +- extmod/vfs_blockdev.c | 2 +- extmod/vfs_fat.c | 73 +- extmod/vfs_lfsx.c | 16 +- extmod/vfs_lfsx_file.c | 4 +- extmod/vfs_posix.c | 19 +- extmod/vfs_reader.c | 2 +- lib/berkeley-db-1.xx | 2 +- lib/libm_dbl/libm.h | 2 + lib/libm_dbl/rint.c | 2 +- lib/littlefs/lfs2.c | 870 +++++++++++------- lib/littlefs/lfs2.h | 100 +- lib/littlefs/lfs2_util.c | 3 + lib/littlefs/lfs2_util.h | 56 +- lib/micropython-lib | 2 +- main.c | 4 +- mpy-cross/main.c | 100 +- mpy-cross/mpconfigport.h | 25 +- mpy-cross/mpy_cross/__init__.py | 2 +- mpy-cross/mpy_cross/__main__.py | 1 - ports/analog/supervisor/port.c | 2 +- ports/atmel-samd/common-hal/alarm/__init__.c | 2 +- .../common-hal/alarm/touch/TouchAlarm.c | 2 +- ports/atmel-samd/reset.c | 2 +- ports/atmel-samd/reset.h | 4 +- ports/atmel-samd/supervisor/port.c | 2 +- ports/espressif/bindings/espidf/__init__.c | 2 +- ports/espressif/bindings/espidf/__init__.h | 4 +- ports/espressif/common-hal/alarm/__init__.c | 2 +- .../common-hal/microcontroller/__init__.c | 2 +- ports/espressif/supervisor/port.c | 2 +- ports/mimxrt10xx/reset.h | 4 +- ports/nordic/common-hal/alarm/__init__.c | 2 +- ports/raspberrypi/common-hal/alarm/__init__.c | 2 +- ports/raspberrypi/common-hal/wifi/Radio.c | 2 +- ports/raspberrypi/common-hal/wifi/__init__.h | 2 +- ports/raspberrypi/supervisor/port.c | 2 +- ports/stm/common-hal/alarm/__init__.c | 2 +- ports/stm/supervisor/port.c | 2 +- ports/unix/Makefile | 34 +- ports/unix/README.md | 26 + ports/unix/coverage.c | 133 ++- ports/unix/coveragecpp.cpp | 51 + ports/unix/input.c | 3 + ports/unix/main.c | 181 ++-- ports/unix/modtime.c | 12 +- ports/unix/mpconfigport.h | 30 +- ports/unix/mpconfigport.mk | 1 + ports/unix/mphalport.h | 3 + ports/unix/mpthreadport.c | 41 +- ports/unix/stack_size.h | 54 ++ .../unix/variants/coverage/mpconfigvariant.h | 11 + .../unix/variants/longlong/mpconfigvariant.h | 33 +- .../unix/variants/longlong/mpconfigvariant.mk | 8 + ports/unix/variants/minimal/mpconfigvariant.h | 4 +- ports/unix/variants/mpconfigvariant_common.h | 8 +- ports/unix/variants/nanbox/mpconfigvariant.h | 7 - py/argcheck.c | 6 +- py/asmarm.c | 114 ++- py/asmarm.h | 61 +- py/asmbase.c | 4 +- py/asmrv32.c | 137 +-- py/asmrv32.h | 83 +- py/asmthumb.c | 122 ++- py/asmthumb.h | 128 ++- py/asmx64.h | 37 +- py/asmx86.h | 37 +- py/asmxtensa.c | 178 +++- py/asmxtensa.h | 85 +- py/bc.c | 2 +- py/bc.h | 43 +- py/binary.c | 39 +- py/builtin.h | 1 + py/builtinhelp.c | 5 +- py/builtinimport.c | 33 +- py/circuitpy_mpconfig.h | 2 +- py/compile.c | 16 +- py/cstack.h | 4 +- py/dynruntime.h | 6 +- py/dynruntime.mk | 13 +- py/emitcommon.c | 17 +- py/emitglue.c | 4 + py/emitinlinerv32.c | 422 ++++----- py/emitinlinextensa.c | 211 ++++- py/emitnative.c | 263 ++---- py/emitndebug.c | 12 +- py/formatfloat.c | 759 ++++++++------- py/formatfloat.h | 1 + py/gc.c | 4 +- py/gc.h | 5 + py/make_root_pointers.py | 2 - py/makecompresseddata.py | 2 - py/makemoduledefs.py | 18 +- py/makeqstrdata.py | 23 +- py/makeqstrdefs.py | 4 +- py/makeversionhdr.py | 21 +- py/malloc.c | 56 +- py/misc.h | 164 +++- py/mkrules.cmake | 52 ++ py/mkrules.mk | 13 +- py/modio.c | 9 +- py/modmath.c | 13 +- py/modmicropython.c | 2 +- py/modsys.c | 34 +- py/mpconfig.h | 298 +++++- py/mphal.h | 1 + py/mpprint.c | 93 +- py/mpprint.h | 18 +- py/mpstate.h | 6 + py/mpz.c | 16 +- py/nativeglue.h | 2 +- py/nlr.c | 2 +- py/nlr.h | 6 +- py/nlraarch64.c | 2 +- py/nlrmips.c | 2 +- py/nlrpowerpc.c | 4 +- py/nlrrv32.c | 2 +- py/nlrrv64.c | 2 +- py/nlrthumb.c | 2 +- py/nlrx64.c | 2 +- py/nlrx86.c | 2 +- py/nlrxtensa.c | 2 +- py/obj.c | 32 +- py/obj.h | 56 +- py/objarray.c | 37 +- py/objboundmeth.c | 4 + py/objcell.c | 2 +- py/objcode.c | 71 ++ py/objcode.h | 5 +- py/objcomplex.c | 31 +- py/objdict.c | 5 +- py/objfloat.c | 34 +- py/objint.c | 13 +- py/objint_longlong.c | 78 +- py/objint_mpz.c | 7 +- py/objlist.c | 160 ++-- py/objmodule.c | 22 +- py/objmodule.h | 3 + py/objnamedtuple.c | 2 +- py/objrange.c | 14 +- py/objringio.c | 19 +- py/objstr.c | 19 +- py/objtuple.c | 3 - py/objtuple.h | 5 +- py/objtype.c | 98 +- py/parse.c | 81 +- py/parsenum.c | 336 ++++--- py/parsenum.h | 5 + py/persistentcode.c | 48 +- py/persistentcode.h | 7 +- py/profile.c | 2 +- py/py.cmake | 1 + py/py.mk | 2 +- py/repl.c | 11 +- py/runtime.c | 158 ++-- py/runtime.h | 117 ++- py/runtime_utils.c | 56 ++ py/scheduler.c | 39 +- py/showbc.c | 14 +- py/smallint.c | 26 - py/smallint.h | 1 - py/stream.c | 55 +- py/stream.h | 3 +- py/vm.c | 33 +- pyproject.toml | 120 ++- shared-bindings/_bleio/__init__.c | 6 +- shared-bindings/_bleio/__init__.h | 6 +- shared-bindings/adafruit_pixelbuf/PixelBuf.c | 2 +- shared-bindings/alarm/__init__.h | 2 +- shared-bindings/math/__init__.c | 2 +- shared-bindings/memorymonitor/__init__.c | 2 +- shared-bindings/memorymonitor/__init__.h | 2 +- shared-bindings/microcontroller/Pin.c | 6 +- shared-bindings/microcontroller/Pin.h | 6 +- shared-bindings/microcontroller/__init__.h | 2 +- shared-bindings/paralleldisplaybus/__init__.c | 3 - shared-bindings/socketpool/SocketPool.c | 2 +- shared-bindings/socketpool/SocketPool.h | 2 +- shared-bindings/storage/__init__.h | 2 +- shared-bindings/usb/core/__init__.c | 4 +- shared-bindings/usb/core/__init__.h | 4 +- shared-bindings/util.h | 2 +- shared-module/ssl/SSLSocket.c | 2 +- shared-module/struct/__init__.c | 3 +- shared/libc/abort_.c | 4 +- shared/memzip/make-memzip.py | 2 - shared/netutils/dhcpserver.c | 315 ------- shared/netutils/netutils.c | 100 -- shared/netutils/netutils.h | 58 -- shared/netutils/trace.c | 170 ---- shared/runtime/mpirq.c | 40 +- shared/runtime/mpirq.h | 1 + shared/runtime/pyexec.c | 45 +- shared/runtime/pyexec.h | 11 + shared/timeutils/timeutils.c | 134 ++- shared/timeutils/timeutils.h | 119 ++- supervisor/port.h | 4 +- supervisor/shared/safe_mode.h | 2 +- tests/README.md | 27 +- tests/basics/annotate_var.py.exp | 5 - tests/basics/array_add.py | 6 + tests/basics/assign_expr.py.exp | 16 - tests/basics/assign_expr_scope.py.exp | 23 - tests/basics/async_await.py.exp | 32 - tests/basics/async_await2.py.exp | 5 - tests/basics/async_def.py.exp | 3 - tests/basics/async_for.py.exp | 51 - tests/basics/async_for2.py | 2 +- tests/basics/async_for2.py.exp | 32 - tests/basics/async_syntaxerror.py.exp | 2 - tests/basics/async_with.py.exp | 11 - tests/basics/async_with2.py | 2 +- tests/basics/async_with2.py.exp | 17 - tests/basics/async_with_break.py.exp | 15 - tests/basics/async_with_return.py.exp | 15 - tests/basics/attrtuple2.py | 25 + tests/basics/boundmeth1.py | 2 +- tests/basics/builtin_help.py | 6 + tests/basics/builtin_pow3_intbig.py | 5 + tests/basics/builtin_range.py | 18 +- tests/basics/builtin_range_maxsize.py | 38 + tests/basics/builtin_setattr.py | 2 +- tests/basics/builtin_slice.py | 37 +- tests/basics/bytes_format_modulo.py.exp | 5 - tests/basics/class_bind_self.py | 2 +- tests/basics/class_descriptor.py | 20 +- tests/basics/class_inplace_op2.py.exp | 12 - tests/basics/class_ordereddict.py.exp | 1 - tests/basics/class_setname_hazard.py | 182 ++++ tests/basics/class_setname_hazard_rand.py | 111 +++ tests/basics/del_attr.py | 2 +- tests/basics/del_global.py | 2 +- tests/basics/del_name.py | 2 +- tests/basics/dict_views.py | 5 + tests/basics/exception_chain.py | 7 + tests/basics/frozenset_set.py | 2 +- tests/basics/fun_code_colines.py | 81 ++ tests/basics/fun_code_colines.py.exp | 20 + tests/basics/fun_code_full.py | 40 + tests/basics/fun_code_full.py.exp | 9 + tests/basics/fun_code_lnotab.py | 34 + tests/basics/fun_name.py | 2 +- tests/basics/gc1.py | 4 +- tests/basics/import_instance_method.py | 38 + tests/basics/int_64_basics.py | 161 ++++ tests/basics/int_big_to_small.py | 11 + tests/basics/int_big_to_small_int29.py | 22 + tests/basics/int_big_to_small_int29.py.exp | 25 + tests/basics/io_buffered_writer.py | 31 +- tests/basics/io_buffered_writer.py.exp | 5 + tests/basics/io_bytesio_cow.py | 8 +- tests/basics/io_bytesio_ext.py | 8 +- tests/basics/io_bytesio_ext2.py | 7 +- tests/basics/io_iobase.py | 10 +- tests/basics/io_stringio1.py | 7 +- tests/basics/io_stringio_base.py | 6 +- tests/basics/io_stringio_with.py | 7 +- tests/basics/io_write_ext.py | 7 +- tests/basics/list_pop.py | 2 +- tests/basics/module2.py | 2 +- tests/basics/namedtuple1.py | 3 + tests/basics/parser.py | 2 +- tests/basics/python34.py | 2 +- tests/basics/python36.py.exp | 5 - tests/basics/self_type_check.py | 8 + tests/basics/slice_optimise.py | 23 + tests/basics/slice_optimise.py.exp | 2 + tests/basics/special_methods2.py.exp | 19 - tests/basics/string_format.py | 10 + tests/basics/string_format_sep.py | 9 + tests/basics/string_fstring_debug.py.exp | 9 - tests/basics/subclass_native_init.py | 4 + tests/basics/syntaxerror.py | 4 +- tests/basics/sys1.py | 6 + tests/basics/sys_tracebacklimit.py | 2 +- tests/basics/sys_tracebacklimit.py.native.exp | 22 + tests/basics/try_finally_continue.py.exp | 9 - tests/basics/tuple1.py | 2 +- tests/cmdline/cmd_compile_only.py | 13 + tests/cmdline/cmd_compile_only.py.exp | 1 + tests/cmdline/cmd_compile_only_error.py | 6 + tests/cmdline/cmd_compile_only_error.py.exp | 1 + tests/cmdline/cmd_file_variable.py | 5 + tests/cmdline/cmd_file_variable.py.exp | 1 + tests/cmdline/cmd_module_atexit.py | 16 + tests/cmdline/cmd_module_atexit.py.exp | 3 + tests/cmdline/cmd_module_atexit_exc.py | 19 + tests/cmdline/cmd_module_atexit_exc.py.exp | 3 + tests/cmdline/cmd_sys_exit_0.py | 5 + tests/cmdline/cmd_sys_exit_0.py.exp | 1 + tests/cmdline/cmd_sys_exit_error.py | 5 + tests/cmdline/cmd_sys_exit_error.py.exp | 1 + tests/cmdline/cmd_sys_exit_none.py | 5 + tests/cmdline/cmd_sys_exit_none.py.exp | 1 + tests/cmdline/repl_autocomplete_underscore.py | 33 + .../repl_autocomplete_underscore.py.exp | 41 + tests/cmdline/repl_lock.py | 7 + tests/cmdline/repl_lock.py.exp | 10 + tests/cmdline/repl_paste.py | 90 ++ tests/cmdline/repl_paste.py.exp | 133 +++ tests/cpydiff/core_class_initsubclass.py | 21 + ...core_class_initsubclass_autoclassmethod.py | 31 + .../cpydiff/core_class_initsubclass_kwargs.py | 22 + .../cpydiff/core_class_initsubclass_super.py | 22 + tests/cpydiff/core_fstring_concat.py | 2 +- tests/cpydiff/core_fstring_parser.py | 2 +- tests/cpydiff/core_fstring_repr.py | 2 +- tests/cpydiff/core_import_all.py | 10 - tests/cpydiff/modules3/__init__.py | 1 - tests/cpydiff/modules3/foo.py | 2 - tests/cpydiff/modules_errno_enotsup.py | 10 + tests/cpydiff/modules_struct_fewargs.py | 2 +- tests/cpydiff/modules_struct_manyargs.py | 2 +- .../modules_struct_whitespace_in_format.py | 2 +- tests/cpydiff/syntax_arg_unpacking.py | 2 +- tests/cpydiff/syntax_literal_underscore.py | 19 + tests/cpydiff/syntax_spaces.py | 19 +- tests/cpydiff/types_complex_parser.py | 14 + .../types_float_implicit_conversion.py | 2 +- tests/cpydiff/types_float_rounding.py | 8 - tests/cpydiff/types_oserror_errnomap.py | 48 + tests/cpydiff/types_range_limits.py | 26 + tests/cpydiff/types_str_formatsep.py | 19 + tests/cpydiff/types_str_formatsep_float.py | 11 + tests/extmod/asyncio_basic.py.exp | 6 - tests/extmod/asyncio_event_queue.py | 64 ++ tests/extmod/asyncio_event_queue.py.exp | 5 + tests/extmod/asyncio_heaplock.py | 12 +- tests/extmod/asyncio_iterator_event.py | 86 ++ tests/extmod/asyncio_iterator_event.py.exp | 5 + tests/extmod/asyncio_lock.py.exp | 41 - tests/extmod/asyncio_set_exception_handler.py | 2 +- tests/extmod/asyncio_wait_for_linked_task.py | 66 ++ tests/extmod/asyncio_wait_task.py.exp | 12 - tests/extmod/binascii_hexlify.py | 8 +- tests/extmod/binascii_unhexlify.py | 8 +- tests/extmod/framebuf_blit.py | 68 ++ tests/extmod/framebuf_blit.py.exp | 45 + tests/extmod/hashlib_md5.py | 3 +- tests/extmod/hashlib_sha1.py | 3 +- tests/extmod/hashlib_sha256.py | 3 +- tests/extmod/json_dump.py | 4 +- tests/extmod/json_dump_iobase.py | 2 +- tests/extmod/json_dump_separators.py | 6 +- tests/extmod/json_dumps_extra.py | 2 +- tests/extmod/json_dumps_separators.py | 2 +- tests/extmod/json_loads.py | 24 + tests/extmod/json_loads_bytes.py.exp | 2 - tests/extmod/json_loads_int_64.py | 16 + tests/extmod/machine_hard_timer.py | 45 + tests/extmod/machine_hard_timer.py.exp | 16 + tests/extmod/machine_timer.py | 48 + tests/extmod/machine_timer.py.exp | 16 + tests/extmod/platform_basic.py | 8 + tests/extmod/random_extra_float.py | 8 +- tests/extmod/re_error.py | 2 +- tests/extmod/re_start_end_pos.py | 78 ++ tests/extmod/re_sub.py | 2 +- tests/extmod/re_sub_unmatched.py.exp | 1 - tests/extmod/socket_badconstructor.py | 22 + tests/extmod/socket_fileno.py | 17 + tests/extmod/time_mktime.py | 120 +++ tests/extmod/time_res.py | 7 +- tests/extmod/tls_dtls.py | 12 +- tests/extmod/tls_dtls.py.exp | 1 + tests/extmod/{ssl_noleak.py => tls_noleak.py} | 0 tests/extmod/tls_threads.py | 58 ++ tests/extmod/uctypes_addressof.py | 7 +- tests/extmod/uctypes_array_load_store.py | 7 + tests/extmod/vfs_blockdev_invalid.py | 8 +- tests/extmod/vfs_blockdev_invalid.py.exp | 24 +- tests/extmod/vfs_fat_ilistdir_del.py | 3 +- tests/extmod/vfs_lfs.py | 2 +- tests/extmod/vfs_lfs_error.py | 46 +- tests/extmod/vfs_lfs_error.py.exp | 44 +- tests/extmod/vfs_lfs_ilistdir_del.py | 10 +- tests/extmod/vfs_mountinfo.py | 1 - tests/extmod/vfs_posix.py | 16 +- tests/extmod/vfs_posix_ilistdir_del.py | 3 +- tests/extmod/vfs_posix_paths.py | 2 +- tests/extmod/vfs_posix_paths.py.exp | 2 +- tests/extmod/vfs_posix_readonly.py | 99 ++ tests/extmod/vfs_posix_readonly.py.exp | 7 + tests/extmod/vfs_rom.py | 3 +- tests/extmod/websocket_toobig.py | 28 + tests/extmod/websocket_toobig.py.exp | 1 + tests/extmod_hardware/machine_counter.py | 90 ++ tests/extmod_hardware/machine_encoder.py | 153 +++ tests/extmod_hardware/machine_i2c_target.py | 307 ++++++ tests/feature_check/float.py | 13 - tests/feature_check/float.py.exp | 1 - tests/feature_check/inlineasm_rv32_zba.py | 10 + tests/feature_check/inlineasm_rv32_zba.py.exp | 1 + tests/feature_check/int_64.py | 2 + tests/feature_check/int_64.py.exp | 1 + tests/feature_check/io_module.py | 6 - tests/feature_check/io_module.py.exp | 0 tests/feature_check/repl_emacs_check.py.exp | 2 +- .../repl_words_move_check.py.exp | 2 +- tests/feature_check/target_info.py | 20 +- tests/float/cmath_fun.py | 3 + tests/float/float_array.py | 8 +- tests/float/float_format.py | 17 +- tests/float/float_format_accuracy.py | 73 ++ tests/float/float_format_ints.py | 32 +- tests/float/float_parse_doubleprec.py | 6 + tests/float/float_struct_e.py | 2 +- tests/float/float_struct_e_doubleprec.py | 43 + tests/float/float_struct_e_fp30.py | 43 + tests/float/int_64_float.py | 25 + tests/float/math_constants.py | 27 +- tests/float/math_constants_extra.py | 3 + tests/float/math_fun_special.py | 5 + tests/float/string_format2.py | 4 +- tests/float/string_format_fp30.py | 42 - tests/float/string_format_modulo.py | 2 +- tests/float/string_format_modulo3.py | 2 +- tests/float/string_format_modulo3.py.exp | 2 - tests/frozen/frozentest.mpy | Bin 196 -> 200 bytes tests/frozen/frozentest.py | 2 +- tests/import/builtin_ext.py | 6 + tests/import/builtin_import.py | 9 + tests/import/import_broken.py | 6 + tests/import/import_file.py | 4 + tests/import/import_override.py | 4 + tests/import/import_override.py.exp | 2 - tests/import/import_override2.py | 18 + tests/import/import_pkg7.py.exp | 8 - tests/import/import_pkg9.py | 2 +- tests/import/import_star.py | 59 ++ tests/import/import_star_error.py | 5 + tests/import/module_getattr.py.exp | 1 - tests/import/pkg7/subpkg1/subpkg2/mod3.py | 4 +- tests/import/pkgstar_all_array/__init__.py | 49 + tests/import/pkgstar_all_array/funcs.py | 2 + tests/import/pkgstar_all_inval/__init__.py | 1 + tests/import/pkgstar_all_miss/__init__.py | 8 + tests/import/pkgstar_all_tuple/__init__.py | 22 + tests/import/pkgstar_default/__init__.py | 20 + tests/inlineasm/rv32/asmzba.py | 18 + tests/inlineasm/rv32/asmzba.py.exp | 3 + tests/inlineasm/thumb/asmerrors.py | 4 + tests/inlineasm/thumb/asmerrors.py.exp | 1 + tests/inlineasm/xtensa/asmargs.py | 44 + tests/inlineasm/xtensa/asmargs.py.exp | 5 + tests/inlineasm/xtensa/asmarith.py | 119 +++ tests/inlineasm/xtensa/asmarith.py.exp | 40 + tests/inlineasm/xtensa/asmbranch.py | 299 ++++++ tests/inlineasm/xtensa/asmbranch.py.exp | 66 ++ tests/inlineasm/xtensa/asmjump.py | 26 + tests/inlineasm/xtensa/asmjump.py.exp | 2 + tests/inlineasm/xtensa/asmloadstore.py | 98 ++ tests/inlineasm/xtensa/asmloadstore.py.exp | 9 + tests/inlineasm/xtensa/asmmisc.py | 25 + tests/inlineasm/xtensa/asmmisc.py.exp | 3 + tests/inlineasm/xtensa/asmshift.py | 137 +++ tests/inlineasm/xtensa/asmshift.py.exp | 17 + tests/internal_bench/class_create-0-empty.py | 11 + tests/internal_bench/class_create-1-slots.py | 12 + .../internal_bench/class_create-1.1-slots5.py | 12 + .../class_create-2-classattr.py | 11 + .../class_create-2.1-classattr5.py | 15 + .../class_create-2.3-classattr5objs.py | 20 + .../class_create-3-instancemethod.py | 12 + .../class_create-4-classmethod.py | 13 + .../class_create-4.1-classmethod_implicit.py | 12 + .../class_create-5-staticmethod.py | 13 + .../class_create-6-getattribute.py | 12 + .../class_create-6.1-getattr.py | 12 + .../class_create-6.2-property.py | 13 + .../class_create-6.3-descriptor.py | 17 + .../internal_bench/class_create-7-inherit.py | 14 + .../class_create-7.1-inherit_initsubclass.py | 16 + .../class_create-8-metaclass_setname.py | 17 + .../class_create-8.1-metaclass_setname5.py | 21 + .../internal_bench/class_instance-0-object.py | 11 + .../class_instance-0.1-object-gc.py | 13 + .../internal_bench/class_instance-1-empty.py | 13 + .../class_instance-1.1-classattr.py | 13 + .../internal_bench/class_instance-1.2-func.py | 14 + .../class_instance-1.3-empty-gc.py | 15 + tests/internal_bench/class_instance-2-init.py | 14 + .../class_instance-2.1-init_super.py | 14 + .../internal_bench/class_instance-2.2-new.py | 14 + tests/internal_bench/class_instance-3-del.py | 14 + .../class_instance-3.1-del-gc.py | 16 + .../internal_bench/class_instance-4-slots.py | 13 + .../class_instance-4.1-slots5.py | 13 + .../var-6.2-instance-speciallookup.py | 19 + .../var-6.3-instance-property.py | 17 + .../var-6.4-instance-descriptor.py | 20 + .../var-6.5-instance-getattr.py | 16 + .../var-6.6-instance-builtin_ordered.py | 12 + tests/internal_bench/var-9-getattr.py | 16 + .../internal_bench/var-9.1-getattr_default.py | 15 + .../var-9.2-getattr_default_special.py | 16 + tests/internal_bench/var-9.3-except_ok.py | 23 + .../var-9.4-except_selfinduced.py | 22 + tests/internal_bench/var-9.5-except_error.py | 22 + .../var-9.6-except_error_special.py | 23 + tests/io/builtin_print_file.py | 2 +- tests/io/file_seek.py | 2 +- tests/micropython/builtin_execfile.py | 21 + tests/micropython/builtin_execfile.py.exp | 3 + tests/micropython/const_error.py | 2 - tests/micropython/const_error.py.exp | 2 - tests/micropython/const_float.py | 23 + tests/micropython/const_float.py.exp | 4 + tests/micropython/const_math.py | 18 + tests/micropython/const_math.py.exp | 1 + tests/micropython/decorator_error.py | 2 +- tests/micropython/emg_exc.py | 3 +- tests/micropython/emg_exc.py.native.exp | 2 + tests/micropython/extreme_exc.py | 6 +- tests/micropython/heap_lock.py | 6 +- tests/micropython/heap_locked.py | 6 +- tests/micropython/heapalloc.py | 6 +- tests/micropython/heapalloc_exc_compressed.py | 8 +- .../heapalloc_exc_compressed_emg_exc.py | 8 +- tests/micropython/heapalloc_exc_raise.py | 8 +- tests/micropython/heapalloc_fail_bytearray.py | 8 +- tests/micropython/heapalloc_fail_dict.py | 8 +- tests/micropython/heapalloc_fail_list.py | 8 +- .../micropython/heapalloc_fail_memoryview.py | 8 +- tests/micropython/heapalloc_fail_set.py | 8 +- tests/micropython/heapalloc_fail_tuple.py | 8 +- tests/micropython/heapalloc_inst_call.py | 9 +- tests/micropython/heapalloc_int_from_bytes.py | 9 +- tests/micropython/heapalloc_slice.py | 18 + tests/micropython/heapalloc_str.py | 9 +- tests/micropython/heapalloc_super.py | 9 +- .../heapalloc_traceback.py.native.exp | 3 + tests/micropython/heapalloc_yield_from.py | 8 +- tests/micropython/import_mpy_native.py | 5 +- tests/micropython/import_mpy_native_gc.py | 10 +- tests/micropython/kbd_intr.py | 6 +- tests/micropython/meminfo.py | 18 +- tests/micropython/memstats.py | 24 +- tests/micropython/opt_level.py | 8 +- tests/micropython/opt_level_lineno.py | 9 +- tests/micropython/opt_level_lineno.py.exp | 2 +- tests/micropython/ringio_big.py | 29 + tests/micropython/ringio_big.py.exp | 2 + tests/micropython/schedule.py | 6 +- tests/micropython/stack_use.py | 12 +- tests/micropython/test_normalize_newlines.py | 14 + .../test_normalize_newlines.py.exp | 8 + tests/micropython/viper_large_jump.py | 20 + tests/micropython/viper_large_jump.py.exp | 1 + .../micropython/viper_ptr16_load_boundary.py | 39 + .../viper_ptr16_load_boundary.py.exp | 20 + .../micropython/viper_ptr16_store_boundary.py | 63 ++ .../viper_ptr16_store_boundary.py.exp | 28 + .../micropython/viper_ptr32_load_boundary.py | 39 + .../viper_ptr32_load_boundary.py.exp | 20 + .../micropython/viper_ptr32_store_boundary.py | 68 ++ .../viper_ptr32_store_boundary.py.exp | 28 + tests/micropython/viper_ptr8_load_boundary.py | 38 + .../viper_ptr8_load_boundary.py.exp | 20 + .../micropython/viper_ptr8_store_boundary.py | 63 ++ .../viper_ptr8_store_boundary.py.exp | 28 + tests/misc/non_compliant.py | 6 +- tests/misc/non_compliant_lexer.py | 2 +- tests/misc/print_exception.py.native.exp | 18 + tests/misc/rge_sm.py | 59 +- tests/misc/sys_exc_info.py | 5 +- tests/misc/sys_settrace_cov.py | 23 + tests/misc/sys_settrace_cov.py.exp | 2 + tests/multi_extmod/machine_i2c_target_irq.py | 137 +++ .../machine_i2c_target_irq.py.exp | 15 + .../multi_extmod/machine_i2c_target_memory.py | 79 ++ .../machine_i2c_target_memory.py.exp | 16 + tests/perf_bench/bm_fft.py | 2 +- tests/perf_bench/bm_pidigits.py | 6 + tests/perf_bench/core_import_mpy_multi.py | 4 +- tests/perf_bench/core_import_mpy_single.py | 4 +- tests/run-internalbench.py | 101 +- tests/run-multitests.py | 93 +- tests/run-natmodtests.py | 88 +- tests/run-perfbench.py | 71 +- tests/run-tests.py | 742 ++++++++++----- tests/serial_test.py | 338 +++++++ tests/stress/bytecode_limit.py | 18 +- tests/stress/bytecode_limit.py.exp | 2 +- tests/stress/dict_copy.py | 7 +- tests/stress/dict_create.py | 6 +- tests/stress/fun_call_limit.py | 10 +- tests/stress/fun_call_limit.py.exp | 2 +- tests/stress/qstr_limit.py | 42 +- tests/stress/qstr_limit.py.exp | 55 +- tests/stress/qstr_limit_str_modulo.py | 21 + tests/stress/qstr_limit_str_modulo.py.exp | 5 + tests/stress/recursive_iternext.py | 64 +- tests/target_wiring/EK_RA6M2.py | 8 + tests/target_wiring/NUCLEO_WB55.py | 8 + tests/target_wiring/PYBx.py | 8 + tests/target_wiring/ZEPHYR_NUCLEO_WB55RG.py | 7 + tests/target_wiring/alif.py | 7 + tests/target_wiring/esp32.py | 12 + tests/target_wiring/mimxrt.py | 7 + tests/target_wiring/nrf.py | 7 + tests/target_wiring/rp2.py | 7 + tests/target_wiring/samd.py | 7 + tests/thread/mutate_bytearray.py | 3 +- tests/thread/mutate_dict.py | 3 +- tests/thread/mutate_instance.py | 3 +- tests/thread/mutate_list.py | 3 +- tests/thread/mutate_set.py | 3 +- tests/thread/stress_aes.py | 2 +- tests/thread/stress_recurse.py | 3 +- tests/thread/stress_schedule.py | 4 +- tests/thread/thread_coop.py | 3 + tests/thread/thread_exc1.py | 3 +- tests/thread/thread_exc2.py.native.exp | 3 + tests/thread/thread_gc1.py | 3 +- tests/thread/thread_ident1.py | 3 +- tests/thread/thread_lock3.py | 3 +- ...thread_lock4.py => thread_lock4_intbig.py} | 0 tests/thread/thread_shared1.py | 3 +- tests/thread/thread_shared2.py | 3 +- tests/thread/thread_stacksize1.py | 3 +- tests/thread/thread_stdin.py | 3 +- tests/unix/extra_coverage.py | 35 +- tests/unix/extra_coverage.py.exp | 63 +- tests/unix/ffi_float2.py | 3 +- tests/unix/ffi_float2.py.exp | 12 +- tests/unix/mod_os.py | 3 + tests/unix/time_mktime_localtime.py | 6 +- tools/boardgen.py | 8 +- tools/cc1 | 90 +- tools/ci.sh | 469 +++++++--- tools/codeformat.py | 2 +- tools/file2h.py | 2 - tools/insert-usb-ids.py | 2 - tools/makemanifest.py | 1 - tools/manifestfile.py | 1 - tools/metrics.py | 63 +- tools/mpy-tool.py | 450 ++++++++- tools/mpy_ld.py | 95 +- tools/pyboard.py | 72 +- tools/pydfu.py | 8 +- tools/verifygitlog.py | 31 +- 660 files changed, 14475 insertions(+), 5156 deletions(-) create mode 100644 extmod/cyw43_config_common.h create mode 100644 extmod/littlefs-include/lfs2_defines.h delete mode 100644 extmod/lwip-include/arch/cc.h delete mode 100644 extmod/lwip-include/arch/perf.h delete mode 100644 extmod/lwip-include/lwipopts.h create mode 100644 ports/unix/stack_size.h rename shared/netutils/dhcpserver.h => ports/unix/variants/longlong/mpconfigvariant.h (60%) create mode 100644 ports/unix/variants/longlong/mpconfigvariant.mk delete mode 100644 shared/netutils/dhcpserver.c delete mode 100644 shared/netutils/netutils.c delete mode 100644 shared/netutils/netutils.h delete mode 100644 shared/netutils/trace.c delete mode 100644 tests/basics/annotate_var.py.exp delete mode 100644 tests/basics/assign_expr.py.exp delete mode 100644 tests/basics/assign_expr_scope.py.exp delete mode 100644 tests/basics/async_await.py.exp delete mode 100644 tests/basics/async_await2.py.exp delete mode 100644 tests/basics/async_def.py.exp delete mode 100644 tests/basics/async_for.py.exp delete mode 100644 tests/basics/async_for2.py.exp delete mode 100644 tests/basics/async_syntaxerror.py.exp delete mode 100644 tests/basics/async_with.py.exp delete mode 100644 tests/basics/async_with2.py.exp delete mode 100644 tests/basics/async_with_break.py.exp delete mode 100644 tests/basics/async_with_return.py.exp create mode 100644 tests/basics/attrtuple2.py create mode 100644 tests/basics/builtin_range_maxsize.py delete mode 100644 tests/basics/bytes_format_modulo.py.exp delete mode 100644 tests/basics/class_inplace_op2.py.exp delete mode 100644 tests/basics/class_ordereddict.py.exp create mode 100644 tests/basics/class_setname_hazard.py create mode 100644 tests/basics/class_setname_hazard_rand.py create mode 100644 tests/basics/fun_code_colines.py create mode 100644 tests/basics/fun_code_colines.py.exp create mode 100644 tests/basics/fun_code_full.py create mode 100644 tests/basics/fun_code_full.py.exp create mode 100644 tests/basics/fun_code_lnotab.py create mode 100644 tests/basics/import_instance_method.py create mode 100644 tests/basics/int_64_basics.py create mode 100644 tests/basics/int_big_to_small_int29.py create mode 100644 tests/basics/int_big_to_small_int29.py.exp delete mode 100644 tests/basics/python36.py.exp create mode 100644 tests/basics/slice_optimise.py create mode 100644 tests/basics/slice_optimise.py.exp delete mode 100644 tests/basics/special_methods2.py.exp create mode 100644 tests/basics/string_format_sep.py delete mode 100644 tests/basics/string_fstring_debug.py.exp create mode 100644 tests/basics/sys_tracebacklimit.py.native.exp delete mode 100644 tests/basics/try_finally_continue.py.exp create mode 100644 tests/cmdline/cmd_compile_only.py create mode 100644 tests/cmdline/cmd_compile_only.py.exp create mode 100644 tests/cmdline/cmd_compile_only_error.py create mode 100644 tests/cmdline/cmd_compile_only_error.py.exp create mode 100644 tests/cmdline/cmd_file_variable.py create mode 100644 tests/cmdline/cmd_file_variable.py.exp create mode 100644 tests/cmdline/cmd_module_atexit.py create mode 100644 tests/cmdline/cmd_module_atexit.py.exp create mode 100644 tests/cmdline/cmd_module_atexit_exc.py create mode 100644 tests/cmdline/cmd_module_atexit_exc.py.exp create mode 100644 tests/cmdline/cmd_sys_exit_0.py create mode 100644 tests/cmdline/cmd_sys_exit_0.py.exp create mode 100644 tests/cmdline/cmd_sys_exit_error.py create mode 100644 tests/cmdline/cmd_sys_exit_error.py.exp create mode 100644 tests/cmdline/cmd_sys_exit_none.py create mode 100644 tests/cmdline/cmd_sys_exit_none.py.exp create mode 100644 tests/cmdline/repl_autocomplete_underscore.py create mode 100644 tests/cmdline/repl_autocomplete_underscore.py.exp create mode 100644 tests/cmdline/repl_lock.py create mode 100644 tests/cmdline/repl_lock.py.exp create mode 100644 tests/cmdline/repl_paste.py create mode 100644 tests/cmdline/repl_paste.py.exp create mode 100644 tests/cpydiff/core_class_initsubclass.py create mode 100644 tests/cpydiff/core_class_initsubclass_autoclassmethod.py create mode 100644 tests/cpydiff/core_class_initsubclass_kwargs.py create mode 100644 tests/cpydiff/core_class_initsubclass_super.py delete mode 100644 tests/cpydiff/core_import_all.py delete mode 100644 tests/cpydiff/modules3/__init__.py delete mode 100644 tests/cpydiff/modules3/foo.py create mode 100644 tests/cpydiff/modules_errno_enotsup.py create mode 100644 tests/cpydiff/syntax_literal_underscore.py create mode 100644 tests/cpydiff/types_complex_parser.py delete mode 100644 tests/cpydiff/types_float_rounding.py create mode 100644 tests/cpydiff/types_oserror_errnomap.py create mode 100644 tests/cpydiff/types_range_limits.py create mode 100644 tests/cpydiff/types_str_formatsep.py create mode 100644 tests/cpydiff/types_str_formatsep_float.py delete mode 100644 tests/extmod/asyncio_basic.py.exp create mode 100644 tests/extmod/asyncio_event_queue.py create mode 100644 tests/extmod/asyncio_event_queue.py.exp create mode 100644 tests/extmod/asyncio_iterator_event.py create mode 100644 tests/extmod/asyncio_iterator_event.py.exp delete mode 100644 tests/extmod/asyncio_lock.py.exp create mode 100644 tests/extmod/asyncio_wait_for_linked_task.py delete mode 100644 tests/extmod/asyncio_wait_task.py.exp create mode 100644 tests/extmod/framebuf_blit.py create mode 100644 tests/extmod/framebuf_blit.py.exp delete mode 100644 tests/extmod/json_loads_bytes.py.exp create mode 100644 tests/extmod/json_loads_int_64.py create mode 100644 tests/extmod/machine_hard_timer.py create mode 100644 tests/extmod/machine_hard_timer.py.exp create mode 100644 tests/extmod/machine_timer.py create mode 100644 tests/extmod/machine_timer.py.exp create mode 100644 tests/extmod/platform_basic.py create mode 100644 tests/extmod/re_start_end_pos.py delete mode 100644 tests/extmod/re_sub_unmatched.py.exp create mode 100644 tests/extmod/socket_badconstructor.py create mode 100644 tests/extmod/socket_fileno.py create mode 100644 tests/extmod/time_mktime.py rename tests/extmod/{ssl_noleak.py => tls_noleak.py} (100%) create mode 100644 tests/extmod/tls_threads.py create mode 100644 tests/extmod/vfs_posix_readonly.py create mode 100644 tests/extmod/vfs_posix_readonly.py.exp create mode 100644 tests/extmod/websocket_toobig.py create mode 100644 tests/extmod/websocket_toobig.py.exp create mode 100644 tests/extmod_hardware/machine_counter.py create mode 100644 tests/extmod_hardware/machine_encoder.py create mode 100644 tests/extmod_hardware/machine_i2c_target.py delete mode 100644 tests/feature_check/float.py delete mode 100644 tests/feature_check/float.py.exp create mode 100644 tests/feature_check/inlineasm_rv32_zba.py create mode 100644 tests/feature_check/inlineasm_rv32_zba.py.exp create mode 100644 tests/feature_check/int_64.py create mode 100644 tests/feature_check/int_64.py.exp delete mode 100644 tests/feature_check/io_module.py delete mode 100644 tests/feature_check/io_module.py.exp create mode 100644 tests/float/float_format_accuracy.py create mode 100644 tests/float/float_struct_e_doubleprec.py create mode 100644 tests/float/float_struct_e_fp30.py create mode 100644 tests/float/int_64_float.py delete mode 100644 tests/float/string_format_fp30.py delete mode 100644 tests/float/string_format_modulo3.py.exp delete mode 100644 tests/import/import_override.py.exp create mode 100644 tests/import/import_override2.py delete mode 100644 tests/import/import_pkg7.py.exp create mode 100644 tests/import/import_star.py delete mode 100644 tests/import/module_getattr.py.exp create mode 100644 tests/import/pkgstar_all_array/__init__.py create mode 100644 tests/import/pkgstar_all_array/funcs.py create mode 100644 tests/import/pkgstar_all_inval/__init__.py create mode 100644 tests/import/pkgstar_all_miss/__init__.py create mode 100644 tests/import/pkgstar_all_tuple/__init__.py create mode 100644 tests/import/pkgstar_default/__init__.py create mode 100644 tests/inlineasm/rv32/asmzba.py create mode 100644 tests/inlineasm/rv32/asmzba.py.exp create mode 100644 tests/inlineasm/thumb/asmerrors.py create mode 100644 tests/inlineasm/thumb/asmerrors.py.exp create mode 100644 tests/inlineasm/xtensa/asmargs.py create mode 100644 tests/inlineasm/xtensa/asmargs.py.exp create mode 100644 tests/inlineasm/xtensa/asmarith.py create mode 100644 tests/inlineasm/xtensa/asmarith.py.exp create mode 100644 tests/inlineasm/xtensa/asmbranch.py create mode 100644 tests/inlineasm/xtensa/asmbranch.py.exp create mode 100644 tests/inlineasm/xtensa/asmjump.py create mode 100644 tests/inlineasm/xtensa/asmjump.py.exp create mode 100644 tests/inlineasm/xtensa/asmloadstore.py create mode 100644 tests/inlineasm/xtensa/asmloadstore.py.exp create mode 100644 tests/inlineasm/xtensa/asmmisc.py create mode 100644 tests/inlineasm/xtensa/asmmisc.py.exp create mode 100644 tests/inlineasm/xtensa/asmshift.py create mode 100644 tests/inlineasm/xtensa/asmshift.py.exp create mode 100644 tests/internal_bench/class_create-0-empty.py create mode 100644 tests/internal_bench/class_create-1-slots.py create mode 100644 tests/internal_bench/class_create-1.1-slots5.py create mode 100644 tests/internal_bench/class_create-2-classattr.py create mode 100644 tests/internal_bench/class_create-2.1-classattr5.py create mode 100644 tests/internal_bench/class_create-2.3-classattr5objs.py create mode 100644 tests/internal_bench/class_create-3-instancemethod.py create mode 100644 tests/internal_bench/class_create-4-classmethod.py create mode 100644 tests/internal_bench/class_create-4.1-classmethod_implicit.py create mode 100644 tests/internal_bench/class_create-5-staticmethod.py create mode 100644 tests/internal_bench/class_create-6-getattribute.py create mode 100644 tests/internal_bench/class_create-6.1-getattr.py create mode 100644 tests/internal_bench/class_create-6.2-property.py create mode 100644 tests/internal_bench/class_create-6.3-descriptor.py create mode 100644 tests/internal_bench/class_create-7-inherit.py create mode 100644 tests/internal_bench/class_create-7.1-inherit_initsubclass.py create mode 100644 tests/internal_bench/class_create-8-metaclass_setname.py create mode 100644 tests/internal_bench/class_create-8.1-metaclass_setname5.py create mode 100644 tests/internal_bench/class_instance-0-object.py create mode 100644 tests/internal_bench/class_instance-0.1-object-gc.py create mode 100644 tests/internal_bench/class_instance-1-empty.py create mode 100644 tests/internal_bench/class_instance-1.1-classattr.py create mode 100644 tests/internal_bench/class_instance-1.2-func.py create mode 100644 tests/internal_bench/class_instance-1.3-empty-gc.py create mode 100644 tests/internal_bench/class_instance-2-init.py create mode 100644 tests/internal_bench/class_instance-2.1-init_super.py create mode 100644 tests/internal_bench/class_instance-2.2-new.py create mode 100644 tests/internal_bench/class_instance-3-del.py create mode 100644 tests/internal_bench/class_instance-3.1-del-gc.py create mode 100644 tests/internal_bench/class_instance-4-slots.py create mode 100644 tests/internal_bench/class_instance-4.1-slots5.py create mode 100644 tests/internal_bench/var-6.2-instance-speciallookup.py create mode 100644 tests/internal_bench/var-6.3-instance-property.py create mode 100644 tests/internal_bench/var-6.4-instance-descriptor.py create mode 100644 tests/internal_bench/var-6.5-instance-getattr.py create mode 100644 tests/internal_bench/var-6.6-instance-builtin_ordered.py create mode 100644 tests/internal_bench/var-9-getattr.py create mode 100644 tests/internal_bench/var-9.1-getattr_default.py create mode 100644 tests/internal_bench/var-9.2-getattr_default_special.py create mode 100644 tests/internal_bench/var-9.3-except_ok.py create mode 100644 tests/internal_bench/var-9.4-except_selfinduced.py create mode 100644 tests/internal_bench/var-9.5-except_error.py create mode 100644 tests/internal_bench/var-9.6-except_error_special.py create mode 100644 tests/micropython/const_float.py create mode 100644 tests/micropython/const_float.py.exp create mode 100644 tests/micropython/const_math.py create mode 100644 tests/micropython/const_math.py.exp create mode 100644 tests/micropython/emg_exc.py.native.exp create mode 100644 tests/micropython/heapalloc_slice.py create mode 100644 tests/micropython/heapalloc_traceback.py.native.exp create mode 100644 tests/micropython/ringio_big.py create mode 100644 tests/micropython/ringio_big.py.exp create mode 100644 tests/micropython/test_normalize_newlines.py create mode 100644 tests/micropython/test_normalize_newlines.py.exp create mode 100644 tests/micropython/viper_large_jump.py create mode 100644 tests/micropython/viper_large_jump.py.exp create mode 100644 tests/micropython/viper_ptr16_load_boundary.py create mode 100644 tests/micropython/viper_ptr16_load_boundary.py.exp create mode 100644 tests/micropython/viper_ptr16_store_boundary.py create mode 100644 tests/micropython/viper_ptr16_store_boundary.py.exp create mode 100644 tests/micropython/viper_ptr32_load_boundary.py create mode 100644 tests/micropython/viper_ptr32_load_boundary.py.exp create mode 100644 tests/micropython/viper_ptr32_store_boundary.py create mode 100644 tests/micropython/viper_ptr32_store_boundary.py.exp create mode 100644 tests/micropython/viper_ptr8_load_boundary.py create mode 100644 tests/micropython/viper_ptr8_load_boundary.py.exp create mode 100644 tests/micropython/viper_ptr8_store_boundary.py create mode 100644 tests/micropython/viper_ptr8_store_boundary.py.exp create mode 100644 tests/misc/print_exception.py.native.exp create mode 100644 tests/misc/sys_settrace_cov.py create mode 100644 tests/misc/sys_settrace_cov.py.exp create mode 100644 tests/multi_extmod/machine_i2c_target_irq.py create mode 100644 tests/multi_extmod/machine_i2c_target_irq.py.exp create mode 100644 tests/multi_extmod/machine_i2c_target_memory.py create mode 100644 tests/multi_extmod/machine_i2c_target_memory.py.exp create mode 100755 tests/serial_test.py create mode 100644 tests/stress/qstr_limit_str_modulo.py create mode 100644 tests/stress/qstr_limit_str_modulo.py.exp create mode 100644 tests/target_wiring/EK_RA6M2.py create mode 100644 tests/target_wiring/NUCLEO_WB55.py create mode 100644 tests/target_wiring/PYBx.py create mode 100644 tests/target_wiring/ZEPHYR_NUCLEO_WB55RG.py create mode 100644 tests/target_wiring/alif.py create mode 100644 tests/target_wiring/esp32.py create mode 100644 tests/target_wiring/mimxrt.py create mode 100644 tests/target_wiring/nrf.py create mode 100644 tests/target_wiring/rp2.py create mode 100644 tests/target_wiring/samd.py create mode 100644 tests/thread/thread_exc2.py.native.exp rename tests/thread/{thread_lock4.py => thread_lock4_intbig.py} (100%) diff --git a/.gitattributes b/.gitattributes index 38bba729fc191..d226cebf5d81d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -27,4 +27,5 @@ # These should also not be modified by git. tests/basics/string_cr_conversion.py -text tests/basics/string_crlf_conversion.py -text +tests/micropython/test_normalize_newlines.py.exp -text # CIRCUITPY-CHANGE: remove non-CircuitPython tests diff --git a/LICENSE_MicroPython b/LICENSE_MicroPython index 469ae1927396d..1fad9b4134e01 100644 --- a/LICENSE_MicroPython +++ b/LICENSE_MicroPython @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2024 Damien P. George +Copyright (c) 2013-2025 Damien P. George Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/library/collections.rst b/docs/library/collections.rst index b006a97dcaf22..c3ec763d20e1b 100644 --- a/docs/library/collections.rst +++ b/docs/library/collections.rst @@ -109,3 +109,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) diff --git a/docs/library/platform.rst b/docs/library/platform.rst index c091477d84cb1..c19ef0f5df524 100644 --- a/docs/library/platform.rst +++ b/docs/library/platform.rst @@ -36,3 +36,11 @@ Functions Returns a tuple of strings *(lib, version)*, where *lib* is the name of the libc that MicroPython is linked to, and *version* the corresponding version of this libc. + +.. function:: processor() + + Returns a string with a detailed name of the processor, if one is available. + If no name for the processor is known, it will return an empty string + instead. + + This is currently available only on RISC-V targets (both 32 and 64 bits). diff --git a/docs/library/re.rst b/docs/library/re.rst index 19b15d2d2c299..b8aeefd90cfa4 100644 --- a/docs/library/re.rst +++ b/docs/library/re.rst @@ -154,8 +154,8 @@ Regex objects Compiled regular expression. Instances of this class are created using `re.compile()`. -.. method:: regex.match(string) - regex.search(string) +.. method:: regex.match(string, [pos, [endpos]]) + regex.search(string, [pos, [endpos]]) regex.sub(replace, string, count=0, flags=0, /) Similar to the module-level functions :meth:`match`, :meth:`search` @@ -163,6 +163,16 @@ Compiled regular expression. Instances of this class are created using Using methods is (much) more efficient if the same regex is applied to multiple strings. + The optional second parameter *pos* gives an index in the string where the + search is to start; it defaults to ``0``. This is not completely equivalent + to slicing the string; the ``'^'`` pattern character matches at the real + beginning of the string and at positions just after a newline, but not + necessarily at the index where the search is to start. + + The optional parameter *endpos* limits how far the string will be searched; + it will be as if the string is *endpos* characters long, so only the + characters from *pos* to ``endpos - 1`` will be searched for a match. + .. method:: regex.split(string, max_split=-1, /) Split a *string* using regex. If *max_split* is given, it specifies diff --git a/docs/library/sys.rst b/docs/library/sys.rst index 8def36a2b07a2..c9c687df35936 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -12,17 +12,9 @@ Functions .. function:: exit(retval=0, /) Terminate current program with a given exit code. Underlyingly, this - function raise as `SystemExit` exception. If an argument is given, its + function raises a `SystemExit` exception. If an argument is given, its value given as an argument to `SystemExit`. -.. function:: print_exception(exc, file=sys.stdout, /) - - This function is deprecated and will be removed starting in - CircuitPython 10.x, `traceback.print_exception()` should be used instead. - - Print exception with a traceback to a file-like object *file* (or - `sys.stdout` by default). - .. admonition:: Difference to CPython :class: attention @@ -52,6 +44,8 @@ Constants * *version* - tuple (major, minor, micro), e.g. (1, 7, 0) * *_machine* - string describing the underlying machine * *_mpy* - supported mpy file-format version (optional attribute) + * *_build* - string that can help identify the configuration that + MicroPython was built with This object is the recommended way to distinguish CircuitPython from other Python implementations (note that it still may not exist in the very @@ -116,15 +110,15 @@ Constants .. data:: stderr - Standard error ``stream``. + Standard error `stream`. .. data:: stdin - Standard input ``stream``. + Standard input `stream`. .. data:: stdout - Standard output ``stream``. + Standard output `stream`. .. data:: version diff --git a/docs/reference/glossary.rst b/docs/reference/glossary.rst index 9e9330de4ccfe..391dc307d2343 100644 --- a/docs/reference/glossary.rst +++ b/docs/reference/glossary.rst @@ -186,6 +186,13 @@ Glossary Most MicroPython boards make a REPL available over a UART, and this is typically accessible on a host PC via USB. + small integer + MicroPython optimises the internal representation of integers such that + "small" values do not take up space on the heap, and calculations with + them do not require heap allocation. On most 32-bit ports, this + corresponds to values in the interval ``-2**30 <= x < 2**30``, but this + should be considered an implementation detail and not relied upon. + stream Also known as a "file-like object". A Python object which provides sequential read-write access to the underlying data. A stream object diff --git a/extmod/cyw43_config_common.h b/extmod/cyw43_config_common.h new file mode 100644 index 0000000000000..595af37d71318 --- /dev/null +++ b/extmod/cyw43_config_common.h @@ -0,0 +1,126 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Damien P. George, Angus Gratton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_EXTMOD_CYW43_CONFIG_COMMON_H +#define MICROPY_INCLUDED_EXTMOD_CYW43_CONFIG_COMMON_H + +// The board-level config will be included here, so it can set some CYW43 values. +#include "py/mpconfig.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "extmod/modnetwork.h" +#include "lwip/apps/mdns.h" +#include "pendsv.h" + +// This file is included at the top of port-specific cyw43_configport.h files, +// and holds the MicroPython-specific but non-port-specific parts. +// +// It's included into both MicroPython sources and the lib/cyw43-driver sources. + +#define CYW43_IOCTL_TIMEOUT_US (1000000) +#define CYW43_NETUTILS (1) +#define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) + +#define CYW43_EPERM MP_EPERM // Operation not permitted +#define CYW43_EIO MP_EIO // I/O error +#define CYW43_EINVAL MP_EINVAL // Invalid argument +#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out + +#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER +#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT +#define CYW43_THREAD_LOCK_CHECK + +#define CYW43_HOST_NAME mod_network_hostname_data + +#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) + +#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT +#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT +#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE +#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP +#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN + +#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 +#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR + +#define cyw43_hal_ticks_us mp_hal_ticks_us +#define cyw43_hal_ticks_ms mp_hal_ticks_ms + +#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t +#define cyw43_hal_pin_config mp_hal_pin_config +#define cyw43_hal_pin_read mp_hal_pin_read +#define cyw43_hal_pin_low mp_hal_pin_low +#define cyw43_hal_pin_high mp_hal_pin_high + +#define cyw43_hal_get_mac mp_hal_get_mac +#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii +#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac + +#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) + +// Note: this function is only called if CYW43_POST_POLL_HOOK is defined in cyw43_configport.h +void cyw43_post_poll_hook(void); + +#ifdef MICROPY_EVENT_POLL_HOOK +// Older style hook macros on some ports +#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK +#else +// Newer style hooks on other ports +#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() +#endif + +static inline void cyw43_delay_us(uint32_t us) { + uint32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + } +} + +static inline void cyw43_delay_ms(uint32_t ms) { + // PendSV may be disabled via CYW43_THREAD_ENTER, so this delay is a busy loop. + uint32_t us = ms * 1000; + uint32_t start = mp_hal_ticks_us(); + while (mp_hal_ticks_us() - start < us) { + CYW43_EVENT_POLL_HOOK; + } +} + +#if LWIP_MDNS_RESPONDER == 1 + +// Hook for any additional TCP/IP initialization than needs to be done. +// Called after the netif specified by `itf` has been set up. +#ifndef CYW43_CB_TCPIP_INIT_EXTRA +#define CYW43_CB_TCPIP_INIT_EXTRA(self, itf) mdns_resp_add_netif(&self->netif[itf], mod_network_hostname_data) +#endif + +// Hook for any additional TCP/IP deinitialization than needs to be done. +// Called before the netif specified by `itf` is removed. +#ifndef CYW43_CB_TCPIP_DEINIT_EXTRA +#define CYW43_CB_TCPIP_DEINIT_EXTRA(self, itf) mdns_resp_remove_netif(&self->netif[itf]) +#endif + +#endif + +#endif // MICROPY_INCLUDED_EXTMOD_CYW43_CONFIG_COMMON_H diff --git a/extmod/extmod.mk b/extmod/extmod.mk index daa6f267342ae..5b2772e4e2a99 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -162,7 +162,7 @@ endif ifeq ($(MICROPY_VFS_LFS2),1) CFLAGS_EXTMOD += -DMICROPY_VFS_LFS2=1 -CFLAGS_THIRDPARTY += -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT +CFLAGS_THIRDPARTY += -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT -DLFS2_DEFINES=extmod/littlefs-include/lfs2_defines.h SRC_THIRDPARTY_C += $(addprefix $(LITTLEFS_DIR)/,\ lfs2.c \ lfs2_util.c \ @@ -461,7 +461,7 @@ ESP_HOSTED_SRC_C = $(addprefix $(ESP_HOSTED_DIR)/,\ ) ifeq ($(MICROPY_PY_BLUETOOTH),1) -ESP_HOSTED_SRC_C += $(ESP_HOSTED_DIR)/esp_hosted_bthci.c +ESP_HOSTED_SRC_C += $(ESP_HOSTED_DIR)/esp_hosted_bthci_uart.c endif # Include the protobuf-c support functions diff --git a/extmod/littlefs-include/lfs2_defines.h b/extmod/littlefs-include/lfs2_defines.h new file mode 100644 index 0000000000000..4ae566f508afa --- /dev/null +++ b/extmod/littlefs-include/lfs2_defines.h @@ -0,0 +1,12 @@ +#ifndef LFS2_DEFINES_H +#define LFS2_DEFINES_H + +#include "py/mpconfig.h" + +#if MICROPY_PY_DEFLATE +// We reuse the CRC32 implementation from uzlib to save a few bytes +#include "lib/uzlib/uzlib.h" +#define LFS2_CRC(crc, buffer, size) uzlib_crc32(buffer, size, crc) +#endif + +#endif \ No newline at end of file diff --git a/extmod/lwip-include/arch/cc.h b/extmod/lwip-include/arch/cc.h deleted file mode 100644 index 400dc6ec75de1..0000000000000 --- a/extmod/lwip-include/arch/cc.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H - -#include - -// Generate lwip's internal types from stdint - -typedef uint8_t u8_t; -typedef int8_t s8_t; -typedef uint16_t u16_t; -typedef int16_t s16_t; -typedef uint32_t u32_t; -typedef int32_t s32_t; - -typedef u32_t mem_ptr_t; - -#define U16_F "hu" -#define S16_F "hd" -#define X16_F "hx" -#define U32_F "u" -#define S32_F "d" -#define X32_F "x" - -#define X8_F "02x" -#define SZT_F "u" - -#define BYTE_ORDER LITTLE_ENDIAN - -#define LWIP_CHKSUM_ALGORITHM 2 - -#include -#define LWIP_PLATFORM_DIAG(x) -#define LWIP_PLATFORM_ASSERT(x) { assert(1); } - -//#define PACK_STRUCT_FIELD(x) x __attribute__((packed)) -#define PACK_STRUCT_FIELD(x) x -#define PACK_STRUCT_STRUCT __attribute__((packed)) -#define PACK_STRUCT_BEGIN -#define PACK_STRUCT_END - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H diff --git a/extmod/lwip-include/arch/perf.h b/extmod/lwip-include/arch/perf.h deleted file mode 100644 index d310fc339f162..0000000000000 --- a/extmod/lwip-include/arch/perf.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H - -#define PERF_START /* null definition */ -#define PERF_STOP(x) /* null definition */ - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H diff --git a/extmod/lwip-include/lwipopts.h b/extmod/lwip-include/lwipopts.h deleted file mode 100644 index 584decfe85f83..0000000000000 --- a/extmod/lwip-include/lwipopts.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H - -#include -#include -#include - -// We're running without an OS for this port. We don't provide any services except light protection. -#define NO_SYS 1 - -#define SYS_LIGHTWEIGHT_PROT 1 -#include -typedef uint32_t sys_prot_t; - -#define TCP_LISTEN_BACKLOG 1 - -// We'll put these into a proper ifdef once somebody implements an ethernet driver -#define LWIP_ARP 0 -#define LWIP_ETHERNET 0 - -#define LWIP_DNS 1 - -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 - -// CIRCUITPY-CHANGE: #if instead of #ifdef -#if MICROPY_PY_LWIP_SLIP -#define LWIP_HAVE_SLIPIF 1 -#endif - -// For now, we can simply define this as a macro for the timer code. But this function isn't -// universal and other ports will need to do something else. It may be necessary to move -// things like this into a port-provided header file. -#define sys_now mp_hal_ticks_ms - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H diff --git a/extmod/lwip-include/lwipopts_common.h b/extmod/lwip-include/lwipopts_common.h index 3e4230909499e..8cb1acfe2ca62 100644 --- a/extmod/lwip-include/lwipopts_common.h +++ b/extmod/lwip-include/lwipopts_common.h @@ -28,6 +28,9 @@ #include "py/mpconfig.h" +// This is needed to access `next_timeout` via `sys_timeouts_get_next_timeout()`. +#define LWIP_TESTMODE 1 + // This sys-arch protection is not needed. // Ports either protect lwIP code with flags, or run it at PendSV priority. #define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) diff --git a/extmod/modjson.c b/extmod/modjson.c index 67da36f0ccf71..77f4a336aa821 100644 --- a/extmod/modjson.c +++ b/extmod/modjson.c @@ -227,7 +227,8 @@ static mp_obj_t _mod_json_load(mp_obj_t stream_obj, bool return_first_json) { for (;;) { cont: if (S_END(s)) { - break; + // Input finished abruptly in the middle of a composite entity. + goto fail; } mp_obj_t next = MP_OBJ_NULL; bool enter = false; diff --git a/extmod/modplatform.c b/extmod/modplatform.c index c6d4d31b97ec1..e1f5476c3b3aa 100644 --- a/extmod/modplatform.c +++ b/extmod/modplatform.c @@ -61,11 +61,49 @@ static mp_obj_t platform_libc_ver(size_t n_args, const mp_obj_t *pos_args, mp_ma } static MP_DEFINE_CONST_FUN_OBJ_KW(platform_libc_ver_obj, 0, platform_libc_ver); +#ifdef __riscv +static mp_obj_t platform_processor(void) { + #if (__riscv_xlen <= 64) && !defined(__linux__) + uintptr_t misa_csr = 0; + + // Load the MISA CSR directly. + __asm volatile ( + "csrr %0, misa \n" + : "+r" (misa_csr) + : + : + ); + + char processor_buffer[31] = { // "RV32"/"RV64" + up to 26 chars. + #if (__riscv_xlen < 64) + "RV32" + #else + "RV64" + #endif + }; + mp_uint_t offset = 4; + for (mp_uint_t bit = 0; bit < 26; bit++) { + if (misa_csr & (1U << bit)) { + processor_buffer[offset++] = 'A' + bit; + } + } + + return mp_obj_new_str(processor_buffer, offset); + #else + return MP_OBJ_NEW_QSTR(MP_QSTR_); + #endif +} +static MP_DEFINE_CONST_FUN_OBJ_0(platform_processor_obj, platform_processor); +#endif + static const mp_rom_map_elem_t modplatform_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_platform) }, { MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&platform_platform_obj) }, { MP_ROM_QSTR(MP_QSTR_python_compiler), MP_ROM_PTR(&platform_python_compiler_obj) }, { MP_ROM_QSTR(MP_QSTR_libc_ver), MP_ROM_PTR(&platform_libc_ver_obj) }, + #ifdef __riscv + { MP_ROM_QSTR(MP_QSTR_processor), MP_ROM_PTR(&platform_processor_obj) }, + #endif }; static MP_DEFINE_CONST_DICT(modplatform_globals, modplatform_globals_table); diff --git a/extmod/modre.c b/extmod/modre.c index ba2927eb4a8a6..36fff1d1dc668 100644 --- a/extmod/modre.c +++ b/extmod/modre.c @@ -196,10 +196,11 @@ static void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t // Note: this function can't be named re_exec because it may clash with system headers, eg on FreeBSD static mp_obj_t re_exec_helper(bool is_anchored, uint n_args, const mp_obj_t *args) { - (void)n_args; mp_obj_re_t *self; + bool was_compiled = false; if (mp_obj_is_type(args[0], (mp_obj_type_t *)&re_type)) { self = MP_OBJ_TO_PTR(args[0]); + was_compiled = true; } else { self = MP_OBJ_TO_PTR(mod_re_compile(1, args)); } @@ -207,37 +208,28 @@ static mp_obj_t re_exec_helper(bool is_anchored, uint n_args, const mp_obj_t *ar size_t len; subj.begin_line = subj.begin = mp_obj_str_get_data(args[1], &len); subj.end = subj.begin + len; - // CIRCUITPY-CHANGE - #if MICROPY_PY_RE_MATCH_SPAN_START_END && !(defined(MICROPY_ENABLE_DYNRUNTIME) && MICROPY_ENABLE_DYNRUNTIME) - - if (n_args > 2) { - const mp_obj_type_t *self_type = mp_obj_get_type(args[1]); - mp_int_t str_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(args[1])); - const byte *begin = (const byte *)subj.begin; - int pos = mp_obj_get_int(args[2]); - if (pos >= str_len) { - return mp_const_none; - } - if (pos < 0) { - pos = 0; + if (was_compiled && n_args > 2) { + // Arg #2 is starting-pos + mp_int_t startpos = mp_obj_get_int(args[2]); + if (startpos > (mp_int_t)len) { + startpos = len; + } else if (startpos < 0) { + startpos = 0; } - const byte *pos_ptr = str_index_to_ptr(self_type, begin, len, MP_OBJ_NEW_SMALL_INT(pos), true); - - const byte *endpos_ptr = (const byte *)subj.end; + subj.begin += startpos; if (n_args > 3) { - int endpos = mp_obj_get_int(args[3]); - if (endpos <= pos) { - return mp_const_none; + // Arg #3 is ending-pos + mp_int_t endpos = mp_obj_get_int(args[3]); + if (endpos > (mp_int_t)len) { + endpos = len; + } else if (endpos < startpos) { + endpos = startpos; } - // Will cap to length - endpos_ptr = str_index_to_ptr(self_type, begin, len, args[3], true); + subj.end = subj.begin_line + endpos; } - - subj.begin = (const char *)pos_ptr; - subj.end = (const char *)endpos_ptr; } - #endif + int caps_num = (self->re.sub + 1) * 2; mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, caps, char *, caps_num); // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char @@ -458,6 +450,9 @@ static mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { const char *re_str = mp_obj_str_get_str(args[0]); int size = re1_5_sizecode(re_str); if (size == -1) { + #if MICROPY_ERROR_REPORTING >= MICROPY_ERROR_REPORTING_NORMAL + mp_raise_ValueError(MP_ERROR_TEXT("regex too complex")); + #endif goto error; } mp_obj_re_t *o = mp_obj_malloc_var(mp_obj_re_t, re.insts, char, size, (mp_obj_type_t *)&re_type); @@ -470,6 +465,7 @@ static mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { int error = re1_5_compilecode(&o->re, re_str); if (error != 0) { error: + // CIRCUITPY-CHANGE: capitalized mp_raise_ValueError(MP_ERROR_TEXT("Error in regex")); } #if MICROPY_PY_RE_DEBUG diff --git a/extmod/modtime.c b/extmod/modtime.c index deb4bb4c9ace7..ee898828a4ab1 100644 --- a/extmod/modtime.c +++ b/extmod/modtime.c @@ -53,26 +53,26 @@ // - weekday is 0-6 for Mon-Sun // - yearday is 1-366 static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { + timeutils_struct_time_t tm; if (n_args == 0 || args[0] == mp_const_none) { // Get current date and time. - return mp_time_localtime_get(); + mp_time_localtime_get(&tm); } else { // Convert given seconds to tuple. - mp_int_t seconds = mp_obj_get_int(args[0]); - timeutils_struct_time_t tm; + mp_timestamp_t seconds = timeutils_obj_get_timestamp(args[0]); timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); - mp_obj_t tuple[8] = { - tuple[0] = mp_obj_new_int(tm.tm_year), - tuple[1] = mp_obj_new_int(tm.tm_mon), - tuple[2] = mp_obj_new_int(tm.tm_mday), - tuple[3] = mp_obj_new_int(tm.tm_hour), - tuple[4] = mp_obj_new_int(tm.tm_min), - tuple[5] = mp_obj_new_int(tm.tm_sec), - tuple[6] = mp_obj_new_int(tm.tm_wday), - tuple[7] = mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); } + mp_obj_t tuple[8] = { + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_time_localtime_obj, 0, 1, time_localtime); @@ -90,7 +90,7 @@ static mp_obj_t time_mktime(mp_obj_t tuple) { mp_raise_TypeError(MP_ERROR_TEXT("mktime needs a tuple of length 8 or 9")); } - return mp_obj_new_int_from_uint(timeutils_mktime(mp_obj_get_int(elem[0]), + return timeutils_obj_from_timestamp(timeutils_mktime(mp_obj_get_int(elem[0]), mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), mp_obj_get_int(elem[3]), mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5]))); } diff --git a/extmod/vfs.c b/extmod/vfs.c index 0cf93c3966c90..c4156990bcc38 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -101,7 +101,7 @@ mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) { return MP_STATE_VM(vfs_cur); } -// Version of mp_vfs_lookup_path that takes and returns uPy string objects. +// Version of mp_vfs_lookup_path that takes and returns MicroPython string objects. static mp_vfs_mount_t *lookup_path(mp_obj_t path_in, mp_obj_t *path_out) { const char *path = mp_obj_str_get_str(path_in); const char *p_out; diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index 0cd3cc6f0c631..74d1262364ef4 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -108,7 +108,7 @@ static int mp_vfs_blockdev_call_rw(mp_obj_t *args, size_t block_num, size_t bloc // and negative integer on errors. Check for positive integer // results as some callers (i.e. littlefs) will produce corrupt // results from these. - int i = MP_OBJ_SMALL_INT_VALUE(ret); + int i = mp_obj_get_int(ret); return i > 0 ? (-MP_EINVAL) : i; } } diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 6267452cd32c7..cb5fb649a1e5b 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -58,7 +58,7 @@ // CIRCUITPY-CHANGE // Factoring this common call saves about 90 bytes. -static NORETURN void mp_raise_OSError_fresult(FRESULT res) { +static MP_NORETURN void mp_raise_OSError_fresult(FRESULT res) { mp_raise_OSError(fresult_to_errno_table[res]); } @@ -455,77 +455,6 @@ static mp_obj_t vfs_fat_umount(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount); -// CIRCUITPY-CHANGE -static mp_obj_t vfs_fat_utime(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t times_in) { - mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); - const char *path = mp_obj_str_get_str(path_in); - if (!mp_obj_is_tuple_compatible(times_in)) { - mp_raise_type_arg(&mp_type_TypeError, times_in); - } - - mp_obj_t *otimes; - mp_obj_get_array_fixed_n(times_in, 2, &otimes); - - // Validate that both elements of the tuple are int and discard the second one - int time[2]; - time[0] = mp_obj_get_int(otimes[0]); - time[1] = mp_obj_get_int(otimes[1]); - timeutils_struct_time_t tm; - timeutils_seconds_since_epoch_to_struct_time(time[0], &tm); - - FILINFO fno; - fno.fdate = (WORD)(((tm.tm_year - 1980) * 512U) | tm.tm_mon * 32U | tm.tm_mday); - fno.ftime = (WORD)(tm.tm_hour * 2048U | tm.tm_min * 32U | tm.tm_sec / 2U); - FRESULT res = f_utime(&self->fatfs, path, &fno); - if (res != FR_OK) { - mp_raise_OSError_fresult(res); - } - - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_utime_obj, vfs_fat_utime); - -static mp_obj_t vfs_fat_getreadonly(mp_obj_t self_in) { - fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(!filesystem_is_writable_by_python(self)); -} -static MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getreadonly_obj, vfs_fat_getreadonly); - -static MP_PROPERTY_GETTER(fat_vfs_readonly_obj, - (mp_obj_t)&fat_vfs_getreadonly_obj); - -#if MICROPY_FATFS_USE_LABEL -static mp_obj_t vfs_fat_getlabel(mp_obj_t self_in) { - fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); - char working_buf[12]; - FRESULT res = f_getlabel(&self->fatfs, working_buf, NULL); - if (res != FR_OK) { - mp_raise_OSError_fresult(res); - } - return mp_obj_new_str(working_buf, strlen(working_buf)); -} -static MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getlabel_obj, vfs_fat_getlabel); - -static mp_obj_t vfs_fat_setlabel(mp_obj_t self_in, mp_obj_t label_in) { - fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); - verify_fs_writable(self); - const char *label_str = mp_obj_str_get_str(label_in); - FRESULT res = f_setlabel(&self->fatfs, label_str); - if (res != FR_OK) { - if (res == FR_WRITE_PROTECTED) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Read-only filesystem")); - } - mp_raise_OSError_fresult(res); - } - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_setlabel_obj, vfs_fat_setlabel); - -static MP_PROPERTY_GETSET(fat_vfs_label_obj, - (mp_obj_t)&fat_vfs_getlabel_obj, - (mp_obj_t)&fat_vfs_setlabel_obj); -#endif - static const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { // CIRCUITPY-CHANGE: correct name #if FF_FS_REENTRANT diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index 4b10ca3aa597c..bbdd21cfb9176 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -104,6 +104,12 @@ static void MP_VFS_LFSx(init_config)(MP_OBJ_VFS_LFSx * self, mp_obj_t bdev, size config->read_buffer = m_new(uint8_t, config->cache_size); config->prog_buffer = m_new(uint8_t, config->cache_size); config->lookahead_buffer = m_new(uint8_t, config->lookahead_size); + #ifdef LFS2_MULTIVERSION + // This can be set to override the on-disk lfs version. + // eg. for compat with lfs2 < v2.6 add the following to make: + // CFLAGS += '-DLFS2_MULTIVERSION=0x00020000' + config->disk_version = LFS2_MULTIVERSION; + #endif #endif } @@ -300,7 +306,7 @@ static mp_obj_t MP_VFS_LFSx(chdir)(mp_obj_t self_in, mp_obj_t path_in) { struct LFSx_API (info) info; int ret = LFSx_API(stat)(&self->lfs, path, &info); if (ret < 0 || info.type != LFSx_MACRO(_TYPE_DIR)) { - mp_raise_OSError(-MP_ENOENT); + mp_raise_OSError(MP_ENOENT); } } @@ -378,7 +384,7 @@ static mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { mp_raise_OSError(-ret); } - mp_uint_t mtime = 0; + mp_timestamp_t mtime = 0; #if LFS_BUILD_VERSION == 2 uint8_t mtime_buf[8]; lfs2_ssize_t sz = lfs2_getattr(&self->lfs, path, LFS_ATTR_MTIME, &mtime_buf, sizeof(mtime_buf)); @@ -400,9 +406,9 @@ static mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid t->items[6] = mp_obj_new_int_from_uint(info.size); // st_size - t->items[7] = mp_obj_new_int_from_uint(mtime); // st_atime - t->items[8] = mp_obj_new_int_from_uint(mtime); // st_mtime - t->items[9] = mp_obj_new_int_from_uint(mtime); // st_ctime + t->items[7] = timeutils_obj_from_timestamp(mtime); // st_atime + t->items[8] = timeutils_obj_from_timestamp(mtime); // st_mtime + t->items[9] = timeutils_obj_from_timestamp(mtime); // st_ctime return MP_OBJ_FROM_PTR(t); } diff --git a/extmod/vfs_lfsx_file.c b/extmod/vfs_lfsx_file.c index ab5cce50088b9..56daa53e06869 100644 --- a/extmod/vfs_lfsx_file.c +++ b/extmod/vfs_lfsx_file.c @@ -90,9 +90,9 @@ mp_obj_t MP_VFS_LFSx(file_open)(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mod } #if LFS_BUILD_VERSION == 1 - MP_OBJ_VFS_LFSx_FILE *o = mp_obj_malloc_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->prog_size, type); + MP_OBJ_VFS_LFSx_FILE *o = mp_obj_malloc_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, file_buffer, uint8_t, self->lfs.cfg->prog_size, type); #else - MP_OBJ_VFS_LFSx_FILE *o = mp_obj_malloc_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->cache_size, type); + MP_OBJ_VFS_LFSx_FILE *o = mp_obj_malloc_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, file_buffer, uint8_t, self->lfs.cfg->cache_size, type); #endif o->vfs = self; #if !MICROPY_GC_CONSERVATIVE_CLEAR diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index bd9a6d84062cf..27f833e802f69 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -137,7 +137,7 @@ static mp_obj_t vfs_posix_make_new(const mp_obj_type_t *type, size_t n_args, siz vstr_add_char(&vfs->root, '/'); } vfs->root_len = vfs->root.len; - vfs->readonly = false; + vfs->readonly = !MICROPY_VFS_POSIX_WRITABLE; return MP_OBJ_FROM_PTR(vfs); } @@ -160,10 +160,21 @@ static mp_obj_t vfs_posix_umount(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_umount_obj, vfs_posix_umount); +static inline bool vfs_posix_is_readonly(mp_obj_vfs_posix_t *self) { + return !MICROPY_VFS_POSIX_WRITABLE || self->readonly; +} + +static void vfs_posix_require_writable(mp_obj_t self_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + if (vfs_posix_is_readonly(self)) { + mp_raise_OSError(MP_EROFS); + } +} + static mp_obj_t vfs_posix_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *mode = mp_obj_str_get_str(mode_in); - if (self->readonly + if (vfs_posix_is_readonly(self) && (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL || strchr(mode, '+') != NULL)) { mp_raise_OSError(MP_EROFS); } @@ -303,6 +314,7 @@ typedef struct _mp_obj_listdir_t { } mp_obj_listdir_t; static mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) { + vfs_posix_require_writable(self_in); mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *path = vfs_posix_get_path_str(self, path_in); MP_THREAD_GIL_EXIT(); @@ -320,11 +332,13 @@ static mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) { static MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_mkdir_obj, vfs_posix_mkdir); static mp_obj_t vfs_posix_remove(mp_obj_t self_in, mp_obj_t path_in) { + vfs_posix_require_writable(self_in); return vfs_posix_fun1_helper(self_in, path_in, unlink); } static MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_remove_obj, vfs_posix_remove); static mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_t new_path_in) { + vfs_posix_require_writable(self_in); mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *old_path = vfs_posix_get_path_str(self, old_path_in); const char *new_path = vfs_posix_get_path_str(self, new_path_in); @@ -339,6 +353,7 @@ static mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_ static MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_rename_obj, vfs_posix_rename); static mp_obj_t vfs_posix_rmdir(mp_obj_t self_in, mp_obj_t path_in) { + vfs_posix_require_writable(self_in); return vfs_posix_fun1_helper(self_in, path_in, rmdir); } static MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_rmdir_obj, vfs_posix_rmdir); diff --git a/extmod/vfs_reader.c b/extmod/vfs_reader.c index de5c4e03d3c13..6c36770295be6 100644 --- a/extmod/vfs_reader.c +++ b/extmod/vfs_reader.c @@ -83,7 +83,7 @@ void mp_reader_new_file(mp_reader_t *reader, qstr filename) { }; mp_obj_t file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); - const mp_stream_p_t *stream_p = mp_get_stream(file); + const mp_stream_p_t *stream_p = mp_get_stream_raise(file, MP_STREAM_OP_READ); int errcode = 0; #if MICROPY_VFS_ROM diff --git a/lib/berkeley-db-1.xx b/lib/berkeley-db-1.xx index 85373b548f1fb..0f3bb6947c2f5 160000 --- a/lib/berkeley-db-1.xx +++ b/lib/berkeley-db-1.xx @@ -1 +1 @@ -Subproject commit 85373b548f1fb0119a463582570b44189dfb09ae +Subproject commit 0f3bb6947c2f57233916dccd7bb425d7bf86e5a6 diff --git a/lib/libm_dbl/libm.h b/lib/libm_dbl/libm.h index cbae6916625e2..fa49ad1cddda5 100644 --- a/lib/libm_dbl/libm.h +++ b/lib/libm_dbl/libm.h @@ -89,7 +89,9 @@ do { \ (d) = __u.f; \ } while (0) +#if !defined(DBL_EPSILON) #define DBL_EPSILON 2.22044604925031308085e-16 +#endif int __rem_pio2(double, double*); int __rem_pio2_large(double*, double*, int, int, int); diff --git a/lib/libm_dbl/rint.c b/lib/libm_dbl/rint.c index fbba390e7d723..b85dec8f2465e 100644 --- a/lib/libm_dbl/rint.c +++ b/lib/libm_dbl/rint.c @@ -2,7 +2,7 @@ #include #include -#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 +#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 || FLT_EVAL_METHOD==16 #define EPS DBL_EPSILON #elif FLT_EVAL_METHOD==2 #define EPS LDBL_EPSILON diff --git a/lib/littlefs/lfs2.c b/lib/littlefs/lfs2.c index 613213669c8f7..aec2ddab9b663 100644 --- a/lib/littlefs/lfs2.c +++ b/lib/littlefs/lfs2.c @@ -93,6 +93,7 @@ static int lfs2_bd_read(lfs2_t *lfs2, // bypass cache? diff = lfs2_aligndown(diff, lfs2->cfg->read_size); int err = lfs2->cfg->read(lfs2->cfg, block, off, data, diff); + LFS2_ASSERT(err <= 0); if (err) { return err; } @@ -282,6 +283,21 @@ static int lfs2_bd_erase(lfs2_t *lfs2, lfs2_block_t block) { /// Small type-level utilities /// + +// some operations on paths +static inline lfs2_size_t lfs2_path_namelen(const char *path) { + return strcspn(path, "/"); +} + +static inline bool lfs2_path_islast(const char *path) { + lfs2_size_t namelen = lfs2_path_namelen(path); + return path[namelen + strspn(path + namelen, "/")] == '\0'; +} + +static inline bool lfs2_path_isdir(const char *path) { + return path[lfs2_path_namelen(path)] != '\0'; +} + // operations on block pairs static inline void lfs2_pair_swap(lfs2_block_t pair[2]) { lfs2_block_t t = pair[0]; @@ -389,18 +405,15 @@ struct lfs2_diskoff { // operations on global state static inline void lfs2_gstate_xor(lfs2_gstate_t *a, const lfs2_gstate_t *b) { - for (int i = 0; i < 3; i++) { - ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; - } + a->tag ^= b->tag; + a->pair[0] ^= b->pair[0]; + a->pair[1] ^= b->pair[1]; } static inline bool lfs2_gstate_iszero(const lfs2_gstate_t *a) { - for (int i = 0; i < 3; i++) { - if (((uint32_t*)a)[i] != 0) { - return false; - } - } - return true; + return a->tag == 0 + && a->pair[0] == 0 + && a->pair[1] == 0; } #ifndef LFS2_READONLY @@ -550,9 +563,9 @@ static int lfs2_dir_compact(lfs2_t *lfs2, lfs2_mdir_t *source, uint16_t begin, uint16_t end); static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, const void *buffer, lfs2_size_t size); -static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_write_(lfs2_t *lfs2, lfs2_file_t *file, const void *buffer, lfs2_size_t size); -static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file); +static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file); static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file); static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file); @@ -574,65 +587,72 @@ static int lfs21_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); #endif -static int lfs2_dir_rawrewind(lfs2_t *lfs2, lfs2_dir_t *dir); +static int lfs2_dir_rewind_(lfs2_t *lfs2, lfs2_dir_t *dir); static lfs2_ssize_t lfs2_file_flushedread(lfs2_t *lfs2, lfs2_file_t *file, void *buffer, lfs2_size_t size); -static lfs2_ssize_t lfs2_file_rawread(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_read_(lfs2_t *lfs2, lfs2_file_t *file, void *buffer, lfs2_size_t size); -static int lfs2_file_rawclose(lfs2_t *lfs2, lfs2_file_t *file); -static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file); +static int lfs2_file_close_(lfs2_t *lfs2, lfs2_file_t *file); +static lfs2_soff_t lfs2_file_size_(lfs2_t *lfs2, lfs2_file_t *file); -static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2); -static int lfs2_fs_rawtraverse(lfs2_t *lfs2, +static lfs2_ssize_t lfs2_fs_size_(lfs2_t *lfs2); +static int lfs2_fs_traverse_(lfs2_t *lfs2, int (*cb)(void *data, lfs2_block_t block), void *data, bool includeorphans); static int lfs2_deinit(lfs2_t *lfs2); -static int lfs2_rawunmount(lfs2_t *lfs2); +static int lfs2_unmount_(lfs2_t *lfs2); /// Block allocator /// + +// allocations should call this when all allocated blocks are committed to +// the filesystem +// +// after a checkpoint, the block allocator may realloc any untracked blocks +static void lfs2_alloc_ckpoint(lfs2_t *lfs2) { + lfs2->lookahead.ckpoint = lfs2->block_count; +} + +// drop the lookahead buffer, this is done during mounting and failed +// traversals in order to avoid invalid lookahead state +static void lfs2_alloc_drop(lfs2_t *lfs2) { + lfs2->lookahead.size = 0; + lfs2->lookahead.next = 0; + lfs2_alloc_ckpoint(lfs2); +} + #ifndef LFS2_READONLY static int lfs2_alloc_lookahead(void *p, lfs2_block_t block) { lfs2_t *lfs2 = (lfs2_t*)p; - lfs2_block_t off = ((block - lfs2->free.off) + lfs2_block_t off = ((block - lfs2->lookahead.start) + lfs2->block_count) % lfs2->block_count; - if (off < lfs2->free.size) { - lfs2->free.buffer[off / 32] |= 1U << (off % 32); + if (off < lfs2->lookahead.size) { + lfs2->lookahead.buffer[off / 8] |= 1U << (off % 8); } return 0; } #endif -// indicate allocated blocks have been committed into the filesystem, this -// is to prevent blocks from being garbage collected in the middle of a -// commit operation -static void lfs2_alloc_ack(lfs2_t *lfs2) { - lfs2->free.ack = lfs2->block_count; -} - -// drop the lookahead buffer, this is done during mounting and failed -// traversals in order to avoid invalid lookahead state -static void lfs2_alloc_drop(lfs2_t *lfs2) { - lfs2->free.size = 0; - lfs2->free.i = 0; - lfs2_alloc_ack(lfs2); -} - #ifndef LFS2_READONLY -static int lfs2_fs_rawgc(lfs2_t *lfs2) { - // Move free offset at the first unused block (lfs2->free.i) - // lfs2->free.i is equal lfs2->free.size when all blocks are used - lfs2->free.off = (lfs2->free.off + lfs2->free.i) % lfs2->block_count; - lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, lfs2->free.ack); - lfs2->free.i = 0; +static int lfs2_alloc_scan(lfs2_t *lfs2) { + // move lookahead buffer to the first unused block + // + // note we limit the lookahead buffer to at most the amount of blocks + // checkpointed, this prevents the math in lfs2_alloc from underflowing + lfs2->lookahead.start = (lfs2->lookahead.start + lfs2->lookahead.next) + % lfs2->block_count; + lfs2->lookahead.next = 0; + lfs2->lookahead.size = lfs2_min( + 8*lfs2->cfg->lookahead_size, + lfs2->lookahead.ckpoint); // find mask of free blocks from tree - memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); - int err = lfs2_fs_rawtraverse(lfs2, lfs2_alloc_lookahead, lfs2, true); + memset(lfs2->lookahead.buffer, 0, lfs2->cfg->lookahead_size); + int err = lfs2_fs_traverse_(lfs2, lfs2_alloc_lookahead, lfs2, true); if (err) { lfs2_alloc_drop(lfs2); return err; @@ -645,36 +665,49 @@ static int lfs2_fs_rawgc(lfs2_t *lfs2) { #ifndef LFS2_READONLY static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) { while (true) { - while (lfs2->free.i != lfs2->free.size) { - lfs2_block_t off = lfs2->free.i; - lfs2->free.i += 1; - lfs2->free.ack -= 1; - - if (!(lfs2->free.buffer[off / 32] & (1U << (off % 32)))) { + // scan our lookahead buffer for free blocks + while (lfs2->lookahead.next < lfs2->lookahead.size) { + if (!(lfs2->lookahead.buffer[lfs2->lookahead.next / 8] + & (1U << (lfs2->lookahead.next % 8)))) { // found a free block - *block = (lfs2->free.off + off) % lfs2->block_count; - - // eagerly find next off so an alloc ack can - // discredit old lookahead blocks - while (lfs2->free.i != lfs2->free.size && - (lfs2->free.buffer[lfs2->free.i / 32] - & (1U << (lfs2->free.i % 32)))) { - lfs2->free.i += 1; - lfs2->free.ack -= 1; + *block = (lfs2->lookahead.start + lfs2->lookahead.next) + % lfs2->block_count; + + // eagerly find next free block to maximize how many blocks + // lfs2_alloc_ckpoint makes available for scanning + while (true) { + lfs2->lookahead.next += 1; + lfs2->lookahead.ckpoint -= 1; + + if (lfs2->lookahead.next >= lfs2->lookahead.size + || !(lfs2->lookahead.buffer[lfs2->lookahead.next / 8] + & (1U << (lfs2->lookahead.next % 8)))) { + return 0; + } } - - return 0; } + + lfs2->lookahead.next += 1; + lfs2->lookahead.ckpoint -= 1; } - // check if we have looked at all blocks since last ack - if (lfs2->free.ack == 0) { - LFS2_ERROR("No more free space %"PRIu32, - lfs2->free.i + lfs2->free.off); + // In order to keep our block allocator from spinning forever when our + // filesystem is full, we mark points where there are no in-flight + // allocations with a checkpoint before starting a set of allocations. + // + // If we've looked at all blocks since the last checkpoint, we report + // the filesystem as out of storage. + // + if (lfs2->lookahead.ckpoint <= 0) { + LFS2_ERROR("No more free space 0x%"PRIx32, + (lfs2->lookahead.start + lfs2->lookahead.next) + % lfs2->block_count); return LFS2_ERR_NOSPC; } - int err = lfs2_fs_rawgc(lfs2); + // No blocks in our lookahead buffer, we need to scan the filesystem for + // unused blocks in the next lookahead window. + int err = lfs2_alloc_scan(lfs2); if(err) { return err; } @@ -690,11 +723,14 @@ static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir, lfs2_tag_t ntag = dir->etag; lfs2_stag_t gdiff = 0; + // synthetic moves if (lfs2_gstate_hasmovehere(&lfs2->gdisk, dir->pair) && - lfs2_tag_id(gmask) != 0 && - lfs2_tag_id(lfs2->gdisk.tag) <= lfs2_tag_id(gtag)) { - // synthetic moves - gdiff -= LFS2_MKTAG(0, 1, 0); + lfs2_tag_id(gmask) != 0) { + if (lfs2_tag_id(lfs2->gdisk.tag) == lfs2_tag_id(gtag)) { + return LFS2_ERR_NOENT; + } else if (lfs2_tag_id(lfs2->gdisk.tag) < lfs2_tag_id(gtag)) { + gdiff -= LFS2_MKTAG(0, 1, 0); + } } // iterate over dir block backwards (for faster lookups) @@ -704,6 +740,7 @@ static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir, int err = lfs2_bd_read(lfs2, NULL, &lfs2->rcache, sizeof(ntag), dir->pair[0], off, &ntag, sizeof(ntag)); + LFS2_ASSERT(err <= 0); if (err) { return err; } @@ -732,6 +769,7 @@ static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir, err = lfs2_bd_read(lfs2, NULL, &lfs2->rcache, diff, dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff); + LFS2_ASSERT(err <= 0); if (err) { return err; } @@ -793,9 +831,6 @@ static int lfs2_dir_getread(lfs2_t *lfs2, const lfs2_mdir_t *dir, size -= diff; continue; } - - // rcache takes priority - diff = lfs2_min(diff, rcache->off-off); } // load to cache, first condition can no longer fail @@ -1247,6 +1282,7 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, if (err == LFS2_ERR_CORRUPT) { break; } + return err; } lfs2_fcrc_fromle32(&fcrc); @@ -1438,32 +1474,46 @@ static int lfs2_dir_find_match(void *data, return LFS2_CMP_EQ; } +// lfs2_dir_find tries to set path and id even if file is not found +// +// returns: +// - 0 if file is found +// - LFS2_ERR_NOENT if file or parent is not found +// - LFS2_ERR_NOTDIR if parent is not a dir static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, const char **path, uint16_t *id) { // we reduce path to a single name if we can find it const char *name = *path; - if (id) { - *id = 0x3ff; - } // default to root dir lfs2_stag_t tag = LFS2_MKTAG(LFS2_TYPE_DIR, 0x3ff, 0); dir->tail[0] = lfs2->root[0]; dir->tail[1] = lfs2->root[1]; + // empty paths are not allowed + if (*name == '\0') { + return LFS2_ERR_INVAL; + } + while (true) { nextname: - // skip slashes - name += strspn(name, "/"); + // skip slashes if we're a directory + if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) { + name += strspn(name, "/"); + } lfs2_size_t namelen = strcspn(name, "/"); - // skip '.' and root '..' - if ((namelen == 1 && memcmp(name, ".", 1) == 0) || - (namelen == 2 && memcmp(name, "..", 2) == 0)) { + // skip '.' + if (namelen == 1 && memcmp(name, ".", 1) == 0) { name += namelen; goto nextname; } + // error on unmatched '..', trying to go above root? + if (namelen == 2 && memcmp(name, "..", 2) == 0) { + return LFS2_ERR_INVAL; + } + // skip if matched by '..' in name const char *suffix = name + namelen; lfs2_size_t sufflen; @@ -1475,7 +1525,9 @@ static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, break; } - if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { + if (sufflen == 1 && memcmp(suffix, ".", 1) == 0) { + // noop + } else if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { depth -= 1; if (depth == 0) { name = suffix + sufflen; @@ -1489,14 +1541,14 @@ static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, } // found path - if (name[0] == '\0') { + if (*name == '\0') { return tag; } // update what we've found so far *path = name; - // only continue if we hit a directory + // only continue if we're a directory if (lfs2_tag_type3(tag) != LFS2_TYPE_DIR) { return LFS2_ERR_NOTDIR; } @@ -1516,8 +1568,7 @@ static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, tag = lfs2_dir_fetchmatch(lfs2, dir, dir->tail, LFS2_MKTAG(0x780, 0, 0), LFS2_MKTAG(LFS2_TYPE_NAME, 0, namelen), - // are we last name? - (strchr(name, '/') == NULL) ? id : NULL, + id, lfs2_dir_find_match, &(struct lfs2_dir_find_match){ lfs2, name, namelen}); if (tag < 0) { @@ -2105,13 +2156,14 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, // And we cap at half a block to avoid degenerate cases with // nearly-full metadata blocks. // + lfs2_size_t metadata_max = (lfs2->cfg->metadata_max) + ? lfs2->cfg->metadata_max + : lfs2->cfg->block_size; if (end - split < 0xff && size <= lfs2_min( - lfs2->cfg->block_size - 40, + metadata_max - 40, lfs2_alignup( - (lfs2->cfg->metadata_max - ? lfs2->cfg->metadata_max - : lfs2->cfg->block_size)/2, + metadata_max/2, lfs2->cfg->prog_size))) { break; } @@ -2146,14 +2198,16 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, && lfs2_pair_cmp(dir->pair, (const lfs2_block_t[2]){0, 1}) == 0) { // oh no! we're writing too much to the superblock, // should we expand? - lfs2_ssize_t size = lfs2_fs_rawsize(lfs2); + lfs2_ssize_t size = lfs2_fs_size_(lfs2); if (size < 0) { return size; } - // do we have extra space? littlefs can't reclaim this space - // by itself, so expand cautiously - if ((lfs2_size_t)size < lfs2->block_count/2) { + // littlefs cannot reclaim expanded superblocks, so expand cautiously + // + // if our filesystem is more than ~88% full, don't expand, this is + // somewhat arbitrary + if (lfs2->block_count - size > lfs2->block_count/8) { LFS2_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); int err = lfs2_dir_split(lfs2, dir, attrs, attrcount, source, begin, end); @@ -2166,7 +2220,8 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, // we can do, we'll error later if we've become frozen LFS2_WARN("Unable to expand superblock"); } else { - end = begin; + // duplicate the superblock entry into the new superblock + end = 1; } } } @@ -2213,7 +2268,7 @@ static int lfs2_dir_relocatingcommit(lfs2_t *lfs2, lfs2_mdir_t *dir, } } - if (dir->erased) { + if (dir->erased && dir->count < 0xff) { // try to commit struct lfs2_commit commit = { .block = dir->pair[0], @@ -2312,7 +2367,8 @@ fixmlist:; if (d->m.pair != pair) { for (int i = 0; i < attrcount; i++) { if (lfs2_tag_type3(attrs[i].tag) == LFS2_TYPE_DELETE && - d->id == lfs2_tag_id(attrs[i].tag)) { + d->id == lfs2_tag_id(attrs[i].tag) && + d->type != LFS2_TYPE_DIR) { d->m.pair[0] = LFS2_BLOCK_NULL; d->m.pair[1] = LFS2_BLOCK_NULL; } else if (lfs2_tag_type3(attrs[i].tag) == LFS2_TYPE_DELETE && @@ -2333,7 +2389,9 @@ fixmlist:; while (d->id >= d->m.count && d->m.split) { // we split and id is on tail now - d->id -= d->m.count; + if (lfs2_pair_cmp(d->m.tail, lfs2->root) != 0) { + d->id -= d->m.count; + } int err = lfs2_dir_fetch(lfs2, &d->m, d->m.tail); if (err) { return err; @@ -2499,7 +2557,7 @@ static int lfs2_dir_orphaningcommit(lfs2_t *lfs2, lfs2_mdir_t *dir, if (err != LFS2_ERR_NOENT) { if (lfs2_gstate_hasorphans(&lfs2->gstate)) { // next step, clean up orphans - err = lfs2_fs_preporphans(lfs2, -hasparent); + err = lfs2_fs_preporphans(lfs2, -(int8_t)hasparent); if (err) { return err; } @@ -2564,7 +2622,7 @@ static int lfs2_dir_commit(lfs2_t *lfs2, lfs2_mdir_t *dir, /// Top level directory operations /// #ifndef LFS2_READONLY -static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) { +static int lfs2_mkdir_(lfs2_t *lfs2, const char *path) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { @@ -2575,18 +2633,18 @@ static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) { cwd.next = lfs2->mlist; uint16_t id; err = lfs2_dir_find(lfs2, &cwd.m, &path, &id); - if (!(err == LFS2_ERR_NOENT && id != 0x3ff)) { + if (!(err == LFS2_ERR_NOENT && lfs2_path_islast(path))) { return (err < 0) ? err : LFS2_ERR_EXIST; } // check that name fits - lfs2_size_t nlen = strlen(path); + lfs2_size_t nlen = lfs2_path_namelen(path); if (nlen > lfs2->name_max) { return LFS2_ERR_NAMETOOLONG; } // build up new directory - lfs2_alloc_ack(lfs2); + lfs2_alloc_ckpoint(lfs2); lfs2_mdir_t dir; err = lfs2_dir_alloc(lfs2, &dir); if (err) { @@ -2660,7 +2718,7 @@ static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) { } #endif -static int lfs2_dir_rawopen(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { +static int lfs2_dir_open_(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { lfs2_stag_t tag = lfs2_dir_find(lfs2, &dir->m, &path, NULL); if (tag < 0) { return tag; @@ -2704,14 +2762,14 @@ static int lfs2_dir_rawopen(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { return 0; } -static int lfs2_dir_rawclose(lfs2_t *lfs2, lfs2_dir_t *dir) { +static int lfs2_dir_close_(lfs2_t *lfs2, lfs2_dir_t *dir) { // remove from list of mdirs lfs2_mlist_remove(lfs2, (struct lfs2_mlist *)dir); return 0; } -static int lfs2_dir_rawread(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { +static int lfs2_dir_read_(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { memset(info, 0, sizeof(*info)); // special offset for '.' and '..' @@ -2756,9 +2814,9 @@ static int lfs2_dir_rawread(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *inf return true; } -static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { +static int lfs2_dir_seek_(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { // simply walk from head dir - int err = lfs2_dir_rawrewind(lfs2, dir); + int err = lfs2_dir_rewind_(lfs2, dir); if (err) { return err; } @@ -2793,12 +2851,12 @@ static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { return 0; } -static lfs2_soff_t lfs2_dir_rawtell(lfs2_t *lfs2, lfs2_dir_t *dir) { +static lfs2_soff_t lfs2_dir_tell_(lfs2_t *lfs2, lfs2_dir_t *dir) { (void)lfs2; return dir->pos; } -static int lfs2_dir_rawrewind(lfs2_t *lfs2, lfs2_dir_t *dir) { +static int lfs2_dir_rewind_(lfs2_t *lfs2, lfs2_dir_t *dir) { // reload the head dir int err = lfs2_dir_fetch(lfs2, &dir->m, dir->head); if (err) { @@ -3004,7 +3062,7 @@ static int lfs2_ctz_traverse(lfs2_t *lfs2, /// Top level file operations /// -static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, +static int lfs2_file_opencfg_(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags, const struct lfs2_file_config *cfg) { #ifndef LFS2_READONLY @@ -3029,7 +3087,7 @@ static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, // allocate entry for file if it doesn't exist lfs2_stag_t tag = lfs2_dir_find(lfs2, &file->m, &path, &file->id); - if (tag < 0 && !(tag == LFS2_ERR_NOENT && file->id != 0x3ff)) { + if (tag < 0 && !(tag == LFS2_ERR_NOENT && lfs2_path_islast(path))) { err = tag; goto cleanup; } @@ -3049,8 +3107,14 @@ static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, goto cleanup; } + // don't allow trailing slashes + if (lfs2_path_isdir(path)) { + err = LFS2_ERR_NOTDIR; + goto cleanup; + } + // check that name fits - lfs2_size_t nlen = strlen(path); + lfs2_size_t nlen = lfs2_path_namelen(path); if (nlen > lfs2->name_max) { err = LFS2_ERR_NAMETOOLONG; goto cleanup; @@ -3166,22 +3230,22 @@ static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, #ifndef LFS2_READONLY file->flags |= LFS2_F_ERRED; #endif - lfs2_file_rawclose(lfs2, file); + lfs2_file_close_(lfs2, file); return err; } #ifndef LFS2_NO_MALLOC -static int lfs2_file_rawopen(lfs2_t *lfs2, lfs2_file_t *file, +static int lfs2_file_open_(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags) { static const struct lfs2_file_config defaults = {0}; - int err = lfs2_file_rawopencfg(lfs2, file, path, flags, &defaults); + int err = lfs2_file_opencfg_(lfs2, file, path, flags, &defaults); return err; } #endif -static int lfs2_file_rawclose(lfs2_t *lfs2, lfs2_file_t *file) { +static int lfs2_file_close_(lfs2_t *lfs2, lfs2_file_t *file) { #ifndef LFS2_READONLY - int err = lfs2_file_rawsync(lfs2, file); + int err = lfs2_file_sync_(lfs2, file); #else int err = 0; #endif @@ -3272,7 +3336,7 @@ static int lfs2_file_relocate(lfs2_t *lfs2, lfs2_file_t *file) { #ifndef LFS2_READONLY static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file) { file->off = file->pos; - lfs2_alloc_ack(lfs2); + lfs2_alloc_ckpoint(lfs2); int err = lfs2_file_relocate(lfs2, file); if (err) { return err; @@ -3364,7 +3428,7 @@ static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { } #ifndef LFS2_READONLY -static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) { +static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file) { if (file->flags & LFS2_F_ERRED) { // it's not safe to do anything if our file errored return 0; @@ -3379,6 +3443,15 @@ static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) { if ((file->flags & LFS2_F_DIRTY) && !lfs2_pair_isnull(file->m.pair)) { + // before we commit metadata, we need sync the disk to make sure + // data writes don't complete after metadata writes + if (!(file->flags & LFS2_F_INLINE)) { + err = lfs2_bd_sync(lfs2, &lfs2->pcache, &lfs2->rcache, false); + if (err) { + return err; + } + } + // update dir entry uint16_t type; const void *buffer; @@ -3477,7 +3550,7 @@ static lfs2_ssize_t lfs2_file_flushedread(lfs2_t *lfs2, lfs2_file_t *file, return size; } -static lfs2_ssize_t lfs2_file_rawread(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_read_(lfs2_t *lfs2, lfs2_file_t *file, void *buffer, lfs2_size_t size) { LFS2_ASSERT((file->flags & LFS2_O_RDONLY) == LFS2_O_RDONLY); @@ -3502,11 +3575,7 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, lfs2_size_t nsize = size; if ((file->flags & LFS2_F_INLINE) && - lfs2_max(file->pos+nsize, file->ctz.size) > - lfs2_min(0x3fe, lfs2_min( - lfs2->cfg->cache_size, - (lfs2->cfg->metadata_max ? - lfs2->cfg->metadata_max : lfs2->cfg->block_size) / 8))) { + lfs2_max(file->pos+nsize, file->ctz.size) > lfs2->inline_max) { // inline file doesn't fit anymore int err = lfs2_file_outline(lfs2, file); if (err) { @@ -3535,7 +3604,7 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, } // extend file with new blocks - lfs2_alloc_ack(lfs2); + lfs2_alloc_ckpoint(lfs2); int err = lfs2_ctz_extend(lfs2, &file->cache, &lfs2->rcache, file->block, file->pos, &file->block, &file->off); @@ -3578,13 +3647,13 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, data += diff; nsize -= diff; - lfs2_alloc_ack(lfs2); + lfs2_alloc_ckpoint(lfs2); } return size; } -static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_write_(lfs2_t *lfs2, lfs2_file_t *file, const void *buffer, lfs2_size_t size) { LFS2_ASSERT((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY); @@ -3628,25 +3697,19 @@ static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file, } #endif -static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_soff_t lfs2_file_seek_(lfs2_t *lfs2, lfs2_file_t *file, lfs2_soff_t off, int whence) { // find new pos + // + // fortunately for us, littlefs is limited to 31-bit file sizes, so we + // don't have to worry too much about integer overflow lfs2_off_t npos = file->pos; if (whence == LFS2_SEEK_SET) { npos = off; } else if (whence == LFS2_SEEK_CUR) { - if ((lfs2_soff_t)file->pos + off < 0) { - return LFS2_ERR_INVAL; - } else { - npos = file->pos + off; - } + npos = file->pos + (lfs2_off_t)off; } else if (whence == LFS2_SEEK_END) { - lfs2_soff_t res = lfs2_file_rawsize(lfs2, file) + off; - if (res < 0) { - return LFS2_ERR_INVAL; - } else { - npos = res; - } + npos = (lfs2_off_t)lfs2_file_size_(lfs2, file) + (lfs2_off_t)off; } if (npos > lfs2->file_max) { @@ -3661,13 +3724,8 @@ static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file, // if we're only reading and our new offset is still in the file's cache // we can avoid flushing and needing to reread the data - if ( -#ifndef LFS2_READONLY - !(file->flags & LFS2_F_WRITING) -#else - true -#endif - ) { + if ((file->flags & LFS2_F_READING) + && file->off != lfs2->cfg->block_size) { int oindex = lfs2_ctz_index(lfs2, &(lfs2_off_t){file->pos}); lfs2_off_t noff = npos; int nindex = lfs2_ctz_index(lfs2, &noff); @@ -3692,7 +3750,7 @@ static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file, } #ifndef LFS2_READONLY -static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { +static int lfs2_file_truncate_(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { LFS2_ASSERT((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY); if (size > LFS2_FILE_MAX) { @@ -3700,15 +3758,12 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz } lfs2_off_t pos = file->pos; - lfs2_off_t oldsize = lfs2_file_rawsize(lfs2, file); + lfs2_off_t oldsize = lfs2_file_size_(lfs2, file); if (size < oldsize) { // revert to inline file? - if (size <= lfs2_min(0x3fe, lfs2_min( - lfs2->cfg->cache_size, - (lfs2->cfg->metadata_max ? - lfs2->cfg->metadata_max : lfs2->cfg->block_size) / 8))) { + if (size <= lfs2->inline_max) { // flush+seek to head - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_SET); + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_SET); if (res < 0) { return (int)res; } @@ -3753,14 +3808,14 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz } } else if (size > oldsize) { // flush+seek if not already at end - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_END); + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_END); if (res < 0) { return (int)res; } // fill with zeros while (file->pos < size) { - res = lfs2_file_rawwrite(lfs2, file, &(uint8_t){0}, 1); + res = lfs2_file_write_(lfs2, file, &(uint8_t){0}, 1); if (res < 0) { return (int)res; } @@ -3768,7 +3823,7 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz } // restore pos - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, pos, LFS2_SEEK_SET); + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, pos, LFS2_SEEK_SET); if (res < 0) { return (int)res; } @@ -3777,13 +3832,13 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz } #endif -static lfs2_soff_t lfs2_file_rawtell(lfs2_t *lfs2, lfs2_file_t *file) { +static lfs2_soff_t lfs2_file_tell_(lfs2_t *lfs2, lfs2_file_t *file) { (void)lfs2; return file->pos; } -static int lfs2_file_rawrewind(lfs2_t *lfs2, lfs2_file_t *file) { - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_SET); +static int lfs2_file_rewind_(lfs2_t *lfs2, lfs2_file_t *file) { + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_SET); if (res < 0) { return (int)res; } @@ -3791,7 +3846,7 @@ static int lfs2_file_rawrewind(lfs2_t *lfs2, lfs2_file_t *file) { return 0; } -static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file) { +static lfs2_soff_t lfs2_file_size_(lfs2_t *lfs2, lfs2_file_t *file) { (void)lfs2; #ifndef LFS2_READONLY @@ -3805,18 +3860,24 @@ static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file) { /// General fs operations /// -static int lfs2_rawstat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { +static int lfs2_stat_(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { lfs2_mdir_t cwd; lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); if (tag < 0) { return (int)tag; } + // only allow trailing slashes on dirs + if (strchr(path, '/') != NULL + && lfs2_tag_type3(tag) != LFS2_TYPE_DIR) { + return LFS2_ERR_NOTDIR; + } + return lfs2_dir_getinfo(lfs2, &cwd, lfs2_tag_id(tag), info); } #ifndef LFS2_READONLY -static int lfs2_rawremove(lfs2_t *lfs2, const char *path) { +static int lfs2_remove_(lfs2_t *lfs2, const char *path) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { @@ -3872,7 +3933,9 @@ static int lfs2_rawremove(lfs2_t *lfs2, const char *path) { } lfs2->mlist = dir.next; - if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) { + if (lfs2_gstate_hasorphans(&lfs2->gstate)) { + LFS2_ASSERT(lfs2_tag_type3(tag) == LFS2_TYPE_DIR); + // fix orphan err = lfs2_fs_preporphans(lfs2, -1); if (err) { @@ -3895,7 +3958,7 @@ static int lfs2_rawremove(lfs2_t *lfs2, const char *path) { #endif #ifndef LFS2_READONLY -static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { +static int lfs2_rename_(lfs2_t *lfs2, const char *oldpath, const char *newpath) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { @@ -3914,7 +3977,7 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath uint16_t newid; lfs2_stag_t prevtag = lfs2_dir_find(lfs2, &newcwd, &newpath, &newid); if ((prevtag < 0 || lfs2_tag_id(prevtag) == 0x3ff) && - !(prevtag == LFS2_ERR_NOENT && newid != 0x3ff)) { + !(prevtag == LFS2_ERR_NOENT && lfs2_path_islast(newpath))) { return (prevtag < 0) ? (int)prevtag : LFS2_ERR_INVAL; } @@ -3925,8 +3988,14 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath struct lfs2_mlist prevdir; prevdir.next = lfs2->mlist; if (prevtag == LFS2_ERR_NOENT) { + // if we're a file, don't allow trailing slashes + if (lfs2_path_isdir(newpath) + && lfs2_tag_type3(oldtag) != LFS2_TYPE_DIR) { + return LFS2_ERR_NOTDIR; + } + // check that name fits - lfs2_size_t nlen = strlen(newpath); + lfs2_size_t nlen = lfs2_path_namelen(newpath); if (nlen > lfs2->name_max) { return LFS2_ERR_NAMETOOLONG; } @@ -3938,7 +4007,9 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath newoldid += 1; } } else if (lfs2_tag_type3(prevtag) != lfs2_tag_type3(oldtag)) { - return LFS2_ERR_ISDIR; + return (lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR) + ? LFS2_ERR_ISDIR + : LFS2_ERR_NOTDIR; } else if (samepair && newid == newoldid) { // we're renaming to ourselves?? return 0; @@ -3984,7 +4055,8 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath {LFS2_MKTAG_IF(prevtag != LFS2_ERR_NOENT, LFS2_TYPE_DELETE, newid, 0), NULL}, {LFS2_MKTAG(LFS2_TYPE_CREATE, newid, 0), NULL}, - {LFS2_MKTAG(lfs2_tag_type3(oldtag), newid, strlen(newpath)), newpath}, + {LFS2_MKTAG(lfs2_tag_type3(oldtag), + newid, lfs2_path_namelen(newpath)), newpath}, {LFS2_MKTAG(LFS2_FROM_MOVE, newid, lfs2_tag_id(oldtag)), &oldcwd}, {LFS2_MKTAG_IF(samepair, LFS2_TYPE_DELETE, newoldid, 0), NULL})); @@ -4007,8 +4079,10 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath } lfs2->mlist = prevdir.next; - if (prevtag != LFS2_ERR_NOENT - && lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR) { + if (lfs2_gstate_hasorphans(&lfs2->gstate)) { + LFS2_ASSERT(prevtag != LFS2_ERR_NOENT + && lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR); + // fix orphan err = lfs2_fs_preporphans(lfs2, -1); if (err) { @@ -4030,7 +4104,7 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath } #endif -static lfs2_ssize_t lfs2_rawgetattr(lfs2_t *lfs2, const char *path, +static lfs2_ssize_t lfs2_getattr_(lfs2_t *lfs2, const char *path, uint8_t type, void *buffer, lfs2_size_t size) { lfs2_mdir_t cwd; lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); @@ -4088,7 +4162,7 @@ static int lfs2_commitattr(lfs2_t *lfs2, const char *path, #endif #ifndef LFS2_READONLY -static int lfs2_rawsetattr(lfs2_t *lfs2, const char *path, +static int lfs2_setattr_(lfs2_t *lfs2, const char *path, uint8_t type, const void *buffer, lfs2_size_t size) { if (size > lfs2->attr_max) { return LFS2_ERR_NOSPC; @@ -4099,13 +4173,28 @@ static int lfs2_rawsetattr(lfs2_t *lfs2, const char *path, #endif #ifndef LFS2_READONLY -static int lfs2_rawremoveattr(lfs2_t *lfs2, const char *path, uint8_t type) { +static int lfs2_removeattr_(lfs2_t *lfs2, const char *path, uint8_t type) { return lfs2_commitattr(lfs2, path, type, NULL, 0x3ff); } #endif /// Filesystem operations /// + +// compile time checks, see lfs2.h for why these limits exist +#if LFS2_NAME_MAX > 1022 +#error "Invalid LFS2_NAME_MAX, must be <= 1022" +#endif + +#if LFS2_FILE_MAX > 2147483647 +#error "Invalid LFS2_FILE_MAX, must be <= 2147483647" +#endif + +#if LFS2_ATTR_MAX > 1022 +#error "Invalid LFS2_ATTR_MAX, must be <= 1022" +#endif + +// common filesystem initialization static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { lfs2->cfg = cfg; lfs2->block_count = cfg->block_count; // May be 0 @@ -4126,6 +4215,14 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { // which littlefs currently does not support LFS2_ASSERT((bool)0x80000000); + // check that the required io functions are provided + LFS2_ASSERT(lfs2->cfg->read != NULL); +#ifndef LFS2_READONLY + LFS2_ASSERT(lfs2->cfg->prog != NULL); + LFS2_ASSERT(lfs2->cfg->erase != NULL); + LFS2_ASSERT(lfs2->cfg->sync != NULL); +#endif + // validate that the lfs2-cfg sizes were initiated properly before // performing any arithmetic logics with them LFS2_ASSERT(lfs2->cfg->read_size != 0); @@ -4153,6 +4250,23 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { // wear-leveling. LFS2_ASSERT(lfs2->cfg->block_cycles != 0); + // check that compact_thresh makes sense + // + // metadata can't be compacted below block_size/2, and metadata can't + // exceed a block_size + LFS2_ASSERT(lfs2->cfg->compact_thresh == 0 + || lfs2->cfg->compact_thresh >= lfs2->cfg->block_size/2); + LFS2_ASSERT(lfs2->cfg->compact_thresh == (lfs2_size_t)-1 + || lfs2->cfg->compact_thresh <= lfs2->cfg->block_size); + + // check that metadata_max is a multiple of read_size and prog_size, + // and a factor of the block_size + LFS2_ASSERT(!lfs2->cfg->metadata_max + || lfs2->cfg->metadata_max % lfs2->cfg->read_size == 0); + LFS2_ASSERT(!lfs2->cfg->metadata_max + || lfs2->cfg->metadata_max % lfs2->cfg->prog_size == 0); + LFS2_ASSERT(!lfs2->cfg->metadata_max + || lfs2->cfg->block_size % lfs2->cfg->metadata_max == 0); // setup read cache if (lfs2->cfg->read_buffer) { @@ -4180,15 +4294,14 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { lfs2_cache_zero(lfs2, &lfs2->rcache); lfs2_cache_zero(lfs2, &lfs2->pcache); - // setup lookahead, must be multiple of 64-bits, 32-bit aligned + // setup lookahead buffer, note mount finishes initializing this after + // we establish a decent pseudo-random seed LFS2_ASSERT(lfs2->cfg->lookahead_size > 0); - LFS2_ASSERT(lfs2->cfg->lookahead_size % 8 == 0 && - (uintptr_t)lfs2->cfg->lookahead_buffer % 4 == 0); if (lfs2->cfg->lookahead_buffer) { - lfs2->free.buffer = lfs2->cfg->lookahead_buffer; + lfs2->lookahead.buffer = lfs2->cfg->lookahead_buffer; } else { - lfs2->free.buffer = lfs2_malloc(lfs2->cfg->lookahead_size); - if (!lfs2->free.buffer) { + lfs2->lookahead.buffer = lfs2_malloc(lfs2->cfg->lookahead_size); + if (!lfs2->lookahead.buffer) { err = LFS2_ERR_NOMEM; goto cleanup; } @@ -4215,6 +4328,27 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { LFS2_ASSERT(lfs2->cfg->metadata_max <= lfs2->cfg->block_size); + LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1 + || lfs2->cfg->inline_max <= lfs2->cfg->cache_size); + LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1 + || lfs2->cfg->inline_max <= lfs2->attr_max); + LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1 + || lfs2->cfg->inline_max <= ((lfs2->cfg->metadata_max) + ? lfs2->cfg->metadata_max + : lfs2->cfg->block_size)/8); + lfs2->inline_max = lfs2->cfg->inline_max; + if (lfs2->inline_max == (lfs2_size_t)-1) { + lfs2->inline_max = 0; + } else if (lfs2->inline_max == 0) { + lfs2->inline_max = lfs2_min( + lfs2->cfg->cache_size, + lfs2_min( + lfs2->attr_max, + ((lfs2->cfg->metadata_max) + ? lfs2->cfg->metadata_max + : lfs2->cfg->block_size)/8)); + } + // setup default state lfs2->root[0] = LFS2_BLOCK_NULL; lfs2->root[1] = LFS2_BLOCK_NULL; @@ -4245,7 +4379,7 @@ static int lfs2_deinit(lfs2_t *lfs2) { } if (!lfs2->cfg->lookahead_buffer) { - lfs2_free(lfs2->free.buffer); + lfs2_free(lfs2->lookahead.buffer); } return 0; @@ -4254,7 +4388,7 @@ static int lfs2_deinit(lfs2_t *lfs2) { #ifndef LFS2_READONLY -static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { +static int lfs2_format_(lfs2_t *lfs2, const struct lfs2_config *cfg) { int err = 0; { err = lfs2_init(lfs2, cfg); @@ -4265,12 +4399,12 @@ static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { LFS2_ASSERT(cfg->block_count != 0); // create free lookahead - memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); - lfs2->free.off = 0; - lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, + memset(lfs2->lookahead.buffer, 0, lfs2->cfg->lookahead_size); + lfs2->lookahead.start = 0; + lfs2->lookahead.size = lfs2_min(8*lfs2->cfg->lookahead_size, lfs2->block_count); - lfs2->free.i = 0; - lfs2_alloc_ack(lfs2); + lfs2->lookahead.next = 0; + lfs2_alloc_ckpoint(lfs2); // create root dir lfs2_mdir_t root; @@ -4321,7 +4455,31 @@ static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { } #endif -static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { +struct lfs2_tortoise_t { + lfs2_block_t pair[2]; + lfs2_size_t i; + lfs2_size_t period; +}; + +static int lfs2_tortoise_detectcycles( + const lfs2_mdir_t *dir, struct lfs2_tortoise_t *tortoise) { + // detect cycles with Brent's algorithm + if (lfs2_pair_issync(dir->tail, tortoise->pair)) { + LFS2_WARN("Cycle detected in tail list"); + return LFS2_ERR_CORRUPT; + } + if (tortoise->i == tortoise->period) { + tortoise->pair[0] = dir->tail[0]; + tortoise->pair[1] = dir->tail[1]; + tortoise->i = 0; + tortoise->period *= 2; + } + tortoise->i += 1; + + return LFS2_ERR_OK; +} + +static int lfs2_mount_(lfs2_t *lfs2, const struct lfs2_config *cfg) { int err = lfs2_init(lfs2, cfg); if (err) { return err; @@ -4329,23 +4487,16 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // scan directory blocks for superblock and any global updates lfs2_mdir_t dir = {.tail = {0, 1}}; - lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; - lfs2_size_t tortoise_i = 1; - lfs2_size_t tortoise_period = 1; + struct lfs2_tortoise_t tortoise = { + .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}, + .i = 1, + .period = 1, + }; while (!lfs2_pair_isnull(dir.tail)) { - // detect cycles with Brent's algorithm - if (lfs2_pair_issync(dir.tail, tortoise)) { - LFS2_WARN("Cycle detected in tail list"); - err = LFS2_ERR_CORRUPT; + err = lfs2_tortoise_detectcycles(&dir, &tortoise); + if (err < 0) { goto cleanup; } - if (tortoise_i == tortoise_period) { - tortoise[0] = dir.tail[0]; - tortoise[1] = dir.tail[1]; - tortoise_i = 0; - tortoise_period *= 2; - } - tortoise_i += 1; // fetch next block in tail list lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, &dir, dir.tail, @@ -4394,6 +4545,7 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // found older minor version? set an in-device only bit in the // gstate so we know we need to rewrite the superblock before // the first write + bool needssuperblock = false; if (minor_version < lfs2_fs_disk_version_minor(lfs2)) { LFS2_DEBUG("Found older minor version " "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, @@ -4401,10 +4553,11 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { minor_version, lfs2_fs_disk_version_major(lfs2), lfs2_fs_disk_version_minor(lfs2)); - // note this bit is reserved on disk, so fetching more gstate - // will not interfere here - lfs2_fs_prepsuperblock(lfs2, true); + needssuperblock = true; } + // note this bit is reserved on disk, so fetching more gstate + // will not interfere here + lfs2_fs_prepsuperblock(lfs2, needssuperblock); // check superblock configuration if (superblock.name_max) { @@ -4438,6 +4591,9 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { } lfs2->attr_max = superblock.attr_max; + + // we also need to update inline_max in case attr_max changed + lfs2->inline_max = lfs2_min(lfs2->inline_max, lfs2->attr_max); } // this is where we get the block_count from disk if block_count=0 @@ -4478,23 +4634,23 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // setup free lookahead, to distribute allocations uniformly across // boots, we start the allocator at a random location - lfs2->free.off = lfs2->seed % lfs2->block_count; + lfs2->lookahead.start = lfs2->seed % lfs2->block_count; lfs2_alloc_drop(lfs2); return 0; cleanup: - lfs2_rawunmount(lfs2); + lfs2_unmount_(lfs2); return err; } -static int lfs2_rawunmount(lfs2_t *lfs2) { +static int lfs2_unmount_(lfs2_t *lfs2) { return lfs2_deinit(lfs2); } /// Filesystem filesystem operations /// -static int lfs2_fs_rawstat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { +static int lfs2_fs_stat_(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { // if the superblock is up-to-date, we must be on the most recent // minor version of littlefs if (!lfs2_gstate_needssuperblock(&lfs2->gstate)) { @@ -4534,7 +4690,7 @@ static int lfs2_fs_rawstat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { return 0; } -int lfs2_fs_rawtraverse(lfs2_t *lfs2, +int lfs2_fs_traverse_(lfs2_t *lfs2, int (*cb)(void *data, lfs2_block_t block), void *data, bool includeorphans) { // iterate over metadata pairs @@ -4553,22 +4709,17 @@ int lfs2_fs_rawtraverse(lfs2_t *lfs2, } #endif - lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; - lfs2_size_t tortoise_i = 1; - lfs2_size_t tortoise_period = 1; + struct lfs2_tortoise_t tortoise = { + .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}, + .i = 1, + .period = 1, + }; + int err = LFS2_ERR_OK; while (!lfs2_pair_isnull(dir.tail)) { - // detect cycles with Brent's algorithm - if (lfs2_pair_issync(dir.tail, tortoise)) { - LFS2_WARN("Cycle detected in tail list"); + err = lfs2_tortoise_detectcycles(&dir, &tortoise); + if (err < 0) { return LFS2_ERR_CORRUPT; } - if (tortoise_i == tortoise_period) { - tortoise[0] = dir.tail[0]; - tortoise[1] = dir.tail[1]; - tortoise_i = 0; - tortoise_period *= 2; - } - tortoise_i += 1; for (int i = 0; i < 2; i++) { int err = cb(data, dir.tail[i]); @@ -4647,22 +4798,17 @@ static int lfs2_fs_pred(lfs2_t *lfs2, // iterate over all directory directory entries pdir->tail[0] = 0; pdir->tail[1] = 1; - lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; - lfs2_size_t tortoise_i = 1; - lfs2_size_t tortoise_period = 1; + struct lfs2_tortoise_t tortoise = { + .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}, + .i = 1, + .period = 1, + }; + int err = LFS2_ERR_OK; while (!lfs2_pair_isnull(pdir->tail)) { - // detect cycles with Brent's algorithm - if (lfs2_pair_issync(pdir->tail, tortoise)) { - LFS2_WARN("Cycle detected in tail list"); + err = lfs2_tortoise_detectcycles(pdir, &tortoise); + if (err < 0) { return LFS2_ERR_CORRUPT; } - if (tortoise_i == tortoise_period) { - tortoise[0] = pdir->tail[0]; - tortoise[1] = pdir->tail[1]; - tortoise_i = 0; - tortoise_period *= 2; - } - tortoise_i += 1; if (lfs2_pair_cmp(pdir->tail, pair) == 0) { return 0; @@ -4712,22 +4858,17 @@ static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t pair[2], // use fetchmatch with callback to find pairs parent->tail[0] = 0; parent->tail[1] = 1; - lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; - lfs2_size_t tortoise_i = 1; - lfs2_size_t tortoise_period = 1; + struct lfs2_tortoise_t tortoise = { + .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}, + .i = 1, + .period = 1, + }; + int err = LFS2_ERR_OK; while (!lfs2_pair_isnull(parent->tail)) { - // detect cycles with Brent's algorithm - if (lfs2_pair_issync(parent->tail, tortoise)) { - LFS2_WARN("Cycle detected in tail list"); - return LFS2_ERR_CORRUPT; - } - if (tortoise_i == tortoise_period) { - tortoise[0] = parent->tail[0]; - tortoise[1] = parent->tail[1]; - tortoise_i = 0; - tortoise_period *= 2; + err = lfs2_tortoise_detectcycles(parent, &tortoise); + if (err < 0) { + return err; } - tortoise_i += 1; lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, parent, parent->tail, LFS2_MKTAG(0x7ff, 0, 0x3ff), @@ -4999,7 +5140,7 @@ static int lfs2_fs_forceconsistency(lfs2_t *lfs2) { #endif #ifndef LFS2_READONLY -static int lfs2_fs_rawmkconsistent(lfs2_t *lfs2) { +static int lfs2_fs_mkconsistent_(lfs2_t *lfs2) { // lfs2_fs_forceconsistency does most of the work here int err = lfs2_fs_forceconsistency(lfs2); if (err) { @@ -5035,9 +5176,9 @@ static int lfs2_fs_size_count(void *p, lfs2_block_t block) { return 0; } -static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2) { +static lfs2_ssize_t lfs2_fs_size_(lfs2_t *lfs2) { lfs2_size_t size = 0; - int err = lfs2_fs_rawtraverse(lfs2, lfs2_fs_size_count, &size, false); + int err = lfs2_fs_traverse_(lfs2, lfs2_fs_size_count, &size, false); if (err) { return err; } @@ -5045,41 +5186,118 @@ static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2) { return size; } +// explicit garbage collection #ifndef LFS2_READONLY -static int lfs2_fs_rawgrow(lfs2_t *lfs2, lfs2_size_t block_count) { - // shrinking is not supported - LFS2_ASSERT(block_count >= lfs2->block_count); +static int lfs2_fs_gc_(lfs2_t *lfs2) { + // force consistency, even if we're not necessarily going to write, + // because this function is supposed to take care of janitorial work + // isn't it? + int err = lfs2_fs_forceconsistency(lfs2); + if (err) { + return err; + } - if (block_count > lfs2->block_count) { - lfs2->block_count = block_count; + // try to compact metadata pairs, note we can't really accomplish + // anything if compact_thresh doesn't at least leave a prog_size + // available + if (lfs2->cfg->compact_thresh + < lfs2->cfg->block_size - lfs2->cfg->prog_size) { + // iterate over all mdirs + lfs2_mdir_t mdir = {.tail = {0, 1}}; + while (!lfs2_pair_isnull(mdir.tail)) { + err = lfs2_dir_fetch(lfs2, &mdir, mdir.tail); + if (err) { + return err; + } - // fetch the root - lfs2_mdir_t root; - int err = lfs2_dir_fetch(lfs2, &root, lfs2->root); + // not erased? exceeds our compaction threshold? + if (!mdir.erased || ((lfs2->cfg->compact_thresh == 0) + ? mdir.off > lfs2->cfg->block_size - lfs2->cfg->block_size/8 + : mdir.off > lfs2->cfg->compact_thresh)) { + // the easiest way to trigger a compaction is to mark + // the mdir as unerased and add an empty commit + mdir.erased = false; + err = lfs2_dir_commit(lfs2, &mdir, NULL, 0); + if (err) { + return err; + } + } + } + } + + // try to populate the lookahead buffer, unless it's already full + if (lfs2->lookahead.size < lfs2_min( + 8 * lfs2->cfg->lookahead_size, + lfs2->block_count)) { + err = lfs2_alloc_scan(lfs2); if (err) { return err; } + } - // update the superblock - lfs2_superblock_t superblock; - lfs2_stag_t tag = lfs2_dir_get(lfs2, &root, LFS2_MKTAG(0x7ff, 0x3ff, 0), - LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock); - if (tag < 0) { - return tag; - } - lfs2_superblock_fromle32(&superblock); + return 0; +} +#endif - superblock.block_count = lfs2->block_count; +#ifndef LFS2_READONLY +#ifdef LFS2_SHRINKNONRELOCATING +static int lfs2_shrink_checkblock(void *data, lfs2_block_t block) { + lfs2_size_t threshold = *((lfs2_size_t*)data); + if (block >= threshold) { + return LFS2_ERR_NOTEMPTY; + } + return 0; +} +#endif - lfs2_superblock_tole32(&superblock); - err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS( - {tag, &superblock})); +static int lfs2_fs_grow_(lfs2_t *lfs2, lfs2_size_t block_count) { + int err; + + if (block_count == lfs2->block_count) { + return 0; + } + + +#ifndef LFS2_SHRINKNONRELOCATING + // shrinking is not supported + LFS2_ASSERT(block_count >= lfs2->block_count); +#endif +#ifdef LFS2_SHRINKNONRELOCATING + if (block_count < lfs2->block_count) { + err = lfs2_fs_traverse_(lfs2, lfs2_shrink_checkblock, &block_count, true); if (err) { return err; } } +#endif + + lfs2->block_count = block_count; + // fetch the root + lfs2_mdir_t root; + err = lfs2_dir_fetch(lfs2, &root, lfs2->root); + if (err) { + return err; + } + + // update the superblock + lfs2_superblock_t superblock; + lfs2_stag_t tag = lfs2_dir_get(lfs2, &root, LFS2_MKTAG(0x7ff, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock); + if (tag < 0) { + return tag; + } + lfs2_superblock_fromle32(&superblock); + + superblock.block_count = lfs2->block_count; + + lfs2_superblock_tole32(&superblock); + err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS( + {tag, &superblock})); + if (err) { + return err; + } return 0; } #endif @@ -5451,10 +5669,10 @@ static int lfs21_mount(lfs2_t *lfs2, struct lfs21 *lfs21, lfs2->lfs21->root[1] = LFS2_BLOCK_NULL; // setup free lookahead - lfs2->free.off = 0; - lfs2->free.size = 0; - lfs2->free.i = 0; - lfs2_alloc_ack(lfs2); + lfs2->lookahead.start = 0; + lfs2->lookahead.size = 0; + lfs2->lookahead.next = 0; + lfs2_alloc_ckpoint(lfs2); // load superblock lfs21_dir_t dir; @@ -5505,7 +5723,7 @@ static int lfs21_unmount(lfs2_t *lfs2) { } /// v1 migration /// -static int lfs2_rawmigrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { +static int lfs2_migrate_(lfs2_t *lfs2, const struct lfs2_config *cfg) { struct lfs21 lfs21; // Indeterminate filesystem size not allowed for migration. @@ -5759,7 +5977,7 @@ int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { ".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".block_cycles=%"PRId32", .cache_size=%"PRIu32", " ".lookahead_size=%"PRIu32", .read_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, " ".name_max=%"PRIu32", .file_max=%"PRIu32", " @@ -5772,7 +5990,7 @@ int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs2_rawformat(lfs2, cfg); + err = lfs2_format_(lfs2, cfg); LFS2_TRACE("lfs2_format -> %d", err); LFS2_UNLOCK(cfg); @@ -5789,7 +6007,7 @@ int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) { ".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".block_cycles=%"PRId32", .cache_size=%"PRIu32", " ".lookahead_size=%"PRIu32", .read_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, " ".name_max=%"PRIu32", .file_max=%"PRIu32", " @@ -5802,7 +6020,7 @@ int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs2_rawmount(lfs2, cfg); + err = lfs2_mount_(lfs2, cfg); LFS2_TRACE("lfs2_mount -> %d", err); LFS2_UNLOCK(cfg); @@ -5816,7 +6034,7 @@ int lfs2_unmount(lfs2_t *lfs2) { } LFS2_TRACE("lfs2_unmount(%p)", (void*)lfs2); - err = lfs2_rawunmount(lfs2); + err = lfs2_unmount_(lfs2); LFS2_TRACE("lfs2_unmount -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5831,7 +6049,7 @@ int lfs2_remove(lfs2_t *lfs2, const char *path) { } LFS2_TRACE("lfs2_remove(%p, \"%s\")", (void*)lfs2, path); - err = lfs2_rawremove(lfs2, path); + err = lfs2_remove_(lfs2, path); LFS2_TRACE("lfs2_remove -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5847,7 +6065,7 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { } LFS2_TRACE("lfs2_rename(%p, \"%s\", \"%s\")", (void*)lfs2, oldpath, newpath); - err = lfs2_rawrename(lfs2, oldpath, newpath); + err = lfs2_rename_(lfs2, oldpath, newpath); LFS2_TRACE("lfs2_rename -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5862,7 +6080,7 @@ int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { } LFS2_TRACE("lfs2_stat(%p, \"%s\", %p)", (void*)lfs2, path, (void*)info); - err = lfs2_rawstat(lfs2, path, info); + err = lfs2_stat_(lfs2, path, info); LFS2_TRACE("lfs2_stat -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5878,7 +6096,7 @@ lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, LFS2_TRACE("lfs2_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", (void*)lfs2, path, type, buffer, size); - lfs2_ssize_t res = lfs2_rawgetattr(lfs2, path, type, buffer, size); + lfs2_ssize_t res = lfs2_getattr_(lfs2, path, type, buffer, size); LFS2_TRACE("lfs2_getattr -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -5895,7 +6113,7 @@ int lfs2_setattr(lfs2_t *lfs2, const char *path, LFS2_TRACE("lfs2_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", (void*)lfs2, path, type, buffer, size); - err = lfs2_rawsetattr(lfs2, path, type, buffer, size); + err = lfs2_setattr_(lfs2, path, type, buffer, size); LFS2_TRACE("lfs2_setattr -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5911,7 +6129,7 @@ int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type) { } LFS2_TRACE("lfs2_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs2, path, type); - err = lfs2_rawremoveattr(lfs2, path, type); + err = lfs2_removeattr_(lfs2, path, type); LFS2_TRACE("lfs2_removeattr -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5926,10 +6144,10 @@ int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags) return err; } LFS2_TRACE("lfs2_file_open(%p, %p, \"%s\", %x)", - (void*)lfs2, (void*)file, path, flags); + (void*)lfs2, (void*)file, path, (unsigned)flags); LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawopen(lfs2, file, path, flags); + err = lfs2_file_open_(lfs2, file, path, flags); LFS2_TRACE("lfs2_file_open -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5946,11 +6164,11 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, } LFS2_TRACE("lfs2_file_opencfg(%p, %p, \"%s\", %x, %p {" ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", - (void*)lfs2, (void*)file, path, flags, + (void*)lfs2, (void*)file, path, (unsigned)flags, (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawopencfg(lfs2, file, path, flags, cfg); + err = lfs2_file_opencfg_(lfs2, file, path, flags, cfg); LFS2_TRACE("lfs2_file_opencfg -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5965,7 +6183,7 @@ int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file) { LFS2_TRACE("lfs2_file_close(%p, %p)", (void*)lfs2, (void*)file); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawclose(lfs2, file); + err = lfs2_file_close_(lfs2, file); LFS2_TRACE("lfs2_file_close -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5981,7 +6199,7 @@ int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file) { LFS2_TRACE("lfs2_file_sync(%p, %p)", (void*)lfs2, (void*)file); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawsync(lfs2, file); + err = lfs2_file_sync_(lfs2, file); LFS2_TRACE("lfs2_file_sync -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5999,7 +6217,7 @@ lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, (void*)lfs2, (void*)file, buffer, size); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_ssize_t res = lfs2_file_rawread(lfs2, file, buffer, size); + lfs2_ssize_t res = lfs2_file_read_(lfs2, file, buffer, size); LFS2_TRACE("lfs2_file_read -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6017,7 +6235,7 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, (void*)lfs2, (void*)file, buffer, size); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_ssize_t res = lfs2_file_rawwrite(lfs2, file, buffer, size); + lfs2_ssize_t res = lfs2_file_write_(lfs2, file, buffer, size); LFS2_TRACE("lfs2_file_write -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6035,7 +6253,7 @@ lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, (void*)lfs2, (void*)file, off, whence); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, off, whence); + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, off, whence); LFS2_TRACE("lfs2_file_seek -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6052,7 +6270,7 @@ int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { (void*)lfs2, (void*)file, size); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawtruncate(lfs2, file, size); + err = lfs2_file_truncate_(lfs2, file, size); LFS2_TRACE("lfs2_file_truncate -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6068,7 +6286,7 @@ lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file) { LFS2_TRACE("lfs2_file_tell(%p, %p)", (void*)lfs2, (void*)file); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_soff_t res = lfs2_file_rawtell(lfs2, file); + lfs2_soff_t res = lfs2_file_tell_(lfs2, file); LFS2_TRACE("lfs2_file_tell -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6082,7 +6300,7 @@ int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file) { } LFS2_TRACE("lfs2_file_rewind(%p, %p)", (void*)lfs2, (void*)file); - err = lfs2_file_rawrewind(lfs2, file); + err = lfs2_file_rewind_(lfs2, file); LFS2_TRACE("lfs2_file_rewind -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6097,9 +6315,9 @@ lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file) { LFS2_TRACE("lfs2_file_size(%p, %p)", (void*)lfs2, (void*)file); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_soff_t res = lfs2_file_rawsize(lfs2, file); + lfs2_soff_t res = lfs2_file_size_(lfs2, file); - LFS2_TRACE("lfs2_file_size -> %"PRId32, res); + LFS2_TRACE("lfs2_file_size -> %"PRIu32, res); LFS2_UNLOCK(lfs2->cfg); return res; } @@ -6112,7 +6330,7 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) { } LFS2_TRACE("lfs2_mkdir(%p, \"%s\")", (void*)lfs2, path); - err = lfs2_rawmkdir(lfs2, path); + err = lfs2_mkdir_(lfs2, path); LFS2_TRACE("lfs2_mkdir -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6128,7 +6346,7 @@ int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { LFS2_TRACE("lfs2_dir_open(%p, %p, \"%s\")", (void*)lfs2, (void*)dir, path); LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)dir)); - err = lfs2_dir_rawopen(lfs2, dir, path); + err = lfs2_dir_open_(lfs2, dir, path); LFS2_TRACE("lfs2_dir_open -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6142,7 +6360,7 @@ int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir) { } LFS2_TRACE("lfs2_dir_close(%p, %p)", (void*)lfs2, (void*)dir); - err = lfs2_dir_rawclose(lfs2, dir); + err = lfs2_dir_close_(lfs2, dir); LFS2_TRACE("lfs2_dir_close -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6157,7 +6375,7 @@ int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { LFS2_TRACE("lfs2_dir_read(%p, %p, %p)", (void*)lfs2, (void*)dir, (void*)info); - err = lfs2_dir_rawread(lfs2, dir, info); + err = lfs2_dir_read_(lfs2, dir, info); LFS2_TRACE("lfs2_dir_read -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6172,7 +6390,7 @@ int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { LFS2_TRACE("lfs2_dir_seek(%p, %p, %"PRIu32")", (void*)lfs2, (void*)dir, off); - err = lfs2_dir_rawseek(lfs2, dir, off); + err = lfs2_dir_seek_(lfs2, dir, off); LFS2_TRACE("lfs2_dir_seek -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6186,7 +6404,7 @@ lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir) { } LFS2_TRACE("lfs2_dir_tell(%p, %p)", (void*)lfs2, (void*)dir); - lfs2_soff_t res = lfs2_dir_rawtell(lfs2, dir); + lfs2_soff_t res = lfs2_dir_tell_(lfs2, dir); LFS2_TRACE("lfs2_dir_tell -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6200,7 +6418,7 @@ int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir) { } LFS2_TRACE("lfs2_dir_rewind(%p, %p)", (void*)lfs2, (void*)dir); - err = lfs2_dir_rawrewind(lfs2, dir); + err = lfs2_dir_rewind_(lfs2, dir); LFS2_TRACE("lfs2_dir_rewind -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6214,7 +6432,7 @@ int lfs2_fs_stat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { } LFS2_TRACE("lfs2_fs_stat(%p, %p)", (void*)lfs2, (void*)fsinfo); - err = lfs2_fs_rawstat(lfs2, fsinfo); + err = lfs2_fs_stat_(lfs2, fsinfo); LFS2_TRACE("lfs2_fs_stat -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6228,7 +6446,7 @@ lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2) { } LFS2_TRACE("lfs2_fs_size(%p)", (void*)lfs2); - lfs2_ssize_t res = lfs2_fs_rawsize(lfs2); + lfs2_ssize_t res = lfs2_fs_size_(lfs2); LFS2_TRACE("lfs2_fs_size -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6243,7 +6461,7 @@ int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void *, lfs2_block_t), void *data) LFS2_TRACE("lfs2_fs_traverse(%p, %p, %p)", (void*)lfs2, (void*)(uintptr_t)cb, data); - err = lfs2_fs_rawtraverse(lfs2, cb, data, true); + err = lfs2_fs_traverse_(lfs2, cb, data, true); LFS2_TRACE("lfs2_fs_traverse -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6251,32 +6469,32 @@ int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void *, lfs2_block_t), void *data) } #ifndef LFS2_READONLY -int lfs2_fs_gc(lfs2_t *lfs2) { +int lfs2_fs_mkconsistent(lfs2_t *lfs2) { int err = LFS2_LOCK(lfs2->cfg); if (err) { return err; } - LFS2_TRACE("lfs2_fs_gc(%p)", (void*)lfs2); + LFS2_TRACE("lfs2_fs_mkconsistent(%p)", (void*)lfs2); - err = lfs2_fs_rawgc(lfs2); + err = lfs2_fs_mkconsistent_(lfs2); - LFS2_TRACE("lfs2_fs_gc -> %d", err); + LFS2_TRACE("lfs2_fs_mkconsistent -> %d", err); LFS2_UNLOCK(lfs2->cfg); return err; } #endif #ifndef LFS2_READONLY -int lfs2_fs_mkconsistent(lfs2_t *lfs2) { +int lfs2_fs_gc(lfs2_t *lfs2) { int err = LFS2_LOCK(lfs2->cfg); if (err) { return err; } - LFS2_TRACE("lfs2_fs_mkconsistent(%p)", (void*)lfs2); + LFS2_TRACE("lfs2_fs_gc(%p)", (void*)lfs2); - err = lfs2_fs_rawmkconsistent(lfs2); + err = lfs2_fs_gc_(lfs2); - LFS2_TRACE("lfs2_fs_mkconsistent -> %d", err); + LFS2_TRACE("lfs2_fs_gc -> %d", err); LFS2_UNLOCK(lfs2->cfg); return err; } @@ -6290,7 +6508,7 @@ int lfs2_fs_grow(lfs2_t *lfs2, lfs2_size_t block_count) { } LFS2_TRACE("lfs2_fs_grow(%p, %"PRIu32")", (void*)lfs2, block_count); - err = lfs2_fs_rawgrow(lfs2, block_count); + err = lfs2_fs_grow_(lfs2, block_count); LFS2_TRACE("lfs2_fs_grow -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6308,7 +6526,7 @@ int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { ".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".block_cycles=%"PRId32", .cache_size=%"PRIu32", " ".lookahead_size=%"PRIu32", .read_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, " ".name_max=%"PRIu32", .file_max=%"PRIu32", " @@ -6321,7 +6539,7 @@ int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs2_rawmigrate(lfs2, cfg); + err = lfs2_migrate_(lfs2, cfg); LFS2_TRACE("lfs2_migrate -> %d", err); LFS2_UNLOCK(cfg); diff --git a/lib/littlefs/lfs2.h b/lib/littlefs/lfs2.h index 559ccebac9269..aee0619e9326a 100644 --- a/lib/littlefs/lfs2.h +++ b/lib/littlefs/lfs2.h @@ -21,7 +21,7 @@ extern "C" // Software library version // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS2_VERSION 0x00020008 +#define LFS2_VERSION 0x0002000b #define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16)) #define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0)) @@ -52,16 +52,15 @@ typedef uint32_t lfs2_block_t; #endif // Maximum size of a file in bytes, may be redefined to limit to support other -// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the -// functions lfs2_file_seek, lfs2_file_size, and lfs2_file_tell will return -// incorrect values due to using signed integers. Stored in superblock and -// must be respected by other littlefs drivers. +// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be +// respected by other littlefs drivers. #ifndef LFS2_FILE_MAX #define LFS2_FILE_MAX 2147483647 #endif // Maximum size of custom attributes in bytes, may be redefined, but there is -// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022. +// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022. Stored +// in superblock and must be respected by other littlefs drivers. #ifndef LFS2_ATTR_MAX #define LFS2_ATTR_MAX 1022 #endif @@ -205,7 +204,8 @@ struct lfs2_config { // program sizes. lfs2_size_t block_size; - // Number of erasable blocks on the device. + // Number of erasable blocks on the device. Defaults to block_count stored + // on disk when zero. lfs2_size_t block_count; // Number of erase cycles before littlefs evicts metadata logs and moves @@ -226,9 +226,20 @@ struct lfs2_config { // Size of the lookahead buffer in bytes. A larger lookahead buffer // increases the number of blocks found during an allocation pass. The // lookahead buffer is stored as a compact bitmap, so each byte of RAM - // can track 8 blocks. Must be a multiple of 8. + // can track 8 blocks. lfs2_size_t lookahead_size; + // Threshold for metadata compaction during lfs2_fs_gc in bytes. Metadata + // pairs that exceed this threshold will be compacted during lfs2_fs_gc. + // Defaults to ~88% block_size when zero, though the default may change + // in the future. + // + // Note this only affects lfs2_fs_gc. Normal compactions still only occur + // when full. + // + // Set to -1 to disable metadata compaction during lfs2_fs_gc. + lfs2_size_t compact_thresh; + // Optional statically allocated read buffer. Must be cache_size. // By default lfs2_malloc is used to allocate this buffer. void *read_buffer; @@ -237,25 +248,24 @@ struct lfs2_config { // By default lfs2_malloc is used to allocate this buffer. void *prog_buffer; - // Optional statically allocated lookahead buffer. Must be lookahead_size - // and aligned to a 32-bit boundary. By default lfs2_malloc is used to - // allocate this buffer. + // Optional statically allocated lookahead buffer. Must be lookahead_size. + // By default lfs2_malloc is used to allocate this buffer. void *lookahead_buffer; // Optional upper limit on length of file names in bytes. No downside for // larger names except the size of the info struct which is controlled by - // the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX when zero. Stored in - // superblock and must be respected by other littlefs drivers. + // the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX or name_max stored on + // disk when zero. lfs2_size_t name_max; // Optional upper limit on files in bytes. No downside for larger files - // but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX when zero. Stored - // in superblock and must be respected by other littlefs drivers. + // but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX or file_max stored + // on disk when zero. lfs2_size_t file_max; // Optional upper limit on custom attributes in bytes. No downside for // larger attributes size but must be <= LFS2_ATTR_MAX. Defaults to - // LFS2_ATTR_MAX when zero. + // LFS2_ATTR_MAX or attr_max stored on disk when zero. lfs2_size_t attr_max; // Optional upper limit on total space given to metadata pairs in bytes. On @@ -264,6 +274,15 @@ struct lfs2_config { // Defaults to block_size when zero. lfs2_size_t metadata_max; + // Optional upper limit on inlined files in bytes. Inlined files live in + // metadata and decrease storage requirements, but may be limited to + // improve metadata-related performance. Must be <= cache_size, <= + // attr_max, and <= block_size/8. Defaults to the largest possible + // inline_max when zero. + // + // Set to -1 to disable inlined files. + lfs2_size_t inline_max; + #ifdef LFS2_MULTIVERSION // On-disk version to use when writing in the form of 16-bit major version // + 16-bit minor version. This limiting metadata to what is supported by @@ -430,19 +449,20 @@ typedef struct lfs2 { lfs2_gstate_t gdisk; lfs2_gstate_t gdelta; - struct lfs2_free { - lfs2_block_t off; + struct lfs2_lookahead { + lfs2_block_t start; lfs2_block_t size; - lfs2_block_t i; - lfs2_block_t ack; - uint32_t *buffer; - } free; + lfs2_block_t next; + lfs2_block_t ckpoint; + uint8_t *buffer; + } lookahead; const struct lfs2_config *cfg; lfs2_size_t block_count; lfs2_size_t name_max; lfs2_size_t file_max; lfs2_size_t attr_max; + lfs2_size_t inline_max; #ifdef LFS2_MIGRATE struct lfs21 *lfs21; @@ -712,18 +732,6 @@ lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2); // Returns a negative error code on failure. int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); -// Attempt to proactively find free blocks -// -// Calling this function is not required, but may allowing the offloading of -// the expensive block allocation scan to a less time-critical code path. -// -// Note: littlefs currently does not persist any found free blocks to disk. -// This may change in the future. -// -// Returns a negative error code on failure. Finding no free blocks is -// not an error. -int lfs2_fs_gc(lfs2_t *lfs2); - #ifndef LFS2_READONLY // Attempt to make the filesystem consistent and ready for writing // @@ -736,11 +744,33 @@ int lfs2_fs_gc(lfs2_t *lfs2); int lfs2_fs_mkconsistent(lfs2_t *lfs2); #endif +#ifndef LFS2_READONLY +// Attempt any janitorial work +// +// This currently: +// 1. Calls mkconsistent if not already consistent +// 2. Compacts metadata > compact_thresh +// 3. Populates the block allocator +// +// Though additional janitorial work may be added in the future. +// +// Calling this function is not required, but may allow the offloading of +// expensive janitorial work to a less time-critical code path. +// +// Returns a negative error code on failure. Accomplishing nothing is not +// an error. +int lfs2_fs_gc(lfs2_t *lfs2); +#endif + #ifndef LFS2_READONLY // Grows the filesystem to a new size, updating the superblock with the new // block count. // -// Note: This is irreversible. +// If LFS2_SHRINKNONRELOCATING is defined, this function will also accept +// block_counts smaller than the current configuration, after checking +// that none of the blocks that are being removed are in use. +// Note that littlefs's pseudorandom block allocation means that +// this is very unlikely to work in the general case. // // Returns a negative error code on failure. int lfs2_fs_grow(lfs2_t *lfs2, lfs2_size_t block_count); diff --git a/lib/littlefs/lfs2_util.c b/lib/littlefs/lfs2_util.c index c9850e78869c3..4fe7e5340ce77 100644 --- a/lib/littlefs/lfs2_util.c +++ b/lib/littlefs/lfs2_util.c @@ -11,6 +11,8 @@ #ifndef LFS2_CONFIG +// If user provides their own CRC impl we don't need this +#ifndef LFS2_CRC // Software CRC implementation with small lookup table uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) { static const uint32_t rtable[16] = { @@ -29,6 +31,7 @@ uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) { return crc; } +#endif #endif diff --git a/lib/littlefs/lfs2_util.h b/lib/littlefs/lfs2_util.h index dd2cbcc106d84..12c82a630b079 100644 --- a/lib/littlefs/lfs2_util.h +++ b/lib/littlefs/lfs2_util.h @@ -8,6 +8,9 @@ #ifndef LFS2_UTIL_H #define LFS2_UTIL_H +#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x) +#define LFS2_STRINGIZE2(x) #x + // Users can override lfs2_util.h with their own configuration by defining // LFS2_CONFIG as a header file to include (-DLFS2_CONFIG=lfs2_config.h). // @@ -15,11 +18,26 @@ // provided by the config file. To start, I would suggest copying lfs2_util.h // and modifying as needed. #ifdef LFS2_CONFIG -#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x) -#define LFS2_STRINGIZE2(x) #x #include LFS2_STRINGIZE(LFS2_CONFIG) #else +// Alternatively, users can provide a header file which defines +// macros and other things consumed by littlefs. +// +// For example, provide my_defines.h, which contains +// something like: +// +// #include +// extern void *my_malloc(size_t sz); +// #define LFS2_MALLOC(sz) my_malloc(sz) +// +// And build littlefs with the header by defining LFS2_DEFINES. +// (-DLFS2_DEFINES=my_defines.h) + +#ifdef LFS2_DEFINES +#include LFS2_STRINGIZE(LFS2_DEFINES) +#endif + // System includes #include #include @@ -177,10 +195,10 @@ static inline uint32_t lfs2_fromle32(uint32_t a) { (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) return __builtin_bswap32(a); #else - return (((uint8_t*)&a)[0] << 0) | - (((uint8_t*)&a)[1] << 8) | - (((uint8_t*)&a)[2] << 16) | - (((uint8_t*)&a)[3] << 24); + return ((uint32_t)((uint8_t*)&a)[0] << 0) | + ((uint32_t)((uint8_t*)&a)[1] << 8) | + ((uint32_t)((uint8_t*)&a)[2] << 16) | + ((uint32_t)((uint8_t*)&a)[3] << 24); #endif } @@ -200,10 +218,10 @@ static inline uint32_t lfs2_frombe32(uint32_t a) { (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) return a; #else - return (((uint8_t*)&a)[0] << 24) | - (((uint8_t*)&a)[1] << 16) | - (((uint8_t*)&a)[2] << 8) | - (((uint8_t*)&a)[3] << 0); + return ((uint32_t)((uint8_t*)&a)[0] << 24) | + ((uint32_t)((uint8_t*)&a)[1] << 16) | + ((uint32_t)((uint8_t*)&a)[2] << 8) | + ((uint32_t)((uint8_t*)&a)[3] << 0); #endif } @@ -212,12 +230,22 @@ static inline uint32_t lfs2_tobe32(uint32_t a) { } // Calculate CRC-32 with polynomial = 0x04c11db7 +#ifdef LFS2_CRC +static inline uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) { + return LFS2_CRC(crc, buffer, size); +} +#else uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size); +#endif // Allocate memory, only used if buffers are not provided to littlefs -// Note, memory must be 64-bit aligned +// +// littlefs current has no alignment requirements, as it only allocates +// byte-level buffers. static inline void *lfs2_malloc(size_t size) { -#ifndef LFS2_NO_MALLOC +#if defined(LFS2_MALLOC) + return LFS2_MALLOC(size); +#elif !defined(LFS2_NO_MALLOC) return malloc(size); #else (void)size; @@ -227,7 +255,9 @@ static inline void *lfs2_malloc(size_t size) { // Deallocate memory, only used if buffers are not provided to littlefs static inline void lfs2_free(void *p) { -#ifndef LFS2_NO_MALLOC +#if defined(LFS2_FREE) + LFS2_FREE(p); +#elif !defined(LFS2_NO_MALLOC) free(p); #else (void)p; diff --git a/lib/micropython-lib b/lib/micropython-lib index 5b496e944ec04..6ae440a8a1442 160000 --- a/lib/micropython-lib +++ b/lib/micropython-lib @@ -1 +1 @@ -Subproject commit 5b496e944ec045177afa1620920a168410b7f60b +Subproject commit 6ae440a8a144233e6e703f6759b7e7a0afaa37a4 diff --git a/main.c b/main.c index 46b54f522b503..e1cc7e391cc5d 100644 --- a/main.c +++ b/main.c @@ -1202,7 +1202,7 @@ size_t gc_get_max_new_split(void) { return port_heap_get_largest_free_size(); } -void NORETURN nlr_jump_fail(void *val) { +void MP_NORETURN nlr_jump_fail(void *val) { reset_into_safe_mode(SAFE_MODE_NLR_JUMP_FAIL); while (true) { } @@ -1213,7 +1213,7 @@ bool vm_is_running(void) { } #ifndef NDEBUG -static void NORETURN __fatal_error(const char *msg) { +static void MP_NORETURN __fatal_error(const char *msg) { #if CIRCUITPY_DEBUG == 0 reset_into_safe_mode(SAFE_MODE_HARD_FAULT); #endif diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 989aec68bb519..add07c3d49b3e 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -34,13 +34,19 @@ #include "py/persistentcode.h" #include "py/runtime.h" #include "py/gc.h" -#include "py/stackctrl.h" +#include "py/parsenum.h" #include "genhdr/mpversion.h" #ifdef _WIN32 // CIRCUITPY-CHANGE #include "fmode.h" #endif +#if MICROPY_EMIT_NATIVE && MICROPY_EMIT_RV32 +#include "py/asmrv32.h" + +static asm_rv32_backend_options_t rv32_options = { 0 }; +#endif + // Command line options, with their defaults static uint emit_opt = MP_EMIT_OPT_NONE; mp_uint_t mp_verbose_flag = 0; @@ -82,13 +88,20 @@ static int compile_and_save(const char *file, const char *output_file, const cha source_name = qstr_from_str(source_file); } - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #endif mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); mp_compiled_module_t cm; cm.context = m_new_obj(mp_module_context_t); + cm.arch_flags = 0; + #if MICROPY_EMIT_NATIVE && MICROPY_EMIT_RV32 + if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_RV32IMC && mp_dynamic_compiler.backend_options != NULL) { + cm.arch_flags = ((asm_rv32_backend_options_t *)mp_dynamic_compiler.backend_options)->allowed_extensions; + } + #endif + mp_compile_to_raw_code(&parse_tree, source_name, false, &cm); if ((output_file != NULL && strcmp(output_file, "-") == 0) || @@ -131,7 +144,10 @@ static int usage(char **argv) { "Target specific options:\n" "-msmall-int-bits=number : set the maximum bits used to encode a small-int\n" "-march= : set architecture for native emitter;\n" - " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp, armv7emdp, xtensa, xtensawin, rv32imc, debug\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" "\n" "Implementation specific options:\n", argv[0] ); @@ -212,9 +228,39 @@ static char *backslash_to_forwardslash(char *path) { return path; } -MP_NOINLINE int main_(int argc, char **argv) { - mp_stack_set_limit(40000 * (sizeof(void *) / 4)); +// This will need to be reworked in case mpy-cross needs to set more bits than +// what its small int representation allows to fit in there. +static bool parse_integer(const char *value, mp_uint_t *integer) { + assert(value && "Attempting to parse a NULL string"); + assert(integer && "Attempting to store into a NULL integer buffer"); + + size_t value_length = strlen(value); + int base = 10; + if (value_length > 2 && value[0] == '0') { + if ((value[1] | 0x20) == 'b') { + base = 2; + } else if ((value[1] | 0x20) == 'x') { + base = 16; + } else { + return false; + } + } + + bool valid = false; + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t parsed = mp_parse_num_integer(value, value_length, base, NULL); + if (mp_obj_is_small_int(parsed)) { + *integer = MP_OBJ_SMALL_INT_VALUE(parsed); + valid = true; + } + nlr_pop(); + } + return valid; +} + +MP_NOINLINE int main_(int argc, char **argv) { pre_process_options(argc, argv); char *heap = malloc(heap_size); @@ -237,11 +283,13 @@ MP_NOINLINE int main_(int argc, char **argv) { // don't support native emitter unless -march is specified mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_NONE; mp_dynamic_compiler.nlr_buf_num_regs = 0; + mp_dynamic_compiler.backend_options = NULL; const char *input_file = NULL; const char *output_file = NULL; const char *source_file = NULL; bool option_parsing_active = true; + const char *arch_flags = NULL; // parse main options for (int a = 1; a < argc; a++) { @@ -318,6 +366,9 @@ MP_NOINLINE int main_(int argc, char **argv) { } else if (strcmp(arch, "rv32imc") == 0) { mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV32IMC; mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV32I; + } else if (strcmp(arch, "rv64imc") == 0) { + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV64IMC; + mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV64I; } else if (strcmp(arch, "debug") == 0) { mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_DEBUG; mp_dynamic_compiler.nlr_buf_num_regs = 0; @@ -331,6 +382,9 @@ MP_NOINLINE int main_(int argc, char **argv) { #elif defined(__arm__) && !defined(__thumb2__) mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV6; mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP; + #elif defined(__riscv) && (__riscv_xlen == 64) + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV64IMC; + mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV64I; #else mp_printf(&mp_stderr_print, "unable to determine host architecture for -march=host\n"); exit(1); @@ -338,6 +392,8 @@ MP_NOINLINE int main_(int argc, char **argv) { } else { return usage(argv); } + } else if (strncmp(argv[a], "-march-flags=", sizeof("-march-flags=") - 1) == 0) { + arch_flags = argv[a] + sizeof("-march-flags=") - 1; } else if (strcmp(argv[a], "--") == 0) { option_parsing_active = false; } else { @@ -352,6 +408,38 @@ MP_NOINLINE int main_(int argc, char **argv) { } } + if (arch_flags && mp_dynamic_compiler.native_arch != MP_NATIVE_ARCH_NONE) { + bool processed = false; + #if MICROPY_EMIT_NATIVE && MICROPY_EMIT_RV32 + 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 ((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 + if (!processed) { + mp_printf(&mp_stderr_print, "unrecognised arch flags\n"); + exit(1); + } + } + + #if MICROPY_EMIT_NATIVE + if ((MP_STATE_VM(default_emit_opt) == MP_EMIT_OPT_NATIVE_PYTHON + || MP_STATE_VM(default_emit_opt) == MP_EMIT_OPT_VIPER) + && mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_NONE) { + mp_printf(&mp_stderr_print, "arch not specified\n"); + exit(1); + } + #endif + if (input_file == NULL) { mp_printf(&mp_stderr_print, "no input file\n"); exit(1); @@ -371,7 +459,7 @@ MP_NOINLINE int main_(int argc, char **argv) { } int main(int argc, char **argv) { - mp_stack_ctrl_init(); + mp_cstack_init_with_sp_here(40000 * (sizeof(void *) / 4)); return main_(argc, argv); } diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index edbd9f87c0558..36bcbc113941c 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -59,6 +59,7 @@ #define MICROPY_COMP_CONST_FOLDING (1) #define MICROPY_COMP_MODULE_CONST (1) #define MICROPY_COMP_CONST (1) +#define MICROPY_COMP_CONST_FLOAT (1) #define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) #define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) #define MICROPY_COMP_RETURN_IF_EXPR (1) @@ -90,11 +91,12 @@ #define MICROPY_GCREGS_SETJMP (1) #endif -#define MICROPY_PY___FILE__ (0) +#define MICROPY_MODULE___FILE__ (0) #define MICROPY_PY_ARRAY (0) #define MICROPY_PY_ATTRTUPLE (0) #define MICROPY_PY_COLLECTIONS (0) -#define MICROPY_PY_MATH (0) +#define MICROPY_PY_MATH (MICROPY_COMP_CONST_FLOAT) +#define MICROPY_PY_MATH_CONSTANTS (MICROPY_COMP_CONST_FLOAT) #define MICROPY_PY_CMATH (0) #define MICROPY_PY_GC (0) #define MICROPY_PY_IO (0) @@ -102,23 +104,6 @@ // type definitions for the specific machine -#ifdef __LP64__ -typedef long mp_int_t; // must be pointer size -typedef unsigned long mp_uint_t; // must be pointer size -#elif defined(__MINGW32__) && defined(_WIN64) -#include -typedef __int64 mp_int_t; -typedef unsigned __int64 mp_uint_t; -#elif defined(_MSC_VER) && defined(_WIN64) -typedef __int64 mp_int_t; -typedef unsigned __int64 mp_uint_t; -#else -// These are definitions for machines where sizeof(int) == sizeof(void*), -// regardless for actual size. -typedef int mp_int_t; // must be pointer size -typedef unsigned int mp_uint_t; // must be pointer size -#endif - // Cannot include , as it may lead to symbol name clashes #if _FILE_OFFSET_BITS == 64 && !defined(__LP64__) typedef long long mp_off_t; @@ -143,7 +128,7 @@ typedef long mp_off_t; #ifdef _MSC_VER #define MP_ENDIANNESS_LITTLE (1) -#define NORETURN __declspec(noreturn) +#define MP_NORETURN __declspec(noreturn) #define MP_NOINLINE __declspec(noinline) #define MP_ALWAYSINLINE __forceinline #define MP_LIKELY(x) (x) diff --git a/mpy-cross/mpy_cross/__init__.py b/mpy-cross/mpy_cross/__init__.py index 91cd6f99335b3..6d7002a3b89d2 100644 --- a/mpy-cross/mpy_cross/__init__.py +++ b/mpy-cross/mpy_cross/__init__.py @@ -25,7 +25,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import print_function import os import re import stat @@ -44,6 +43,7 @@ "NATIVE_ARCH_XTENSA": "xtensa", "NATIVE_ARCH_XTENSAWIN": "xtensawin", "NATIVE_ARCH_RV32IMC": "rv32imc", + "NATIVE_ARCH_RV64IMC": "rv64imc", } globals().update(NATIVE_ARCHS) diff --git a/mpy-cross/mpy_cross/__main__.py b/mpy-cross/mpy_cross/__main__.py index 2b6b81c333362..fe78a9e077e77 100644 --- a/mpy-cross/mpy_cross/__main__.py +++ b/mpy-cross/mpy_cross/__main__.py @@ -25,7 +25,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import print_function import argparse import sys diff --git a/ports/analog/supervisor/port.c b/ports/analog/supervisor/port.c index 86d96bc251ba2..ee410a38148e9 100644 --- a/ports/analog/supervisor/port.c +++ b/ports/analog/supervisor/port.c @@ -67,7 +67,7 @@ static uint32_t subsec, sec = 0; static uint32_t tick_flag = 0; // defined by cmsis core files -extern void NVIC_SystemReset(void) NORETURN; +extern void NVIC_SystemReset(void) MP_NORETURN; volatile uint32_t system_ticks = 0; diff --git a/ports/atmel-samd/common-hal/alarm/__init__.c b/ports/atmel-samd/common-hal/alarm/__init__.c index 483aa1a9679ad..38ef58bea7659 100644 --- a/ports/atmel-samd/common-hal/alarm/__init__.c +++ b/ports/atmel-samd/common-hal/alarm/__init__.c @@ -133,7 +133,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala _setup_sleep_alarms(true, n_alarms, alarms); } -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_time_timealarm_prepare_for_deep_sleep(); // port_disable_tick(); // TODO: Required for SAMD? diff --git a/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c b/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c index 3c1fbb5ba7b32..de98b7ec85ae6 100644 --- a/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c +++ b/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c @@ -7,6 +7,6 @@ #include "shared-bindings/alarm/touch/TouchAlarm.h" #include "shared-bindings/microcontroller/__init__.h" -NORETURN void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { +MP_NORETURN void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_TouchAlarm); } diff --git a/ports/atmel-samd/reset.c b/ports/atmel-samd/reset.c index 9cacd4ab951ae..816e23e3251db 100644 --- a/ports/atmel-samd/reset.c +++ b/ports/atmel-samd/reset.c @@ -9,7 +9,7 @@ #include "reset.h" #include "supervisor/filesystem.h" -void NVIC_SystemReset(void) NORETURN; +void NVIC_SystemReset(void) MP_NORETURN; void reset(void) { filesystem_flush(); diff --git a/ports/atmel-samd/reset.h b/ports/atmel-samd/reset.h index c74d25fa01ea5..248d43779fdc9 100644 --- a/ports/atmel-samd/reset.h +++ b/ports/atmel-samd/reset.h @@ -16,6 +16,6 @@ extern uint32_t _bootloader_dbl_tap; -void reset_to_bootloader(void) NORETURN; -void reset(void) NORETURN; +void reset_to_bootloader(void) MP_NORETURN; +void reset(void) MP_NORETURN; bool bootloader_available(void); diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 31bc5faf82703..83c8e295d26bc 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -687,7 +687,7 @@ void port_idle_until_interrupt(void) { /** * \brief Default interrupt handler for unused IRQs. */ -__attribute__((used)) NORETURN void HardFault_Handler(void) { +__attribute__((used)) MP_NORETURN void HardFault_Handler(void) { #ifdef ENABLE_MICRO_TRACE_BUFFER // Turn off the micro trace buffer so we don't fill it up in the infinite // loop below. diff --git a/ports/espressif/bindings/espidf/__init__.c b/ports/espressif/bindings/espidf/__init__.c index 012ed7d9f9adf..897da0eb5717b 100644 --- a/ports/espressif/bindings/espidf/__init__.c +++ b/ports/espressif/bindings/espidf/__init__.c @@ -106,7 +106,7 @@ MP_DEFINE_CONST_OBJ_TYPE( //| ... //| //| -NORETURN void mp_raise_espidf_MemoryError(void) { +MP_NORETURN void mp_raise_espidf_MemoryError(void) { nlr_raise(mp_obj_new_exception(&mp_type_espidf_MemoryError)); } diff --git a/ports/espressif/bindings/espidf/__init__.h b/ports/espressif/bindings/espidf/__init__.h index 7c112abf7368c..92d2e0c276931 100644 --- a/ports/espressif/bindings/espidf/__init__.h +++ b/ports/espressif/bindings/espidf/__init__.h @@ -15,9 +15,9 @@ extern const mp_obj_type_t mp_type_espidf_IDFError; extern const mp_obj_type_t mp_type_espidf_MemoryError; -NORETURN void mp_raise_espidf_MemoryError(void); +MP_NORETURN void mp_raise_espidf_MemoryError(void); -void raise_esp_error(esp_err_t err) NORETURN; +void raise_esp_error(esp_err_t err) MP_NORETURN; #define CHECK_ESP_RESULT(x) do { int res = (x); if (res != ESP_OK) raise_esp_error(res); } while (0) size_t common_hal_espidf_get_total_psram(void); diff --git a/ports/espressif/common-hal/alarm/__init__.c b/ports/espressif/common-hal/alarm/__init__.c index 629f976039fd1..4f8e7e530425a 100644 --- a/ports/espressif/common-hal/alarm/__init__.c +++ b/ports/espressif/common-hal/alarm/__init__.c @@ -189,7 +189,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala _setup_sleep_alarms(true, n_alarms, alarms); } -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_pin_pinalarm_prepare_for_deep_sleep(); #if CIRCUITPY_ALARM_TOUCH alarm_touch_touchalarm_prepare_for_deep_sleep(); diff --git a/ports/espressif/common-hal/microcontroller/__init__.c b/ports/espressif/common-hal/microcontroller/__init__.c index d23afce4d999e..9b50cd7b5d286 100644 --- a/ports/espressif/common-hal/microcontroller/__init__.c +++ b/ports/espressif/common-hal/microcontroller/__init__.c @@ -142,7 +142,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { } } -void NORETURN common_hal_mcu_reset(void) { +void MP_NORETURN common_hal_mcu_reset(void) { filesystem_flush(); // TODO: implement as part of flash improvements esp_restart(); } diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 192a91717169a..53564c05bf608 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -103,7 +103,7 @@ static esp_timer_handle_t _sleep_timer; TaskHandle_t circuitpython_task = NULL; -extern void esp_restart(void) NORETURN; +extern void esp_restart(void) MP_NORETURN; static void tick_on_cp_core(void *arg) { supervisor_tick(); diff --git a/ports/mimxrt10xx/reset.h b/ports/mimxrt10xx/reset.h index 04c951221e9e5..cae838556bce3 100644 --- a/ports/mimxrt10xx/reset.h +++ b/ports/mimxrt10xx/reset.h @@ -16,6 +16,6 @@ #define DBL_TAP_MAGIC 0xf01669ef // Randomly selected, adjusted to have first and last bit set #define DBL_TAP_MAGIC_QUICK_BOOT 0xf02669ef -void reset_to_bootloader(void) NORETURN; -void reset(void) NORETURN; +void reset_to_bootloader(void) MP_NORETURN; +void reset(void) MP_NORETURN; bool bootloader_available(void); diff --git a/ports/nordic/common-hal/alarm/__init__.c b/ports/nordic/common-hal/alarm/__init__.c index 9753b0321fd06..026e117b9a67a 100644 --- a/ports/nordic/common-hal/alarm/__init__.c +++ b/ports/nordic/common-hal/alarm/__init__.c @@ -240,7 +240,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala #define PRESCALER_VALUE_IN_DEEP_SLEEP (1024) -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_time_timealarm_prepare_for_deep_sleep(); diff --git a/ports/raspberrypi/common-hal/alarm/__init__.c b/ports/raspberrypi/common-hal/alarm/__init__.c index c47ddc3150515..a72b3a368d407 100644 --- a/ports/raspberrypi/common-hal/alarm/__init__.c +++ b/ports/raspberrypi/common-hal/alarm/__init__.c @@ -197,7 +197,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala _setup_sleep_alarms(true, n_alarms, alarms); } -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { bool timealarm_set = alarm_time_timealarm_is_set(); #if CIRCUITPY_CYW43 diff --git a/ports/raspberrypi/common-hal/wifi/Radio.c b/ports/raspberrypi/common-hal/wifi/Radio.c index 3c22c3548d191..73e7794ecc2dc 100644 --- a/ports/raspberrypi/common-hal/wifi/Radio.c +++ b/ports/raspberrypi/common-hal/wifi/Radio.c @@ -48,7 +48,7 @@ static inline void nw_put_le32(uint8_t *buf, uint32_t x) { buf[3] = x >> 24; } -NORETURN static void ro_attribute(qstr attr) { +MP_NORETURN static void ro_attribute(qstr attr) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q is read-only for this board"), attr); } diff --git a/ports/raspberrypi/common-hal/wifi/__init__.h b/ports/raspberrypi/common-hal/wifi/__init__.h index 73988e8437987..4005c48a3cde2 100644 --- a/ports/raspberrypi/common-hal/wifi/__init__.h +++ b/ports/raspberrypi/common-hal/wifi/__init__.h @@ -11,7 +11,7 @@ #include "lwip/ip_addr.h" void wifi_reset(void); -NORETURN void raise_cyw_error(int err); +MP_NORETURN void raise_cyw_error(int err); #define CHECK_CYW_RESULT(x) do { int res = (x); if (res != 0) raise_cyw_error(res); } while (0) void ipaddress_ipaddress_to_lwip(mp_obj_t ip_address, ip_addr_t *lwip_ip_address); diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 5cfbdfa66a32b..aadcd56dde15a 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -588,7 +588,7 @@ void port_idle_until_interrupt(void) { /** * \brief Default interrupt handler for unused IRQs. */ -extern NORETURN void isr_hardfault(void); // provide a prototype to avoid a missing-prototypes diagnostic +extern MP_NORETURN void isr_hardfault(void); // provide a prototype to avoid a missing-prototypes diagnostic __attribute__((used)) void __not_in_flash_func(isr_hardfault)(void) { // Only safe mode from core 0 which is running CircuitPython. Core 1 faulting // should not be fatal to CP. (Fingers crossed.) diff --git a/ports/stm/common-hal/alarm/__init__.c b/ports/stm/common-hal/alarm/__init__.c index 3d6c4466882b4..1be8f8dc10d09 100644 --- a/ports/stm/common-hal/alarm/__init__.c +++ b/ports/stm/common-hal/alarm/__init__.c @@ -135,7 +135,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala _setup_sleep_alarms(true, n_alarms, alarms); } -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_set_wakeup_reason(STM_WAKEUP_UNDEF); alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_time_timealarm_prepare_for_deep_sleep(); diff --git a/ports/stm/supervisor/port.c b/ports/stm/supervisor/port.c index 3820a046fc4af..1ffc8656a888e 100644 --- a/ports/stm/supervisor/port.c +++ b/ports/stm/supervisor/port.c @@ -39,7 +39,7 @@ #include STM32_HAL_H -void NVIC_SystemReset(void) NORETURN; +void NVIC_SystemReset(void) MP_NORETURN; #if (CPY_STM32H7) || (CPY_STM32F7) #if defined(CIRCUITPY_HW_SDRAM_SIZE) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 6461c310eeb68..8be34feed3c49 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -27,6 +27,9 @@ FROZEN_MANIFEST ?= variants/manifest.py # This should be configured by the mpconfigvariant.mk PROG ?= micropython +# For use in test rules below +ABS_PROG = $(abspath $(BUILD)/$(PROG)) + # qstr definitions (must come before including py.mk) QSTR_DEFS += qstrdefsport.h QSTR_GLOBAL_DEPENDENCIES += $(VARIANT_DIR)/mpconfigvariant.h @@ -46,13 +49,18 @@ INC += -I$(BUILD) # compiler settings CWARN = -Wall -Werror -CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Wno-missing-field-initializers +// CIRCUITPY-CHANGE: add -Wno-missing-field-initializers +CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) # Force the use of 64-bits for file sizes in C library functions on 32-bit platforms. # This option has no effect on 64-bit builds. CFLAGS += -D_FILE_OFFSET_BITS=64 +# Force the use of 64-bits for time_t in C library functions on 32-bit platforms. +# This option has no effect on 64-bit builds. +CFLAGS += -D_TIME_BITS=64 + # Debugging/Optimization ifdef DEBUG COPT ?= -Og @@ -139,7 +147,11 @@ ifeq ($(MICROPY_PY_SOCKET),1) CFLAGS += -DMICROPY_PY_SOCKET=1 endif ifeq ($(MICROPY_PY_THREAD),1) +ifeq ($(MICROPY_PY_THREAD_GIL),1) +CFLAGS += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=1 +else CFLAGS += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0 +endif LDFLAGS += $(LIBPTHREAD) endif @@ -198,7 +210,6 @@ ifeq ($(MICROPY_PY_JNI),1) CFLAGS += -I/usr/lib/jvm/java-7-openjdk-amd64/include -DMICROPY_PY_JNI=1 endif -# CIRCUITPY-CHANGE: CircuitPython-specific files. # source files SRC_C += \ main.c \ @@ -208,13 +219,15 @@ SRC_C += \ input.c \ alloc.c \ fatfs_port.c \ - shared-module/os/__init__.c \ - supervisor/shared/settings.c \ - supervisor/stub/filesystem.c \ - supervisor/stub/safe_mode.c \ - supervisor/stub/stack.c \ - supervisor/shared/translate/translate.c \ - $(SRC_MOD) \ + mpbthciport.c \ + mpbtstackport_common.c \ + mpbtstackport_h4.c \ + mpbtstackport_usb.c \ + mpnimbleport.c \ + modtermios.c \ + modsocket.c \ + modffi.c \ + modjni.c \ $(wildcard $(VARIANT_DIR)/*.c) # CIRCUITPY-CHANGE @@ -225,6 +238,7 @@ $(BUILD)/supervisor/shared/translate/translate.o: $(HEADER_BUILD)/qstrdefs.gener SHARED_SRC_C += $(addprefix shared/,\ runtime/gchelper_generic.c \ + runtime/pyexec.c \ timeutils/timeutils.c \ $(SHARED_SRC_C_EXTRA) \ ) @@ -280,7 +294,7 @@ print-failures clean-failures: TEST_ARGS ?= test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py $(TEST_ARGS) + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) diff --git a/ports/unix/README.md b/ports/unix/README.md index b7aa6e3fef76d..ee983a882cc83 100644 --- a/ports/unix/README.md +++ b/ports/unix/README.md @@ -72,6 +72,14 @@ To run the complete testsuite, use: $ make test +There are other make targets to interact with the testsuite: + + $ make test//int # Run all tests matching the pattern "int" + $ make test/ports/unix # Run all tests in ports/unix + $ make test-failures # Re-run only the failed tests + $ make print-failures # print the differences for failed tests + $ make clean-failures # delete the .exp and .out files from failed tests + The Unix port comes with a built-in package manager called `mip`, e.g.: $ ./build-standard/micropython -m mip install hmac @@ -155,3 +163,21 @@ The default compiler optimisation level is -Os, or -Og if `DEBUG=1` is set. Setting the variable `COPT` will explicitly set the optimisation level. For example `make [other arguments] COPT=-O0 DEBUG=1` will build a binary with no optimisations, assertions enabled, and debug symbols. + +### Sanitizers + +Sanitizers are extra runtime checks supported by gcc and clang. The CI process +supports building with the "undefined behavior" (UBSan) or "address" (ASan) +sanitizers. The script `tools/ci.sh` is the source of truth about how to build +and run in these modes. + +Several classes of checks are disabled via compiler flags: + +* In the undefined behavior sanitizer, checks based on the presence of the + `non_null` attribute are disabled because the code makes technically incorrect + calls like `memset(NULL, 0, 0)`. A future C standard is likely to permit such + calls. +* In the address sanitizer, `detect_stack_use_after_return` is disabled. This + check is intended to make sure locals in a "returned from" stack frame are not + used. However, this mode interferes with various assumptions that + MicroPython's stack checking, NLR, and GC rely on. diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 7f13f9756f177..ba20dba3595cd 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -97,6 +97,7 @@ static const mp_rom_map_elem_t rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_write1), MP_ROM_PTR(&mp_stream_write1_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto1), MP_ROM_PTR(&mp_stream_readinto1_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) }, }; @@ -171,19 +172,31 @@ static void pairheap_test(size_t nops, int *ops) { if (mp_pairheap_is_empty(pairheap_lt, heap)) { mp_printf(&mp_plat_print, " -"); } else { - mp_printf(&mp_plat_print, " %d", mp_pairheap_peek(pairheap_lt, heap) - &node[0]); + mp_printf(&mp_plat_print, " %d", (int)(mp_pairheap_peek(pairheap_lt, heap) - &node[0])); ; } } mp_printf(&mp_plat_print, "\npop all:"); while (!mp_pairheap_is_empty(pairheap_lt, heap)) { - mp_printf(&mp_plat_print, " %d", mp_pairheap_peek(pairheap_lt, heap) - &node[0]); + mp_printf(&mp_plat_print, " %d", (int)(mp_pairheap_peek(pairheap_lt, heap) - &node[0])); ; heap = mp_pairheap_pop(pairheap_lt, heap); } mp_printf(&mp_plat_print, "\n"); } +static mp_sched_node_t mp_coverage_sched_node; +static bool coverage_sched_function_continue; + +static void coverage_sched_function(mp_sched_node_t *node) { + (void)node; + mp_printf(&mp_plat_print, "scheduled function\n"); + if (coverage_sched_function_continue) { + // Re-scheduling node will cause it to run again next time scheduled functions are run + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + } +} + // function to run extra tests for things that can't be checked by scripts static mp_obj_t extra_coverage(void) { // mp_printf (used by ports that don't have a native printf) @@ -191,10 +204,22 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# mp_printf\n"); mp_printf(&mp_plat_print, "%d %+d % d\n", -123, 123, 123); // sign mp_printf(&mp_plat_print, "%05d\n", -123); // negative number with zero padding - mp_printf(&mp_plat_print, "%ld\n", 123); // long - mp_printf(&mp_plat_print, "%lx\n", 0x123); // long hex - mp_printf(&mp_plat_print, "%X\n", 0x1abcdef); // capital hex - mp_printf(&mp_plat_print, "%.2s %.3s '%4.4s' '%5.5q' '%.3q'\n", "abc", "abc", "abc", MP_QSTR_True, MP_QSTR_True); // fixed string precision + mp_printf(&mp_plat_print, "%ld\n", 123l); // long + mp_printf(&mp_plat_print, "%lx\n", 0x123fl); // long hex + mp_printf(&mp_plat_print, "%lX\n", 0x123fl); // capital long hex + if (sizeof(mp_int_t) == 8) { + mp_printf(&mp_plat_print, "%llx\n", LLONG_MAX); // long long hex + mp_printf(&mp_plat_print, "%llX\n", LLONG_MAX); // capital long long hex + mp_printf(&mp_plat_print, "%llu\n", ULLONG_MAX); // unsigned long long + } else { + // fake for platforms without narrower mp_int_t + mp_printf(&mp_plat_print, "7fffffffffffffff\n"); + mp_printf(&mp_plat_print, "7FFFFFFFFFFFFFFF\n"); + mp_printf(&mp_plat_print, "18446744073709551615\n"); + } + mp_printf(&mp_plat_print, "%p\n", (void *)0x789f); // pointer + mp_printf(&mp_plat_print, "%P\n", (void *)0x789f); // pointer uppercase + mp_printf(&mp_plat_print, "%.2s %.3s '%4.4s' '%5.5q' '%.3q'\n", "abc", "abc", "abc", (qstr)MP_QSTR_True, (qstr)MP_QSTR_True); // fixed string precision mp_printf(&mp_plat_print, "%.*s\n", -1, "abc"); // negative string precision mp_printf(&mp_plat_print, "%b %b\n", 0, 1); // bools #ifndef NDEBUG @@ -204,10 +229,36 @@ static mp_obj_t extra_coverage(void) { #endif mp_printf(&mp_plat_print, "%d\n", 0x80000000); // should print signed mp_printf(&mp_plat_print, "%u\n", 0x80000000); // should print unsigned - mp_printf(&mp_plat_print, "%x\n", 0x80000000); // should print unsigned - mp_printf(&mp_plat_print, "%X\n", 0x80000000); // should print unsigned - mp_printf(&mp_plat_print, "abc\n%"); // string ends in middle of format specifier + mp_printf(&mp_plat_print, "%x\n", 0x8000000f); // should print unsigned + mp_printf(&mp_plat_print, "%X\n", 0x8000000f); // should print unsigned + // note: storing the string in a variable is enough to prevent the + // format string checker from checking this format string. Otherwise, + // it would be a compile time diagnostic under the format string + // checker. + const char msg[] = "abc\n%"; + mp_printf(&mp_plat_print, msg); // string ends in middle of format specifier mp_printf(&mp_plat_print, "%%\n"); // literal % character + mp_printf(&mp_plat_print, ".%-3s.\n", "a"); // left adjust + + // Check that all kinds of mp_printf arguments are parsed out + // correctly, by having a char argument before and after each main type + // of value that can be formatted. + mp_printf(&mp_plat_print, "%c%%%c\n", '<', '>'); + mp_printf(&mp_plat_print, "%c%p%c\n", '<', (void *)0xaaaa, '>'); + mp_printf(&mp_plat_print, "%c%b%c\n", '<', true, '>'); + mp_printf(&mp_plat_print, "%c%d%c\n", '<', 0xaaaa, '>'); + mp_printf(&mp_plat_print, "%c%ld%c\n", '<', 0xaaaal, '>'); + mp_printf(&mp_plat_print, "%c" INT_FMT "%c\n", '<', (mp_int_t)0xaaaa, '>'); + mp_printf(&mp_plat_print, "%c%s%c\n", '<', "test", '>'); + mp_printf(&mp_plat_print, "%c%f%c\n", '<', MICROPY_FLOAT_CONST(1000.), '>'); + mp_printf(&mp_plat_print, "%c%q%c\n", '<', (qstr)MP_QSTR_True, '>'); + if (sizeof(mp_int_t) == 8) { + mp_printf(&mp_plat_print, "%c%lld%c\n", '<', LLONG_MAX, '>'); + } else { + mp_printf(&mp_plat_print, "<9223372036854775807>\n"); + } + + } // GC @@ -220,11 +271,11 @@ static mp_obj_t extra_coverage(void) { gc_unlock(); // using gc_realloc to resize to 0, which means free the memory - void *p = gc_alloc(4, false); + void *p = gc_alloc(4, 0); mp_printf(&mp_plat_print, "%p\n", gc_realloc(p, 0, false)); // calling gc_nbytes with a non-heap pointer - mp_printf(&mp_plat_print, "%p\n", gc_nbytes(NULL)); + mp_printf(&mp_plat_print, "%d\n", (int)gc_nbytes(NULL)); } // GC initialisation and allocation stress test, to check the logic behind ALLOC_TABLE_GAP_BYTE @@ -285,7 +336,7 @@ static mp_obj_t extra_coverage(void) { } ptrs[i][j] = j; } - mp_printf(&mp_plat_print, "%d %d\n", i, all_zero); + mp_printf(&mp_plat_print, "%d %d\n", (int)i, (int)all_zero); // hide the pointer from the GC and collect ptrs[i] = FLIP_POINTER(ptrs[i]); @@ -301,7 +352,7 @@ static mp_obj_t extra_coverage(void) { break; } } - mp_printf(&mp_plat_print, "%d %d\n", i, correct_contents); + mp_printf(&mp_plat_print, "%d %d\n", (int)i, (int)correct_contents); } // free the memory blocks @@ -400,7 +451,7 @@ static mp_obj_t extra_coverage(void) { // create a bytearray via mp_obj_new_bytearray mp_buffer_info_t bufinfo; mp_get_buffer_raise(mp_obj_new_bytearray(4, "data"), &bufinfo, MP_BUFFER_RW); - mp_printf(&mp_plat_print, "%.*s\n", bufinfo.len, bufinfo.buf); + mp_printf(&mp_plat_print, "%.*s\n", (int)bufinfo.len, bufinfo.buf); } // mpz @@ -464,6 +515,38 @@ static mp_obj_t extra_coverage(void) { mp_int_t value_signed; mpz_as_int_checked(&mpz, &value_signed); mp_printf(&mp_plat_print, "%d\n", (int)value_signed); + + // hash the zero mpz integer + mpz_set_from_int(&mpz, 0); + mp_printf(&mp_plat_print, "%d\n", (int)mpz_hash(&mpz)); + + // convert the mpz zero integer to int + mp_printf(&mp_plat_print, "%d\n", mpz_as_int_checked(&mpz, &value_signed)); + mp_printf(&mp_plat_print, "%d\n", (int)value_signed); + + // mpz_set_from_float with 0 as argument + mpz_set_from_float(&mpz, 0); + mp_printf(&mp_plat_print, "%f\n", mpz_as_float(&mpz)); + + // convert a large integer value (stored in a mpz) to mp_uint_t and to ll; + mp_obj_t obj_bigint = mp_obj_new_int_from_uint((mp_uint_t)0xdeadbeef); + mp_printf(&mp_plat_print, "%x\n", (int)mp_obj_get_uint(obj_bigint)); + obj_bigint = mp_obj_new_int_from_ll(0xc0ffee777c0ffeell); + long long value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); + + // convert a large integer value (stored via a struct object) to uint and to ll + // `deadbeef` global is an uctypes.struct defined by extra_coverage.py + obj_bigint = mp_load_global(MP_QSTR_deadbeef); + mp_printf(&mp_plat_print, "%x\n", (int)mp_obj_get_uint(obj_bigint)); + value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); + + // convert a smaller integer value to mp_uint_t and to ll + obj_bigint = mp_obj_new_int_from_uint(0xc0ffee); + mp_printf(&mp_plat_print, "%x\n", (int)mp_obj_get_uint(obj_bigint)); + value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); } // runtime utils @@ -481,7 +564,7 @@ static mp_obj_t extra_coverage(void) { mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str_from_cstr("abc"), mp_obj_new_str_from_cstr("abc")); // mp_obj_int_get_checked with mp_obj_int_t that has a value that is a small integer - mp_printf(&mp_plat_print, "%d\n", mp_obj_int_get_checked(mp_obj_int_new_mpz())); + mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_checked(MP_OBJ_FROM_PTR(mp_obj_int_new_mpz()))); // mp_obj_int_get_uint_checked with non-negative small-int mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_uint_checked(MP_OBJ_NEW_SMALL_INT(1))); @@ -636,36 +719,36 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# ringbuf\n"); // Single-byte put/get with empty ringbuf. - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_free(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); ringbuf_put(&ringbuf, 22); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_get(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); // Two-byte put/get with empty ringbuf. ringbuf_put16(&ringbuf, 0xaa55); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%04x\n", ringbuf_get16(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); // Two-byte put with full ringbuf. for (int i = 0; i < RINGBUF_SIZE; ++i) { ringbuf_put(&ringbuf, i); } - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0x11bb)); // Two-byte put with one byte free. ringbuf_get(&ringbuf); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0x3377)); ringbuf_get(&ringbuf); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0xcc99)); for (int i = 0; i < RINGBUF_SIZE - 2; ++i) { ringbuf_get(&ringbuf); } mp_printf(&mp_plat_print, "%04x\n", ringbuf_get16(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); // Two-byte put with wrap around on first byte: ringbuf_clear(&ringbuf); @@ -777,7 +860,7 @@ static mp_obj_t extra_coverage(void) { mp_obj_streamtest_t *s2 = mp_obj_malloc(mp_obj_streamtest_t, &mp_type_stest_textio2); // return a tuple of data for testing on the Python side - mp_obj_t items[] = {(mp_obj_t)&str_no_hash_obj, (mp_obj_t)&bytes_no_hash_obj, MP_OBJ_FROM_PTR(s), MP_OBJ_FROM_PTR(s2)}; + mp_obj_t items[] = {MP_OBJ_FROM_PTR(&str_no_hash_obj), MP_OBJ_FROM_PTR(&bytes_no_hash_obj), MP_OBJ_FROM_PTR(s), MP_OBJ_FROM_PTR(s2)}; return mp_obj_new_tuple(MP_ARRAY_SIZE(items), items); } MP_DEFINE_CONST_FUN_OBJ_0(extra_coverage_obj, extra_coverage); diff --git a/ports/unix/coveragecpp.cpp b/ports/unix/coveragecpp.cpp index 377b5acf763ce..4d21398142d0f 100644 --- a/ports/unix/coveragecpp.cpp +++ b/ports/unix/coveragecpp.cpp @@ -3,10 +3,61 @@ extern "C" { #include "py/obj.h" } +// Invoke all (except one, see below) public API macros which initialize structs to make sure +// they are C++-compatible, meaning they explicitly initialize all struct members. +mp_obj_t f0(); +MP_DEFINE_CONST_FUN_OBJ_0(f0_obj, f0); +mp_obj_t f1(mp_obj_t); +MP_DEFINE_CONST_FUN_OBJ_1(f1_obj, f1); +mp_obj_t f2(mp_obj_t, mp_obj_t); +MP_DEFINE_CONST_FUN_OBJ_2(f2_obj, f2); +mp_obj_t f3(mp_obj_t, mp_obj_t, mp_obj_t); +MP_DEFINE_CONST_FUN_OBJ_3(f3_obj, f3); +mp_obj_t fvar(size_t, const mp_obj_t *); +MP_DEFINE_CONST_FUN_OBJ_VAR(fvar_obj, 1, fvar); +mp_obj_t fvarbetween(size_t, const mp_obj_t *); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fvarbetween_obj, 1, 2, fvarbetween); +mp_obj_t fkw(size_t, const mp_obj_t *, mp_map_t *); +MP_DEFINE_CONST_FUN_OBJ_KW(fkw_obj, 1, fkw); + +static const mp_rom_map_elem_t table[] = { + { MP_ROM_QSTR(MP_QSTR_f0), MP_ROM_PTR(&f0_obj) }, +}; +MP_DEFINE_CONST_MAP(map, table); +MP_DEFINE_CONST_DICT(dict, table); + +static const qstr attrtuple_fields[] = { + MP_QSTR_f0, +}; +MP_DEFINE_ATTRTUPLE(attrtuple, attrtuple_fields, 1, MP_ROM_PTR(&f0_obj)); + +void nlr_cb(void *); +void nlr_cb(void *){ +} + +// The MP_DEFINE_CONST_OBJ_TYPE macro is not C++-compatible because each of the +// MP_DEFINE_CONST_OBJ_TYPE_NARGS_X macros only initializes some of _mp_obj_type_t's +// .slot_index_xxx members but that cannot be fixed to be done in a deterministic way. + + #if defined(MICROPY_UNIX_COVERAGE) // Just to test building of C++ code. static mp_obj_t extra_cpp_coverage_impl() { + MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, nlr_cb, (void *) nlr_cb); + + // To avoid 'error: unused variable [-Werror,-Wunused-const-variable]'. + (void) ctx; + (void) f0_obj; + (void) f1_obj; + (void) f2_obj; + (void) f3_obj; + (void) fvar_obj; + (void) fvarbetween_obj; + (void) fkw_obj; + (void) map; + (void) dict; + (void) attrtuple; return mp_const_none; } diff --git a/ports/unix/input.c b/ports/unix/input.c index 31926a5a8e1af..260e9eac8c9db 100644 --- a/ports/unix/input.c +++ b/ports/unix/input.c @@ -104,6 +104,9 @@ void prompt_write_history(void) { #if MICROPY_USE_READLINE == 1 char *home = getenv("HOME"); if (home != NULL) { + if (MP_STATE_THREAD(gc_lock_depth) != 0) { + return; + } vstr_t vstr; vstr_init(&vstr, 50); vstr_printf(&vstr, "%s/.micropython.history", home); diff --git a/ports/unix/main.c b/ports/unix/main.c index 259b183eb766d..89937008e0531 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -54,9 +54,11 @@ #include "extmod/vfs_posix.h" #include "genhdr/mpversion.h" #include "input.h" +#include "stack_size.h" +#include "shared/runtime/pyexec.h" // Command line options, with their defaults -static bool compile_only = false; +bool mp_compile_only = false; static uint emit_opt = MP_EMIT_OPT_NONE; #if MICROPY_ENABLE_GC @@ -112,8 +114,6 @@ static int handle_uncaught_exception(mp_obj_base_t *exc) { } #define LEX_SRC_STR (1) -#define LEX_SRC_VSTR (2) -#define LEX_SRC_FILENAME (3) #define LEX_SRC_STDIN (4) // Returns standard error codes: 0 for success, 1 for all other errors, @@ -129,19 +129,13 @@ static int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu if (source_kind == LEX_SRC_STR) { const char *line = source; lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false); - } else if (source_kind == LEX_SRC_VSTR) { - const vstr_t *vstr = source; - lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, false); - } else if (source_kind == LEX_SRC_FILENAME) { - const char *filename = (const char *)source; - lex = mp_lexer_new_from_file(qstr_from_str(filename)); } else { // LEX_SRC_STDIN lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false); } qstr source_name = lex->source_name; - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ if (input_kind == MP_PARSE_FILE_INPUT) { mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); } @@ -160,7 +154,7 @@ static int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu mp_obj_t module_fun = mp_compile(&parse_tree, source_name, is_repl); - if (!compile_only) { + if (!mp_compile_only) { // execute it mp_call_function_0(module_fun); } @@ -197,92 +191,32 @@ static char *strjoin(const char *s1, int sep_char, const char *s2) { #endif static int do_repl(void) { - mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION); - mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE); - mp_hal_stdout_tx_str("\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); - #if MICROPY_USE_READLINE == 1 - // use MicroPython supplied readline + // use MicroPython supplied readline-based REPL - vstr_t line; - vstr_init(&line, 16); + int ret = 0; for (;;) { - mp_hal_stdio_mode_raw(); - - input_restart: - vstr_reset(&line); - int ret = readline(&line, mp_repl_get_ps1()); - mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; - - if (ret == CHAR_CTRL_C) { - // cancel input - mp_hal_stdout_tx_str("\r\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // EOF - printf("\n"); - mp_hal_stdio_mode_orig(); - vstr_clear(&line); - return 0; - } else if (ret == CHAR_CTRL_E) { - // paste mode - mp_hal_stdout_tx_str("\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\n=== "); - vstr_reset(&line); - for (;;) { - char c = mp_hal_stdin_rx_chr(); - if (c == CHAR_CTRL_C) { - // cancel everything - mp_hal_stdout_tx_str("\n"); - goto input_restart; - } else if (c == CHAR_CTRL_D) { - // end of input - mp_hal_stdout_tx_str("\n"); - break; - } else { - // add char to buffer and echo - vstr_add_byte(&line, c); - if (c == '\r') { - mp_hal_stdout_tx_str("\n=== "); - } else { - mp_hal_stdout_tx_strn(&c, 1); - } - } - } - parse_input_kind = MP_PARSE_FILE_INPUT; - } else if (line.len == 0) { - if (ret != 0) { - printf("\n"); + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if ((ret = pyexec_raw_repl()) != 0) { + break; } - goto input_restart; } else { - // got a line with non-zero length, see if it needs continuing - while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { - vstr_add_byte(&line, '\n'); - ret = readline(&line, mp_repl_get_ps2()); - if (ret == CHAR_CTRL_C) { - // cancel everything - printf("\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // stop entering compound statement - break; - } + if ((ret = pyexec_friendly_repl()) != 0) { + break; } } - - mp_hal_stdio_mode_orig(); - - ret = execute_from_lexer(LEX_SRC_VSTR, &line, parse_input_kind, true); - if (ret & FORCED_EXIT) { - return ret; - } } + return ret; #else // use simple readline + mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION); + mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE); + mp_hal_stdout_tx_str("\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); + for (;;) { char *line = prompt((char *)mp_repl_get_ps1()); if (line == NULL) { @@ -310,12 +244,40 @@ static int do_repl(void) { #endif } +static inline int convert_pyexec_result(int ret) { + #if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING + // With exit code handling enabled: + // pyexec returns exit code with PYEXEC_FORCED_EXIT flag set for SystemExit + // Unix port expects: 0 for success, non-zero for error/exit + if (ret & PYEXEC_FORCED_EXIT) { + // SystemExit: extract exit code from lower bits + return ret & 0xFF; + } + // Normal execution or exception: return as-is (0 for success, 1 for exception) + return ret; + #else + // pyexec returns 1 for success, 0 for exception, PYEXEC_FORCED_EXIT for SystemExit + // Convert to unix port's expected codes: 0 for success, 1 for exception, FORCED_EXIT|val for SystemExit + if (ret == 1) { + return 0; // success + } else if (ret & PYEXEC_FORCED_EXIT) { + return ret; // SystemExit with exit value in lower 8 bits + } else { + return 1; // exception + } + #endif +} + static int do_file(const char *file) { - return execute_from_lexer(LEX_SRC_FILENAME, file, MP_PARSE_FILE_INPUT, false); + return convert_pyexec_result(pyexec_file(file)); } static int do_str(const char *str) { - return execute_from_lexer(LEX_SRC_STR, str, MP_PARSE_FILE_INPUT, false); + vstr_t vstr; + vstr.buf = (char *)str; + vstr.len = strlen(str); + int ret = pyexec_vstr(&vstr, true); + return convert_pyexec_result(ret); } static void print_help(char **argv) { @@ -384,7 +346,7 @@ static void pre_process_options(int argc, char **argv) { } if (0) { } else if (strcmp(argv[a + 1], "compile-only") == 0) { - compile_only = true; + mp_compile_only = true; } else if (strcmp(argv[a + 1], "emit=bytecode") == 0) { emit_opt = MP_EMIT_OPT_BYTECODE; #if MICROPY_EMIT_NATIVE @@ -456,10 +418,13 @@ static void set_sys_argv(char *argv[], int argc, int start_arg) { #if MICROPY_PY_SYS_EXECUTABLE extern mp_obj_str_t mp_sys_executable_obj; -static char executable_path[MICROPY_ALLOC_PATH_MAX]; +static char *executable_path = NULL; static void sys_set_excecutable(char *argv0) { - if (realpath(argv0, executable_path)) { + if (executable_path == NULL) { + executable_path = realpath(argv0, NULL); + } + if (executable_path != NULL) { mp_obj_str_set_data(&mp_sys_executable_obj, (byte *)executable_path, strlen(executable_path)); } } @@ -479,11 +444,7 @@ int main(int argc, char **argv) { #endif // Define a reasonable stack limit to detect stack overflow. - mp_uint_t stack_size = 40000 * (sizeof(void *) / 4); - #if defined(__arm__) && !defined(__thumb2__) - // ARM (non-Thumb) architectures require more stack. - stack_size *= 2; - #endif + mp_uint_t stack_size = 40000 * UNIX_STACK_MULTIPLIER; // We should capture stack top ASAP after start, and it should be // captured guaranteedly before any other stack variables are allocated. @@ -619,19 +580,6 @@ MP_NOINLINE int main_(int argc, char **argv) { } #endif - // Here is some example code to create a class and instance of that class. - // First is the Python, then the C code. - // - // class TestClass: - // pass - // test_obj = TestClass() - // test_obj.attr = 42 - // - // mp_obj_t test_class_type, test_class_instance; - // test_class_type = mp_obj_new_type(qstr_from_str("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); - // mp_store_name(qstr_from_str("test_obj"), test_class_instance = mp_call_function_0(test_class_type)); - // mp_store_attr(test_class_instance, qstr_from_str("attr"), mp_obj_new_int(42)); - /* printf("bytes:\n"); printf(" total %d\n", m_get_total_bytes_allocated()); @@ -685,12 +633,18 @@ MP_NOINLINE int main_(int argc, char **argv) { subpkg_tried = false; reimport: + mp_hal_set_interrupt_char(CHAR_CTRL_C); if (nlr_push(&nlr) == 0) { mod = mp_builtin___import__(MP_ARRAY_SIZE(import_args), import_args); + mp_hal_set_interrupt_char(-1); + mp_handle_pending(true); nlr_pop(); } else { // uncaught exception - return handle_uncaught_exception(nlr.ret_val) & 0xff; + mp_hal_set_interrupt_char(-1); + mp_handle_pending(false); + ret = handle_uncaught_exception(nlr.ret_val) & 0xff; + break; } // If this module is a package, see if it has a `__main__.py`. @@ -727,11 +681,9 @@ MP_NOINLINE int main_(int argc, char **argv) { return invalid_args(); } } else { - char *pathbuf = malloc(PATH_MAX); - char *basedir = realpath(argv[a], pathbuf); + char *basedir = realpath(argv[a], NULL); if (basedir == NULL) { mp_printf(&mp_stderr_print, "%s: can't open file '%s': [Errno %d] %s\n", argv[0], argv[a], errno, strerror(errno)); - free(pathbuf); // CPython exits with 2 in such case ret = 2; break; @@ -740,7 +692,7 @@ MP_NOINLINE int main_(int argc, char **argv) { // Set base dir of the script as first entry in sys.path. char *p = strrchr(basedir, '/'); mp_obj_list_store(mp_sys_path, MP_OBJ_NEW_SMALL_INT(0), mp_obj_new_str_via_qstr(basedir, p - basedir)); - free(pathbuf); + free(basedir); set_sys_argv(argv, argc, a); ret = do_file(argv[a]); @@ -780,7 +732,7 @@ MP_NOINLINE int main_(int argc, char **argv) { #endif #if MICROPY_PY_BLUETOOTH - void mp_bluetooth_deinit(void); + int mp_bluetooth_deinit(void); mp_bluetooth_deinit(); #endif @@ -806,6 +758,11 @@ MP_NOINLINE int main_(int argc, char **argv) { #endif #endif + #if MICROPY_PY_SYS_EXECUTABLE && !defined(NDEBUG) + // Again, make memory leak detector happy + free(executable_path); + #endif + // printf("total bytes = %d\n", m_get_total_bytes_allocated()); return ret & 0xff; } diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index fbd94b5ecd129..4f0550dbea765 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -34,6 +34,7 @@ #include "py/mphal.h" #include "py/runtime.h" +#include "shared/timeutils/timeutils.h" #ifdef _WIN32 static inline int msec_sleep_tv(struct timeval *tv) { @@ -130,12 +131,7 @@ static mp_obj_t mod_time_gm_local_time(size_t n_args, const mp_obj_t *args, stru if (n_args == 0) { t = time(NULL); } else { - #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE - mp_float_t val = mp_obj_get_float(args[0]); - t = (time_t)MICROPY_FLOAT_C_FUN(trunc)(val); - #else - t = mp_obj_get_int(args[0]); - #endif + t = (time_t)timeutils_obj_get_timestamp(args[0]); } struct tm *tm = time_func(&t); @@ -193,10 +189,10 @@ static mp_obj_t mod_time_mktime(mp_obj_t tuple) { time.tm_isdst = -1; // auto-detect } time_t ret = mktime(&time); - if (ret == -1) { + if (ret == (time_t)-1) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("invalid mktime usage")); } - return mp_obj_new_int(ret); + return timeutils_obj_from_timestamp(ret); } MP_DEFINE_CONST_FUN_OBJ_1(mod_time_mktime_obj, mod_time_mktime); diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 4d9fe9f1dc4a2..991db97f921f2 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -32,6 +32,9 @@ // features to work on Unix-like systems, see mpconfigvariant.h (and // mpconfigvariant_common.h) for feature enabling. +// For time_t, needed by MICROPY_TIMESTAMP_IMPL_TIME_T. +#include + // For size_t and ssize_t #include @@ -78,21 +81,6 @@ #define MICROPY_EMIT_ARM (1) #endif -// Type definitions for the specific machine based on the word size. -#ifndef MICROPY_OBJ_REPR -#ifdef __LP64__ -typedef long mp_int_t; // must be pointer size -typedef unsigned long mp_uint_t; // must be pointer size -#else -// These are definitions for machines where sizeof(int) == sizeof(void*), -// regardless of actual size. -typedef int mp_int_t; // must be pointer size -typedef unsigned int mp_uint_t; // must be pointer size -#endif -#else -// Assume that if we already defined the obj repr then we also defined types. -#endif - // Cannot include , as it may lead to symbol name clashes #if _FILE_OFFSET_BITS == 64 && !defined(__LP64__) typedef long long mp_off_t; @@ -135,6 +123,9 @@ typedef long mp_off_t; // VFS stat functions should return time values relative to 1970/1/1 #define MICROPY_EPOCH_IS_1970 (1) +// port modtime functions use time_t +#define MICROPY_TIMESTAMP_IMPL (MICROPY_TIMESTAMP_IMPL_TIME_T) + // Assume that select() call, interrupted with a signal, and erroring // with EINTR, updates remaining timeout value. #define MICROPY_SELECT_REMAINING_TIME (1) @@ -145,6 +136,9 @@ typedef long mp_off_t; #define MICROPY_STACKLESS_STRICT (0) #endif +// Recursive mutex is needed when threading is enabled, regardless of GIL setting. +#define MICROPY_PY_THREAD_RECURSIVE_MUTEX (MICROPY_PY_THREAD) + // Implementation of the machine module. #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/unix/modmachine.c" @@ -172,6 +166,12 @@ typedef long mp_off_t; // Enable sys.executable. #define MICROPY_PY_SYS_EXECUTABLE (1) +// Enable support for compile-only mode. +#define MICROPY_PYEXEC_COMPILE_ONLY (1) + +// Enable handling of sys.exit() exit codes. +#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (1) + #define MICROPY_PY_SOCKET_LISTEN_BACKLOG_DEFAULT (SOMAXCONN < 128 ? SOMAXCONN : 128) // Bare-metal ports don't have stderr. Printing debug to stderr may give tests diff --git a/ports/unix/mpconfigport.mk b/ports/unix/mpconfigport.mk index 26c04faf4c5ca..c836663cd2339 100644 --- a/ports/unix/mpconfigport.mk +++ b/ports/unix/mpconfigport.mk @@ -14,6 +14,7 @@ MICROPY_PY_BTREE = 0 # _thread module using pthreads MICROPY_PY_THREAD = 1 +MICROPY_PY_THREAD_GIL = 0 # Subset of CPython termios module MICROPY_PY_TERMIOS = 1 diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index 7f71217632a8e..afac25bf86529 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -110,3 +110,6 @@ enum { void mp_hal_get_mac(int idx, uint8_t buf[6]); #endif + +// Global variable to control compile-only mode. +extern bool mp_compile_only; diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index 5172645bc147a..a41b3ec9f4701 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "py/mpthread.h" #include "py/gc.h" +#include "stack_size.h" #if MICROPY_PY_THREAD @@ -45,8 +46,14 @@ // potential conflict with other uses of the more commonly used SIGUSR1. #ifdef SIGRTMIN #define MP_THREAD_GC_SIGNAL (SIGRTMIN + 5) +#ifdef __ANDROID__ +#define MP_THREAD_TERMINATE_SIGNAL (SIGRTMIN + 6) +#endif #else #define MP_THREAD_GC_SIGNAL (SIGUSR1) +#ifdef __ANDROID__ +#define MP_THREAD_TERMINATE_SIGNAL (SIGUSR2) +#endif #endif // This value seems to be about right for both 32-bit and 64-bit builds. @@ -107,6 +114,18 @@ static void mp_thread_gc(int signo, siginfo_t *info, void *context) { } } +// On Android, pthread_cancel and pthread_setcanceltype are not implemented. +// To achieve that result a new signal handler responding on either +// (SIGRTMIN + 6) or SIGUSR2 is installed on every child thread. The sole +// purpose of this new signal handler is to terminate the thread in a safe +// asynchronous manner. + +#ifdef __ANDROID__ +static void mp_thread_terminate(int signo, siginfo_t *info, void *context) { + pthread_exit(NULL); +} +#endif + void mp_thread_init(void) { pthread_key_create(&tls_key, NULL); pthread_setspecific(tls_key, &mp_state_ctx.thread); @@ -135,6 +154,14 @@ void mp_thread_init(void) { sa.sa_sigaction = mp_thread_gc; sigemptyset(&sa.sa_mask); sigaction(MP_THREAD_GC_SIGNAL, &sa, NULL); + + // Install a signal handler for asynchronous termination if needed. + #if defined(__ANDROID__) + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = mp_thread_terminate; + sigemptyset(&sa.sa_mask); + sigaction(MP_THREAD_TERMINATE_SIGNAL, &sa, NULL); + #endif } void mp_thread_deinit(void) { @@ -142,7 +169,11 @@ void mp_thread_deinit(void) { while (thread->next != NULL) { mp_thread_t *th = thread; thread = thread->next; + #if defined(__ANDROID__) + pthread_kill(th->id, MP_THREAD_TERMINATE_SIGNAL); + #else pthread_cancel(th->id); + #endif free(th); } mp_thread_unix_end_atomic_section(); @@ -200,7 +231,9 @@ void mp_thread_start(void) { } #endif + #if !defined(__ANDROID__) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + #endif mp_thread_unix_begin_atomic_section(); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == pthread_self()) { @@ -212,14 +245,14 @@ void mp_thread_start(void) { } mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) { - // default stack size is 8k machine-words + // default stack size if (*stack_size == 0) { - *stack_size = 8192 * sizeof(void *); + *stack_size = 32768 * UNIX_STACK_MULTIPLIER; } // minimum stack size is set by pthreads - if (*stack_size < PTHREAD_STACK_MIN) { - *stack_size = PTHREAD_STACK_MIN; + if (*stack_size < (size_t)PTHREAD_STACK_MIN) { + *stack_size = (size_t)PTHREAD_STACK_MIN; } // ensure there is enough stack to include a stack-overflow margin diff --git a/ports/unix/stack_size.h b/ports/unix/stack_size.h new file mode 100644 index 0000000000000..f6159bb69d529 --- /dev/null +++ b/ports/unix/stack_size.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Angus Gratton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_UNIX_STACK_SIZE_H +#define MICROPY_INCLUDED_UNIX_STACK_SIZE_H + +#include "py/misc.h" + +// Define scaling factors for the stack size (also applies to main thread) +#ifndef UNIX_STACK_MULTIPLIER + +#if defined(__arm__) && !defined(__thumb2__) +// ARM (non-Thumb) architectures require more stack. +#define UNIX_STACK_MUL_ARM 2 +#else +#define UNIX_STACK_MUL_ARM 1 +#endif + +#if MP_SANITIZER_BUILD +// Sanitizer features consume significant stack in some cases +// This multiplier can probably be removed when using GCC 12 or newer. +#define UNIX_STACK_MUL_SANITIZERS 4 +#else +#define UNIX_STACK_MUL_SANITIZERS 1 +#endif + +// Double the stack size for 64-bit builds, plus additional scaling +#define UNIX_STACK_MULTIPLIER ((sizeof(void *) / 4) * UNIX_STACK_MUL_ARM * UNIX_STACK_MUL_SANITIZERS) + +#endif // UNIX_STACK_MULTIPLIER + +#endif // MICROPY_INCLUDED_UNIX_STACK_SIZE_H diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index ca79d3d0d2b61..03f77ec14c6db 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -39,11 +39,22 @@ // Enable additional features. #define MICROPY_DEBUG_PARSE_RULE_NAME (1) +// CIRCUITPY-CHANGE: off +#define MICROPY_PY_SYS_SETTRACE (0) #define MICROPY_TRACKED_ALLOC (1) #define MICROPY_WARNINGS_CATEGORY (1) #undef MICROPY_VFS_ROM_IOCTL #define MICROPY_VFS_ROM_IOCTL (1) #define MICROPY_PY_CRYPTOLIB_CTR (1) +// CIRCUITPY-CHANGE: off +#define MICROPY_SCHEDULER_STATIC_NODES (0) + +// Enable os.uname for attrtuple coverage test +#define MICROPY_PY_OS_UNAME (1) +#define MICROPY_HW_BOARD_NAME "a machine" +#define MICROPY_HW_MCU_NAME MICROPY_PY_SYS_PLATFORM +// Keep the standard banner message +#define MICROPY_BANNER_MACHINE MICROPY_PY_SYS_PLATFORM " [" MICROPY_PLATFORM_COMPILER "] version" // CIRCUITPY-CHANGE: Disable things never used in circuitpython #define MICROPY_PY_CRYPTOLIB (0) diff --git a/shared/netutils/dhcpserver.h b/ports/unix/variants/longlong/mpconfigvariant.h similarity index 60% rename from shared/netutils/dhcpserver.h rename to ports/unix/variants/longlong/mpconfigvariant.h index 2349d2ea427f4..d50d360b1fe5c 100644 --- a/shared/netutils/dhcpserver.h +++ b/ports/unix/variants/longlong/mpconfigvariant.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2018-2019 Damien P. George + * Copyright (c) 2016 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,27 +23,22 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H -#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H -#include "lwip/ip_addr.h" +// This config exists to test that the MICROPY_LONGINT_IMPL_LONGLONG variant +// (i.e. minimal form of "big integer" that's backed by 64-bit int only) builds +// and passes tests. -#define DHCPS_BASE_IP (16) -#define DHCPS_MAX_IP (8) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) -typedef struct _dhcp_server_lease_t { - uint8_t mac[6]; - uint16_t expiry; -} dhcp_server_lease_t; +// We build it on top of REPR C, which uses memory-efficient floating point +// objects encoded directly mp_obj_t (30 bits only). +// Therefore this variant should be built using MICROPY_FORCE_32BIT=1 -typedef struct _dhcp_server_t { - ip_addr_t ip; - ip_addr_t nm; - dhcp_server_lease_t lease[DHCPS_MAX_IP]; - struct udp_pcb *udp; -} dhcp_server_t; +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) -void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm); -void dhcp_server_deinit(dhcp_server_t *d); +// Set base feature level. +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) -#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H +// Enable extra Unix features. +#include "../mpconfigvariant_common.h" diff --git a/ports/unix/variants/longlong/mpconfigvariant.mk b/ports/unix/variants/longlong/mpconfigvariant.mk new file mode 100644 index 0000000000000..2d2c3706469fb --- /dev/null +++ b/ports/unix/variants/longlong/mpconfigvariant.mk @@ -0,0 +1,8 @@ +# build interpreter with "bigints" implemented as "longlong" + +# otherwise, small int is essentially 64-bit +MICROPY_FORCE_32BIT := 1 + +MICROPY_PY_FFI := 0 + +MPY_TOOL_FLAGS = -mlongint-impl longlong diff --git a/ports/unix/variants/minimal/mpconfigvariant.h b/ports/unix/variants/minimal/mpconfigvariant.h index 97ed786b8f409..0e280a8f73058 100644 --- a/ports/unix/variants/minimal/mpconfigvariant.h +++ b/ports/unix/variants/minimal/mpconfigvariant.h @@ -49,6 +49,7 @@ #define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) #define MICROPY_ENABLE_COMPILER (1) #define MICROPY_ENABLE_EXTERNAL_IMPORT (1) +#define MICROPY_STACK_CHECK (1) #define MICROPY_FULL_CHECKS (1) #define MICROPY_HELPER_REPL (1) #define MICROPY_KBD_EXCEPTION (1) @@ -61,6 +62,5 @@ #define MICROPY_PY_BUILTINS_RANGE_ATTRS (1) #define MICROPY_PY_GENERATOR_PEND_THROW (1) -// Enable just the sys and os built-in modules. -#define MICROPY_PY_SYS (1) +// Add just the os built-in module. #define MICROPY_PY_OS (1) diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h index 9eeed8797366c..1ac59c95572dd 100644 --- a/ports/unix/variants/mpconfigvariant_common.h +++ b/ports/unix/variants/mpconfigvariant_common.h @@ -29,7 +29,7 @@ // Send raise KeyboardInterrupt directly from the signal handler rather than // scheduling it into the VM. -#define MICROPY_ASYNC_KBD_INTR (1) +#define MICROPY_ASYNC_KBD_INTR (!MICROPY_PY_THREAD_GIL) // Enable helpers for printing debugging information. #ifndef MICROPY_DEBUG_PRINTERS @@ -104,12 +104,6 @@ #define MICROPY_PY_TIME_CUSTOM_SLEEP (1) #define MICROPY_PY_TIME_INCLUDEFILE "ports/unix/modtime.c" -#if MICROPY_PY_SSL -#define MICROPY_PY_HASHLIB_MD5 (1) -#define MICROPY_PY_HASHLIB_SHA1 (1) -#define MICROPY_PY_CRYPTOLIB (1) -#endif - // The "select" module is enabled by default, but disable select.select(). #define MICROPY_PY_SELECT_POSIX_OPTIMISATIONS (1) #define MICROPY_PY_SELECT_SELECT (0) diff --git a/ports/unix/variants/nanbox/mpconfigvariant.h b/ports/unix/variants/nanbox/mpconfigvariant.h index 7b13b7dc6ce4e..c9e9c1f63d95d 100644 --- a/ports/unix/variants/nanbox/mpconfigvariant.h +++ b/ports/unix/variants/nanbox/mpconfigvariant.h @@ -41,10 +41,3 @@ #define MICROPY_EMIT_X64 (0) #define MICROPY_EMIT_THUMB (0) #define MICROPY_EMIT_ARM (0) - -#include - -typedef int64_t mp_int_t; -typedef uint64_t mp_uint_t; -#define UINT_FMT "%llu" -#define INT_FMT "%lld" diff --git a/py/argcheck.c b/py/argcheck.c index 9302dec96c396..155e29bdc15bf 100644 --- a/py/argcheck.c +++ b/py/argcheck.c @@ -170,13 +170,13 @@ void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, } #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE -NORETURN void mp_arg_error_terse_mismatch(void) { +MP_NORETURN void mp_arg_error_terse_mismatch(void) { mp_raise_TypeError(MP_ERROR_TEXT("argument num/types mismatch")); } #endif #if MICROPY_CPYTHON_COMPAT -NORETURN void mp_arg_error_unimpl_kw(void) { +MP_NORETURN void mp_arg_error_unimpl_kw(void) { mp_raise_NotImplementedError(MP_ERROR_TEXT("keyword argument(s) not implemented - use normal args instead")); } #endif @@ -312,6 +312,6 @@ mp_int_t mp_arg_validate_type_int(mp_obj_t obj, qstr arg_name) { return an_int; } -NORETURN void mp_arg_error_invalid(qstr arg_name) { +MP_NORETURN void mp_arg_error_invalid(qstr arg_name) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), arg_name); } diff --git a/py/asmarm.c b/py/asmarm.c index 6fa751b32eb7c..15bc73b61eca6 100644 --- a/py/asmarm.c +++ b/py/asmarm.c @@ -36,7 +36,7 @@ #include "py/asmarm.h" -#define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000) +#define REG_TEMP ASM_ARM_REG_R8 // Insert word into instruction flow static void emit(asm_arm_t *as, uint op) { @@ -171,8 +171,8 @@ void asm_arm_entry(asm_arm_t *as, int num_locals) { if (as->stack_adjust < 0x100) { emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); } else { - asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, as->stack_adjust); - emit_al(as, asm_arm_op_sub_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, as->stack_adjust); + emit_al(as, asm_arm_op_sub_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, REG_TEMP)); } } } @@ -182,8 +182,8 @@ void asm_arm_exit(asm_arm_t *as) { if (as->stack_adjust < 0x100) { emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); } else { - asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, as->stack_adjust); - emit_al(as, asm_arm_op_add_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, as->stack_adjust); + emit_al(as, asm_arm_op_add_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, REG_TEMP)); } } @@ -293,10 +293,10 @@ void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) { void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) { if (local_num >= 0x40) { - // mov r8, #local_num*4 - // add rd, sp, r8 - asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, local_num << 2); - emit_al(as, asm_arm_op_add_reg(rd, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + // mov temp, #local_num*4 + // add rd, sp, temp + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, local_num << 2); + emit_al(as, asm_arm_op_add_reg(rd, ASM_ARM_REG_SP, REG_TEMP)); } else { // add rd, sp, #local_num*4 emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2)); @@ -333,14 +333,22 @@ void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs) { emit_al(as, 0x1a00050 | (rd << 12) | (rs << 8) | rd); } -void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { - // ldr rd, [rn, #off] - emit_al(as, 0x5900000 | (rn << 16) | (rd << 12) | byte_offset); +void asm_arm_ldr_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { + if (byte_offset < 0x1000) { + // ldr rd, [rn, #off] + emit_al(as, 0x5900000 | (rn << 16) | (rd << 12) | byte_offset); + } else { + // mov temp, #off + // ldr rd, [rn, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x7900000 | (rn << 16) | (rd << 12) | REG_TEMP); + } } -void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn) { - // ldrh rd, [rn] - emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12)); +void asm_arm_ldrh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // ldrh doesn't support scaled register index + emit_al(as, 0x1a00080 | (REG_TEMP << 12) | rn); // mov temp, rn, lsl #1 + emit_al(as, 0x19000b0 | (rm << 16) | (rd << 12) | REG_TEMP); // ldrh rd, [rm, temp]; } void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { @@ -348,31 +356,69 @@ void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offs // ldrh rd, [rn, #off] emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); } else { - // mov r8, #off - // ldrh rd, [rn, r8] - asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, byte_offset); - emit_al(as, 0x19000b0 | (rn << 16) | (rd << 12) | ASM_ARM_REG_R8); + // mov temp, #off + // ldrh rd, [rn, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x19000b0 | (rn << 16) | (rd << 12) | REG_TEMP); } } -void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn) { - // ldrb rd, [rn] - emit_al(as, 0x5d00000 | (rn << 16) | (rd << 12)); +void asm_arm_ldrb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // ldrb rd, [rm, rn] + emit_al(as, 0x7d00000 | (rm << 16) | (rd << 12) | rn); +} + +void asm_arm_ldrb_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { + if (byte_offset < 0x1000) { + // ldrb rd, [rn, #off] + emit_al(as, 0x5d00000 | (rn << 16) | (rd << 12) | byte_offset); + } else { + // mov temp, #off + // ldrb rd, [rn, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x7d00000 | (rn << 16) | (rd << 12) | REG_TEMP); + } } -void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset) { - // str rd, [rm, #off] - emit_al(as, 0x5800000 | (rm << 16) | (rd << 12) | byte_offset); +void asm_arm_ldr_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // ldr rd, [rm, rn, lsl #2] + emit_al(as, 0x7900100 | (rm << 16) | (rd << 12) | rn); } -void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm) { - // strh rd, [rm] - emit_al(as, 0x1c000b0 | (rm << 16) | (rd << 12)); +void asm_arm_str_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset) { + if (byte_offset < 0x1000) { + // str rd, [rm, #off] + emit_al(as, 0x5800000 | (rm << 16) | (rd << 12) | byte_offset); + } else { + // mov temp, #off + // str rd, [rm, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x7800000 | (rm << 16) | (rd << 12) | REG_TEMP); + } } -void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm) { - // strb rd, [rm] - emit_al(as, 0x5c00000 | (rm << 16) | (rd << 12)); +void asm_arm_strh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { + if (byte_offset < 0x100) { + // strh rd, [rn, #off] + emit_al(as, 0x1c000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); + } else { + // mov temp, #off + // strh rd, [rn, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x18000b0 | (rn << 16) | (rd << 12) | REG_TEMP); + } +} + +void asm_arm_strb_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset) { + if (byte_offset < 0x1000) { + // strb rd, [rm, #off] + emit_al(as, 0x5c00000 | (rm << 16) | (rd << 12) | byte_offset); + } else { + // mov temp, #off + // strb rd, [rm, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x7c00000 | (rm << 16) | (rd << 12) | REG_TEMP); + } } void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { @@ -382,8 +428,8 @@ void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { // strh doesn't support scaled register index - emit_al(as, 0x1a00080 | (ASM_ARM_REG_R8 << 12) | rn); // mov r8, rn, lsl #1 - emit_al(as, 0x18000b0 | (rm << 16) | (rd << 12) | ASM_ARM_REG_R8); // strh rd, [rm, r8] + emit_al(as, 0x1a00080 | (REG_TEMP << 12) | rn); // mov temp, rn, lsl #1 + emit_al(as, 0x18000b0 | (rm << 16) | (rd << 12) | REG_TEMP); // strh rd, [rm, temp] } void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { @@ -398,7 +444,7 @@ void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label) { rel -= 8; // account for instruction prefetch, PC is 8 bytes ahead of this instruction rel >>= 2; // in ARM mode the branch target is 32-bit aligned, so the 2 LSB are omitted - if (SIGNED_FIT24(rel)) { + if (MP_FIT_SIGNED(24, rel)) { emit(as, cond | 0xa000000 | (rel & 0xffffff)); } else { printf("asm_arm_bcc: branch does not fit in 24 bits\n"); diff --git a/py/asmarm.h b/py/asmarm.h index a0e057fce4450..f3fd586a37530 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -109,13 +109,18 @@ void asm_arm_lsr_reg_reg(asm_arm_t *as, uint rd, uint rs); void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs); // memory -void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset); -void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn); +void asm_arm_ldr_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset); void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset); -void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn); -void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset); -void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm); -void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm); +void asm_arm_ldrb_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset); +void asm_arm_str_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset); +void asm_arm_strh_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset); +void asm_arm_strb_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset); + +// load from array +void asm_arm_ldr_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); +void asm_arm_ldrh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); +void asm_arm_ldrb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); + // store to array void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); @@ -160,12 +165,12 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); // Holds a pointer to mp_fun_table #define REG_FUN_TABLE ASM_ARM_REG_FUN_TABLE -#define ASM_T asm_arm_t -#define ASM_END_PASS asm_arm_end_pass -#define ASM_ENTRY asm_arm_entry -#define ASM_EXIT asm_arm_exit +#define ASM_T asm_arm_t +#define ASM_END_PASS asm_arm_end_pass +#define ASM_ENTRY(as, num_locals, name) asm_arm_entry((as), (num_locals)) +#define ASM_EXIT asm_arm_exit -#define ASM_JUMP asm_arm_b_label +#define ASM_JUMP asm_arm_b_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ do { \ asm_arm_cmp_reg_i8(as, reg, 0); \ @@ -203,18 +208,28 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_arm_sub_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_arm_mul_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_arm_ldrb_reg_reg((as), (reg_dest), (reg_base)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_arm_ldrh_reg_reg((as), (reg_dest), (reg_base)) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_arm_ldrh_reg_reg_offset((as), (reg_dest), (reg_base), 2 * (uint16_offset)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) - -#define ASM_STORE_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_str_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_value, reg_base) asm_arm_strb_reg_reg((as), (reg_value), (reg_base)) -#define ASM_STORE16_REG_REG(as, reg_value, reg_base) asm_arm_strh_reg_reg((as), (reg_value), (reg_base)) -#define ASM_STORE32_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_arm_ldrb_reg_reg_offset((as), (reg_dest), (reg_base), (byte_offset)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, halfword_offset) asm_arm_ldrh_reg_reg_offset((as), (reg_dest), (reg_base), 2 * (halfword_offset)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg_offset((as), (reg_dest), (reg_base), 4 * (word_offset)) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_value, reg_base, word_offset) ASM_STORE32_REG_REG_OFFSET((as), (reg_value), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_value, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_value), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_value, reg_base, byte_offset) asm_arm_strb_reg_reg_offset((as), (reg_value), (reg_base), (byte_offset)) +#define ASM_STORE16_REG_REG(as, reg_value, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_value), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_value, reg_base, halfword_offset) asm_arm_strh_reg_reg_offset((as), (reg_value), (reg_base), 2 * (halfword_offset)) +#define ASM_STORE32_REG_REG(as, reg_value, reg_base) ASM_STORE32_REG_REG_OFFSET((as), (reg_value), (reg_base), 0) +#define ASM_STORE32_REG_REG_OFFSET(as, reg_value, reg_base, word_offset) asm_arm_str_reg_reg_offset((as), (reg_value), (reg_base), 4 * (word_offset)) + +#define ASM_LOAD8_REG_REG_REG(as, reg_dest, reg_base, reg_index) asm_arm_ldrb_reg_reg_reg((as), (reg_dest), (reg_base), (reg_index)) +#define ASM_LOAD16_REG_REG_REG(as, reg_dest, reg_base, reg_index) asm_arm_ldrh_reg_reg_reg((as), (reg_dest), (reg_base), (reg_index)) +#define ASM_LOAD32_REG_REG_REG(as, reg_dest, reg_base, reg_index) asm_arm_ldr_reg_reg_reg((as), (reg_dest), (reg_base), (reg_index)) +#define ASM_STORE8_REG_REG_REG(as, reg_val, reg_base, reg_index) asm_arm_strb_reg_reg_reg((as), (reg_val), (reg_base), (reg_index)) +#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)) #endif // GENERIC_ASM_API diff --git a/py/asmbase.c b/py/asmbase.c index 3fce543a7f485..07dbf4430f9db 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -53,7 +53,7 @@ void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { } else { // allocating executable RAM is platform specific MP_PLAT_ALLOC_EXEC(as->code_offset, (void **)&as->code_base, &as->code_size); - assert(as->code_base != NULL); + assert(as->code_size == 0 || as->code_base != NULL); } as->pass = pass; as->suppress = false; @@ -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 diff --git a/py/asmrv32.c b/py/asmrv32.c index c24d05a1384d4..1d0cea6c02616 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -36,6 +36,8 @@ #if MICROPY_EMIT_RV32 #include "py/asmrv32.h" +#include "py/mpstate.h" +#include "py/persistentcode.h" #if MICROPY_DEBUG_VERBOSE #define DEBUG_PRINT (1) @@ -450,18 +452,24 @@ void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t asm_rv32_opcode_cadd(state, rd, ASM_RV32_REG_SP); } -void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { - mp_int_t scaled_offset = offset * sizeof(ASM_WORD_SIZE); +static const uint8_t RV32_LOAD_OPCODE_TABLE[3] = { + 0x04, 0x05, 0x02 +}; - if (scaled_offset >= 0 && RV32_IS_IN_C_REGISTER_WINDOW(rd) && RV32_IS_IN_C_REGISTER_WINDOW(rs) && FIT_UNSIGNED(scaled_offset, 6)) { +void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, int32_t offset, mp_uint_t operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); + + int32_t scaled_offset = offset << operation_size; + + if (scaled_offset >= 0 && operation_size == 2 && RV32_IS_IN_C_REGISTER_WINDOW(rd) && RV32_IS_IN_C_REGISTER_WINDOW(rs) && MP_FIT_UNSIGNED(6, scaled_offset)) { // c.lw rd', offset(rs') asm_rv32_opcode_clw(state, RV32_MAP_IN_C_REGISTER_WINDOW(rd), RV32_MAP_IN_C_REGISTER_WINDOW(rs), scaled_offset); return; } - if (FIT_SIGNED(scaled_offset, 12)) { - // lw rd, offset(rs) - asm_rv32_opcode_lw(state, rd, rs, scaled_offset); + if (MP_FIT_SIGNED(12, scaled_offset)) { + // lbu|lhu|lw rd, offset(rs) + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, RV32_LOAD_OPCODE_TABLE[operation_size], rd, rs, scaled_offset)); return; } @@ -469,12 +477,12 @@ void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_ mp_uint_t lower = 0; split_immediate(scaled_offset, &upper, &lower); - // lui rd, HI(offset) ; Or c.lui if possible - // c.add rd, rs - // lw rd, LO(offset)(rd) + // lui rd, HI(offset) ; Or c.lui if possible + // c.add rd, rs + // lbu|lhu|lw rd, LO(offset)(rd) load_upper_immediate(state, rd, upper); asm_rv32_opcode_cadd(state, rd, rs); - asm_rv32_opcode_lw(state, rd, rd, lower); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, RV32_LOAD_OPCODE_TABLE[operation_size], rd, rd, lower)); } void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label) { @@ -497,12 +505,20 @@ void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label) { asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, REG_TEMP2, lower); } -void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { - mp_int_t scaled_offset = offset * ASM_WORD_SIZE; +void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, int32_t offset, mp_uint_t operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); - if (FIT_SIGNED(scaled_offset, 12)) { - // sw rd, offset(rs) - asm_rv32_opcode_sw(state, rd, rs, scaled_offset); + int32_t scaled_offset = offset << operation_size; + + if (scaled_offset >= 0 && operation_size == 2 && RV32_IS_IN_C_REGISTER_WINDOW(rd) && RV32_IS_IN_C_REGISTER_WINDOW(rs) && MP_FIT_UNSIGNED(6, scaled_offset)) { + // c.sw rd', offset(rs') + asm_rv32_opcode_csw(state, RV32_MAP_IN_C_REGISTER_WINDOW(rd), RV32_MAP_IN_C_REGISTER_WINDOW(rs), scaled_offset); + return; + } + + if (MP_FIT_SIGNED(12, scaled_offset)) { + // sb|sh|sw rd, offset(rs) + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0x23, operation_size, rs, rd, scaled_offset)); return; } @@ -510,12 +526,12 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint mp_uint_t lower = 0; split_immediate(scaled_offset, &upper, &lower); - // lui temporary, HI(offset) ; Or c.lui if possible - // c.add temporary, rs - // sw rd, LO(offset)(temporary) + // lui temporary, HI(offset) ; Or c.lui if possible + // c.add temporary, rs + // sb|sh|sw rd, LO(offset)(temporary) load_upper_immediate(state, REG_TEMP2, upper); asm_rv32_opcode_cadd(state, REG_TEMP2, rs); - asm_rv32_opcode_sw(state, rd, REG_TEMP2, lower); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0x23, operation_size, REG_TEMP2, rd, lower)); } void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t label) { @@ -530,27 +546,6 @@ void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t labe asm_rv32_opcode_addi(state, rd, rd, lower); } -void asm_rv32_emit_load16_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { - mp_int_t scaled_offset = offset * sizeof(uint16_t); - - if (FIT_SIGNED(scaled_offset, 12)) { - // lhu rd, offset(rs) - asm_rv32_opcode_lhu(state, rd, rs, scaled_offset); - return; - } - - mp_uint_t upper = 0; - mp_uint_t lower = 0; - split_immediate(scaled_offset, &upper, &lower); - - // lui rd, HI(offset) ; Or c.lui if possible - // c.add rd, rs - // lhu rd, LO(offset)(rd) - load_upper_immediate(state, rd, upper); - asm_rv32_opcode_cadd(state, rd, rs); - asm_rv32_opcode_lhu(state, rd, rd, lower); -} - void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { if (rs == rd) { // c.li rd, 0 @@ -562,14 +557,39 @@ 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."); + + if (operation_size > 0 && asm_rv32_allow_zba_opcodes()) { + // sh{1,2}add rs1, rs2, rs1 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 1 << operation_size, 0x10, rs1, rs2, rs1)); + } else { + if (operation_size > 0) { + asm_rv32_opcode_cslli(state, rs2, operation_size); + } + asm_rv32_opcode_cadd(state, rs1, rs2); + } +} + +void asm_rv32_emit_load_reg_reg_reg(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size) { + asm_rv32_fix_up_scaled_reg_reg_reg(state, rs1, rs2, operation_size); + asm_rv32_emit_load_reg_reg_offset(state, rd, rs1, 0, operation_size); +} + +void asm_rv32_emit_store_reg_reg_reg(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size) { + asm_rv32_fix_up_scaled_reg_reg_reg(state, rs1, rs2, operation_size); + asm_rv32_emit_store_reg_reg_offset(state, rd, rs1, 0, operation_size); +} + void asm_rv32_meta_comparison_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd) { - // c.li rd, 1 ; - // beq rs1, rs2, 6 ; PC + 0 - // c.li rd, 0 ; PC + 4 - // ... ; PC + 6 - asm_rv32_opcode_cli(state, rd, 1); - asm_rv32_opcode_beq(state, rs1, rs2, 6); - asm_rv32_opcode_cli(state, rd, 0); + // sub rd, rs1, rs2 + // sltiu rd, rd, 1 + asm_rv32_opcode_sub(state, rd, rs1, rs2); + asm_rv32_opcode_sltiu(state, rd, rd, 1); } void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd) { @@ -580,26 +600,15 @@ void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2 } void asm_rv32_meta_comparison_lt(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison) { - // slt(u) rd, rs1, rs2 - if (unsigned_comparison) { - asm_rv32_opcode_sltu(state, rd, rs1, rs2); - } else { - asm_rv32_opcode_slt(state, rd, rs1, rs2); - } + // slt|sltu rd, rs1, rs2 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, (0x02 | (unsigned_comparison ? 1 : 0)), 0x00, rd, rs1, rs2)); } void asm_rv32_meta_comparison_le(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison) { - // c.li rd, 1 ; - // beq rs1, rs2, 8 ; PC + 0 - // slt(u) rd, rs1, rs2 ; PC + 4 - // ... ; PC + 8 - asm_rv32_opcode_cli(state, rd, 1); - asm_rv32_opcode_beq(state, rs1, rs2, 8); - if (unsigned_comparison) { - asm_rv32_opcode_sltu(state, rd, rs1, rs2); - } else { - asm_rv32_opcode_slt(state, rd, rs1, rs2); - } + // slt[u] rd, rs2, rs1 + // xori rd, rd, 1 + asm_rv32_meta_comparison_lt(state, rs2, rs1, rd, unsigned_comparison); + asm_rv32_opcode_xori(state, rd, rd, 1); } #endif // MICROPY_EMIT_RV32 diff --git a/py/asmrv32.h b/py/asmrv32.h index b09f48eb12f66..6f709daa11bce 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -122,6 +122,18 @@ typedef struct _asm_rv32_t { mp_uint_t locals_stack_offset; } asm_rv32_t; +enum { + RV32_EXT_NONE = 0, + RV32_EXT_ZBA = 1 << 0, + + RV32_EXT_ALL = RV32_EXT_ZBA +}; + +typedef struct _asm_rv32_backend_options_t { + // This is a bitmask holding a combination of RV32_EXT_* entries. + uint8_t allowed_extensions; +} asm_rv32_backend_options_t; + void asm_rv32_entry(asm_rv32_t *state, mp_uint_t locals); void asm_rv32_exit(asm_rv32_t *state); void asm_rv32_end_pass(asm_rv32_t *state); @@ -583,6 +595,24 @@ static inline void asm_rv32_opcode_remu(asm_rv32_t *state, mp_uint_t rd, mp_uint asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x07, 0x01, rd, rs1, rs2)); } +// SH1ADD RD, RS1, RS2 +static inline void asm_rv32_opcode_sh1add(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0010000 ..... ..... 010 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x02, 0x10, rd, rs1, rs2)); +} + +// SH2ADD RD, RS1, RS2 +static inline void asm_rv32_opcode_sh2add(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0010000 ..... ..... 100 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x04, 0x10, rd, rs1, rs2)); +} + +// SH3ADD RD, RS1, RS2 +static inline void asm_rv32_opcode_sh3add(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0010000 ..... ..... 110 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x06, 0x10, rd, rs1, rs2)); +} + // SLL RD, RS1, RS2 static inline void asm_rv32_opcode_sll(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 001 ..... 0110011 @@ -679,6 +709,19 @@ static inline void asm_rv32_opcode_xori(asm_rv32_t *state, mp_uint_t rd, mp_uint asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x04, rd, rs, immediate)); } +#define MICROPY_RV32_EXTENSIONS \ + (MICROPY_EMIT_RV32_ZBA ? RV32_EXT_ZBA : 0) + +static inline uint8_t asm_rv32_allowed_extensions(void) { + uint8_t extensions = MICROPY_RV32_EXTENSIONS; + #if MICROPY_DYNAMIC_COMPILER + if (mp_dynamic_compiler.backend_options != NULL) { + extensions |= ((asm_rv32_backend_options_t *)mp_dynamic_compiler.backend_options)->allowed_extensions; + } + #endif + return extensions; +} + #define ASM_WORD_SIZE (4) #define ASM_HALFWORD_SIZE (2) @@ -702,6 +745,8 @@ void asm_rv32_meta_comparison_lt(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2 void asm_rv32_meta_comparison_le(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison); void asm_rv32_emit_optimised_load_immediate(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate); +void asm_rv32_emit_load_reg_reg_reg(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size); +void asm_rv32_emit_store_reg_reg_reg(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size); #ifdef GENERIC_ASM_API @@ -709,17 +754,16 @@ void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index); void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label); void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t label); void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_t label); -void asm_rv32_emit_load16_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset); -void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset); +void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, int32_t offset, mp_uint_t operation_size); void asm_rv32_emit_mov_local_reg(asm_rv32_t *state, mp_uint_t local, mp_uint_t rs); void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local); void asm_rv32_emit_mov_reg_local(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local); void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t label); void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs); -void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_uint_t base, mp_int_t offset); +void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_uint_t base, int32_t offset, mp_uint_t operation_size); #define ASM_T asm_rv32_t -#define ASM_ENTRY(state, labels) asm_rv32_entry(state, labels) +#define ASM_ENTRY(state, labels, name) asm_rv32_entry(state, labels) #define ASM_EXIT(state) asm_rv32_exit(state) #define ASM_END_PASS(state) asm_rv32_end_pass(state) @@ -732,12 +776,13 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #define ASM_JUMP_IF_REG_NONZERO(state, rs, label, bool_test) asm_rv32_emit_jump_if_reg_nonzero(state, rs, label) #define ASM_JUMP_IF_REG_ZERO(state, rs, label, bool_test) asm_rv32_emit_jump_if_reg_eq(state, rs, ASM_RV32_REG_ZERO, label) #define ASM_JUMP_REG(state, rs) asm_rv32_opcode_cjr(state, rs) -#define ASM_LOAD16_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load16_reg_reg_offset(state, rd, rs, offset) -#define ASM_LOAD16_REG_REG(state, rd, rs) asm_rv32_opcode_lhu(state, rd, rs, 0) -#define ASM_LOAD32_REG_REG(state, rd, rs) ASM_LOAD_REG_REG_OFFSET(state, rd, rs, 0) -#define ASM_LOAD8_REG_REG(state, rd, rs) asm_rv32_opcode_lbu(state, rd, rs, 0) -#define ASM_LOAD_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset) -#define ASM_LOAD_REG_REG(state, rd, rs) ASM_LOAD32_REG_REG(state, rd, rs) +#define ASM_LOAD_REG_REG_OFFSET(state, rd, rs, offset) ASM_LOAD32_REG_REG_OFFSET(state, rd, rs, offset) +#define ASM_LOAD8_REG_REG(state, rd, rs) ASM_LOAD8_REG_REG_OFFSET(state, rd, rs, 0) +#define ASM_LOAD16_REG_REG(state, rd, rs) ASM_LOAD16_REG_REG_OFFSET(state, rd, rs, 0) +#define ASM_LOAD32_REG_REG(state, rd, rs) ASM_LOAD32_REG_REG_OFFSET(state, rd, rs, 0) +#define ASM_LOAD8_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset, 0) +#define ASM_LOAD16_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset, 1) +#define ASM_LOAD32_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset, 2) #define ASM_LSL_REG_REG(state, rd, rs) asm_rv32_opcode_sll(state, rd, rd, rs) #define ASM_LSR_REG_REG(state, rd, rs) asm_rv32_opcode_srl(state, rd, rd, rs) #define ASM_MOV_LOCAL_REG(state, local, rs) asm_rv32_emit_mov_local_reg(state, local, rs) @@ -750,14 +795,22 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #define ASM_NEG_REG(state, rd) asm_rv32_opcode_sub(state, rd, ASM_RV32_REG_ZERO, rd) #define ASM_NOT_REG(state, rd) asm_rv32_opcode_xori(state, rd, rd, -1) #define ASM_OR_REG_REG(state, rd, rs) asm_rv32_opcode_or(state, rd, rd, rs) -#define ASM_STORE16_REG_REG(state, rs1, rs2) asm_rv32_opcode_sh(state, rs1, rs2, 0) -#define ASM_STORE32_REG_REG(state, rs1, rs2) ASM_STORE_REG_REG_OFFSET(state, rs1, rs2, 0) -#define ASM_STORE8_REG_REG(state, rs1, rs2) asm_rv32_opcode_sb(state, rs1, rs2, 0) -#define ASM_STORE_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset) -#define ASM_STORE_REG_REG(state, rs1, rs2) ASM_STORE32_REG_REG(state, rs1, rs2) +#define ASM_STORE_REG_REG_OFFSET(state, rd, rs, offset) ASM_STORE32_REG_REG_OFFSET(state, rd, rs, offset) +#define ASM_STORE8_REG_REG(state, rs1, rs2) ASM_STORE8_REG_REG_OFFSET(state, rs1, rs2, 0) +#define ASM_STORE16_REG_REG(state, rs1, rs2) ASM_STORE16_REG_REG_OFFSET(state, rs1, rs2, 0) +#define ASM_STORE32_REG_REG(state, rs1, rs2) ASM_STORE32_REG_REG_OFFSET(state, rs1, rs2, 0) +#define ASM_STORE8_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset, 0) +#define ASM_STORE16_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset, 1) +#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_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) +#define ASM_STORE8_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_store_reg_reg_reg(state, rd, rs1, rs2, 0) +#define ASM_STORE16_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_store_reg_reg_reg(state, rd, rs1, rs2, 1) +#define ASM_STORE32_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_store_reg_reg_reg(state, rd, rs1, rs2, 2) #endif diff --git a/py/asmthumb.c b/py/asmthumb.c index 420815e80269a..58cc7aea88085 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -37,7 +37,6 @@ #include "py/asmthumb.h" #include "py/misc.h" -#define UNSIGNED_FIT5(x) ((uint32_t)(x) < 32) #define UNSIGNED_FIT7(x) ((uint32_t)(x) < 128) #define UNSIGNED_FIT8(x) (((x) & 0xffffff00) == 0) #define UNSIGNED_FIT16(x) (((x) & 0xffff0000) == 0) @@ -52,12 +51,6 @@ #define OP_SUB_W_RRI_HI(reg_src) (0xf2a0 | (reg_src)) #define OP_SUB_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff)) -#define OP_LDR_W_HI(reg_base) (0xf8d0 | (reg_base)) -#define OP_LDR_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12)) - -#define OP_LDRH_W_HI(reg_base) (0xf8b0 | (reg_base)) -#define OP_LDRH_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12)) - static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) { return mp_asm_base_get_cur_to_write_bytes(&as->base, n); } @@ -274,9 +267,8 @@ bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) { #define OP_BCC_N(cond, byte_offset) (0xd000 | ((cond) << 8) | (((byte_offset) >> 1) & 0x00ff)) -// all these bit-arithmetic operations need coverage testing! -#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 14) & 0x003f)) -#define OP_BCC_W_LO(byte_offset) (0x8000 | ((byte_offset) & 0x2000) | (((byte_offset) >> 1) & 0x0fff)) +#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 12) & 0x003f)) +#define OP_BCC_W_LO(byte_offset) (0x8000 | (((byte_offset) >> 5) & 0x2000) | (((byte_offset) >> 8) & 0x0800) | (((byte_offset) >> 1) & 0x07ff)) bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) { mp_uint_t dest = get_label_dest(as, label); @@ -432,11 +424,6 @@ void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label) { asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes } -// ARMv7-M only -static inline void asm_thumb_ldr_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { - asm_thumb_op32(as, OP_LDR_W_HI(reg_base), OP_LDR_W_LO(reg_dest, word_offset * 4)); -} - // emits code for: reg_dest = reg_base + offset << offset_shift static void asm_thumb_add_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint reg_base, uint offset, uint offset_shift) { if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8) { @@ -450,12 +437,12 @@ static void asm_thumb_add_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint re asm_thumb_lsl_rlo_rlo_i5(as, reg_dest, reg_dest, offset_shift); asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_base); } else if (reg_dest != reg_base) { - asm_thumb_mov_rlo_i16(as, reg_dest, offset << offset_shift); - asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_dest); + asm_thumb_mov_reg_i32_optimised(as, reg_dest, offset << offset_shift); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_base); } else { uint reg_other = reg_dest ^ 7; asm_thumb_op16(as, OP_PUSH_RLIST((1 << reg_other))); - asm_thumb_mov_rlo_i16(as, reg_other, offset << offset_shift); + asm_thumb_mov_reg_i32_optimised(as, reg_other, offset << offset_shift); asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_other); asm_thumb_op16(as, OP_POP_RLIST((1 << reg_other))); } @@ -464,30 +451,50 @@ static void asm_thumb_add_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint re } } -void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { - if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(word_offset)) { - asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_base, word_offset); - } else if (asm_thumb_allow_armv7m(as)) { - asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, word_offset); +#define OP_LDR_STR_W_HI(operation_size, reg) ((0xf880 | (operation_size) << 5) | (reg)) +#define OP_LDR_STR_W_LO(reg, imm12) (((reg) << 12) | (imm12)) + +#define OP_LDR 0x01 +#define OP_STR 0x00 + +#define OP_LDR_W 0x10 +#define OP_STR_W 0x00 + +static const uint8_t OP_LDR_STR_TABLE[3] = { + 0x0E, 0x10, 0x0C +}; + +void asm_thumb_load_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint reg_base, uint offset, uint operation_size) { + assert(operation_size <= 2 && "Operation size out of range."); + + if (MP_FIT_UNSIGNED(5, offset) && (reg_dest < ASM_THUMB_REG_R8) && (reg_base < ASM_THUMB_REG_R8)) { + // Can use T1 encoding + asm_thumb_op16(as, ((OP_LDR_STR_TABLE[operation_size] | OP_LDR) << 11) | (offset << 6) | (reg_base << 3) | reg_dest); + } else if (asm_thumb_allow_armv7m(as) && MP_FIT_UNSIGNED(12, offset << operation_size)) { + // Can use T3 encoding + asm_thumb_op32(as, (OP_LDR_STR_W_HI(operation_size, reg_base) | OP_LDR_W), OP_LDR_STR_W_LO(reg_dest, (offset << operation_size))); } else { - asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, word_offset - 31, 2); - asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_dest, 31); + // Must use the generic sequence + asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, offset - 31, operation_size); + asm_thumb_op16(as, ((OP_LDR_STR_TABLE[operation_size] | OP_LDR) << 11) | (31 << 6) | (reg_dest << 3) | (reg_dest)); } } -// ARMv7-M only -static inline void asm_thumb_ldrh_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset) { - asm_thumb_op32(as, OP_LDRH_W_HI(reg_base), OP_LDRH_W_LO(reg_dest, uint16_offset * 2)); -} +void asm_thumb_store_reg_reg_offset(asm_thumb_t *as, uint reg_src, uint reg_base, uint offset, uint operation_size) { + assert(operation_size <= 2 && "Operation size out of range."); -void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset) { - if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(uint16_offset)) { - asm_thumb_ldrh_rlo_rlo_i5(as, reg_dest, reg_base, uint16_offset); - } else if (asm_thumb_allow_armv7m(as)) { - asm_thumb_ldrh_reg_reg_i12(as, reg_dest, reg_base, uint16_offset); + if (MP_FIT_UNSIGNED(5, offset) && (reg_src < ASM_THUMB_REG_R8) && (reg_base < ASM_THUMB_REG_R8)) { + // Can use T1 encoding + asm_thumb_op16(as, ((OP_LDR_STR_TABLE[operation_size] | OP_STR) << 11) | (offset << 6) | (reg_base << 3) | reg_src); + } else if (asm_thumb_allow_armv7m(as) && MP_FIT_UNSIGNED(12, offset << operation_size)) { + // Can use T3 encoding + asm_thumb_op32(as, (OP_LDR_STR_W_HI(operation_size, reg_base) | OP_STR_W), OP_LDR_STR_W_LO(reg_src, (offset << operation_size))); } else { - asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, uint16_offset - 31, 1); - asm_thumb_ldrh_rlo_rlo_i5(as, reg_dest, reg_dest, 31); + // Must use the generic sequence + asm_thumb_op16(as, OP_PUSH_RLIST(1 << reg_base)); + asm_thumb_add_reg_reg_offset(as, reg_base, reg_base, offset - 31, operation_size); + asm_thumb_op16(as, ((OP_LDR_STR_TABLE[operation_size] | OP_STR) << 11) | (31 << 6) | (reg_base << 3) | reg_src); + asm_thumb_op16(as, OP_POP_RLIST(1 << reg_base)); } } @@ -495,6 +502,7 @@ void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint r #define OP_BW_HI(byte_offset) (0xf000 | (((byte_offset) >> 12) & 0x07ff)) #define OP_BW_LO(byte_offset) (0xb800 | (((byte_offset) >> 1) & 0x07ff)) +// In Thumb1 mode, this may clobber r1. void asm_thumb_b_label(asm_thumb_t *as, uint label) { mp_uint_t dest = get_label_dest(as, label); mp_int_t rel = dest - as->base.code_offset; @@ -514,19 +522,40 @@ void asm_thumb_b_label(asm_thumb_t *as, uint label) { if (asm_thumb_allow_armv7m(as)) { asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel)); } else { + // this code path has to be the same instruction size irrespective of the value of rel + bool need_align = as->base.code_offset & 2u; if (SIGNED_FIT12(rel)) { - // this code path has to be the same number of instructions irrespective of rel asm_thumb_op16(as, OP_B_N(rel)); - } else { asm_thumb_op16(as, ASM_THUMB_OP_NOP); - if (dest != (mp_uint_t)-1) { - // we have an actual branch > 12 bits; this is not handled yet - mp_raise_NotImplementedError(MP_ERROR_TEXT("native method too big")); + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + if (need_align) { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + } + } else { + // do a large jump using: + // (nop) + // ldr r1, [pc, _data] + // add pc, r1 + // _data: .word rel + // + // note: can't use r0 as a temporary because native code can have the return value + // in that register and use a large jump to get to the exit point of the function + + rel -= 2; // account for the "ldr r1, [pc, _data]" + if (need_align) { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + rel -= 2; // account for this nop } + asm_thumb_ldr_rlo_pcrel_i8(as, ASM_THUMB_REG_R1, 0); + asm_thumb_add_reg_reg(as, ASM_THUMB_REG_R15, ASM_THUMB_REG_R1); + asm_thumb_op16(as, rel & 0xffff); + asm_thumb_op16(as, rel >> 16); } } } +// In Thumb1 mode, this may clobber r1. void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { mp_uint_t dest = get_label_dest(as, label); mp_int_t rel = dest - as->base.code_offset; @@ -547,8 +576,15 @@ void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); } else { // reverse the sense of the branch to jump over a longer branch - asm_thumb_op16(as, OP_BCC_N(cond ^ 1, 0)); + size_t code_offset_start = as->base.code_offset; + byte *c = asm_thumb_get_cur_to_write_bytes(as, 2); asm_thumb_b_label(as, label); + size_t bytes_to_skip = as->base.code_offset - code_offset_start; + uint16_t op = OP_BCC_N(cond ^ 1, bytes_to_skip - 4); + if (c != NULL) { + c[0] = op; + c[1] = op >> 8; + } } } @@ -569,7 +605,7 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel) { void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp) { // Load ptr to function from table, indexed by fun_id, then call it - asm_thumb_ldr_reg_reg_i12_optimised(as, reg_temp, ASM_THUMB_REG_FUN_TABLE, fun_id); + asm_thumb_load_reg_reg_offset(as, reg_temp, ASM_THUMB_REG_FUN_TABLE, fun_id, 2); asm_thumb_op16(as, OP_BLX(reg_temp)); } diff --git a/py/asmthumb.h b/py/asmthumb.h index 0584ed3227aad..cb786694f0b78 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -251,6 +251,50 @@ static inline void asm_thumb_bx_reg(asm_thumb_t *as, uint r_src) { asm_thumb_format_5(as, ASM_THUMB_FORMAT_5_BX, 0, r_src); } +// FORMAT 7: load/store with register offset +// FORMAT 8: load/store sign-extended byte/halfword + +#define ASM_THUMB_FORMAT_7_LDR (0x5800) +#define ASM_THUMB_FORMAT_7_STR (0x5000) +#define ASM_THUMB_FORMAT_7_WORD_TRANSFER (0x0000) +#define ASM_THUMB_FORMAT_7_BYTE_TRANSFER (0x0400) +#define ASM_THUMB_FORMAT_8_LDRH (0x5A00) +#define ASM_THUMB_FORMAT_8_STRH (0x5200) + +#define ASM_THUMB_FORMAT_7_8_ENCODE(op, rlo_dest, rlo_base, rlo_index) \ + ((op) | ((rlo_index) << 6) | ((rlo_base) << 3) | ((rlo_dest))) + +static inline void asm_thumb_format_7_8(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_base, uint rlo_index) { + assert(rlo_dest < ASM_THUMB_REG_R8); + assert(rlo_base < ASM_THUMB_REG_R8); + assert(rlo_index < ASM_THUMB_REG_R8); + asm_thumb_op16(as, ASM_THUMB_FORMAT_7_8_ENCODE(op, rlo_dest, rlo_base, rlo_index)); +} + +static inline void asm_thumb_ldrb_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_7_LDR | ASM_THUMB_FORMAT_7_BYTE_TRANSFER, rlo_dest, rlo_base, rlo_index); +} + +static inline void asm_thumb_ldrh_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_8_LDRH, rlo_dest, rlo_base, rlo_index); +} + +static inline void asm_thumb_ldr_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_7_LDR | ASM_THUMB_FORMAT_7_WORD_TRANSFER, rlo_dest, rlo_base, rlo_index); +} + +static inline void asm_thumb_strb_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_7_STR | ASM_THUMB_FORMAT_7_BYTE_TRANSFER, rlo_src, rlo_base, rlo_index); +} + +static inline void asm_thumb_strh_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_8_STRH, rlo_dest, rlo_base, rlo_index); +} + +static inline void asm_thumb_str_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_7_STR | ASM_THUMB_FORMAT_7_WORD_TRANSFER, rlo_src, rlo_base, rlo_index); +} + // FORMAT 9: load/store with immediate offset // For word transfers the offset must be aligned, and >>2 @@ -273,24 +317,6 @@ static inline void asm_thumb_format_9_10(asm_thumb_t *as, uint op, uint rlo_dest asm_thumb_op16(as, ASM_THUMB_FORMAT_9_10_ENCODE(op, rlo_dest, rlo_base, offset)); } -static inline void asm_thumb_str_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint word_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_src, rlo_base, word_offset); -} -static inline void asm_thumb_strb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint byte_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_src, rlo_base, byte_offset); -} -static inline void asm_thumb_strh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint uint16_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_STRH, rlo_src, rlo_base, uint16_offset); -} -static inline void asm_thumb_ldr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint word_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_dest, rlo_base, word_offset); -} -static inline void asm_thumb_ldrb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_dest, rlo_base, byte_offset); -} -static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint uint16_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, uint16_offset); -} static inline void asm_thumb_lsl_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) { asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_LSL, rlo_dest, rlo_src, shift); } @@ -338,8 +364,10 @@ void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num); // void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label); -void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset); // convenience -void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset); // convenience +// Generate optimised load dest, [src, #offset] sequence +void asm_thumb_load_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint reg_base, uint offset, uint operation_size); +// Generate optimised store src, [dest, #offset] sequence +void asm_thumb_store_reg_reg_offset(asm_thumb_t *as, uint reg_src, uint reg_base, uint offset, uint operation_size); void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narrow or wide branch void asm_thumb_bcc_label(asm_thumb_t *as, int cc, uint label); // convenience: picks narrow or wide branch @@ -376,12 +404,12 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel); #define REG_FUN_TABLE ASM_THUMB_REG_FUN_TABLE -#define ASM_T asm_thumb_t -#define ASM_END_PASS asm_thumb_end_pass -#define ASM_ENTRY asm_thumb_entry -#define ASM_EXIT asm_thumb_exit +#define ASM_T asm_thumb_t +#define ASM_END_PASS asm_thumb_end_pass +#define ASM_ENTRY(as, num_locals, name) asm_thumb_entry((as), (num_locals)) +#define ASM_EXIT asm_thumb_exit -#define ASM_JUMP asm_thumb_b_label +#define ASM_JUMP asm_thumb_b_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ do { \ asm_thumb_cmp_rlo_i8(as, reg, 0); \ @@ -419,18 +447,44 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_thumb_sub_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_MUL, (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_ldr_reg_reg_i12_optimised((as), (reg_dest), (reg_base), (word_offset)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrb_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrh_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_thumb_ldrh_reg_reg_i12_optimised((as), (reg_dest), (reg_base), (uint16_offset)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_thumb_strb_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_thumb_strh_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_thumb_load_reg_reg_offset((as), (reg_dest), (reg_base), (byte_offset), 0) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, halfword_offset) asm_thumb_load_reg_reg_offset((as), (reg_dest), (reg_base), (halfword_offset), 1) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_load_reg_reg_offset((as), (reg_dest), (reg_base), (word_offset), 2) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_src, reg_base, byte_offset) asm_thumb_store_reg_reg_offset((as), (reg_src), (reg_base), (byte_offset), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_src, reg_base, halfword_offset) asm_thumb_store_reg_reg_offset((as), (reg_src), (reg_base), (halfword_offset), 1) +#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, word_offset) asm_thumb_store_reg_reg_offset((as), (reg_src), (reg_base), (word_offset), 2) + +#define ASM_LOAD8_REG_REG_REG(as, reg_dest, reg_base, reg_index) asm_thumb_ldrb_rlo_rlo_rlo((as), (reg_dest), (reg_base), (reg_index)) +#define ASM_LOAD16_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_thumb_lsl_rlo_rlo_i5((as), (reg_index), (reg_index), 1); \ + asm_thumb_ldrh_rlo_rlo_rlo((as), (reg_dest), (reg_base), (reg_index)); \ + } while (0) +#define ASM_LOAD32_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_thumb_lsl_rlo_rlo_i5((as), (reg_index), (reg_index), 2); \ + asm_thumb_ldr_rlo_rlo_rlo((as), (reg_dest), (reg_base), (reg_index)); \ + } while (0) +#define ASM_STORE8_REG_REG_REG(as, reg_val, reg_base, reg_index) asm_thumb_strb_rlo_rlo_rlo((as), (reg_val), (reg_base), (reg_index)) +#define ASM_STORE16_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_thumb_lsl_rlo_rlo_i5((as), (reg_index), (reg_index), 1); \ + asm_thumb_strh_rlo_rlo_rlo((as), (reg_val), (reg_base), (reg_index)); \ + } while (0) +#define ASM_STORE32_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_thumb_lsl_rlo_rlo_i5((as), (reg_index), (reg_index), 2); \ + asm_thumb_str_rlo_rlo_rlo((as), (reg_val), (reg_base), (reg_index)); \ + } while (0) #endif // GENERIC_ASM_API diff --git a/py/asmx64.h b/py/asmx64.h index 03070b5f63d3b..1e8cb0c905f2b 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -155,12 +155,12 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); // Holds a pointer to mp_fun_table #define REG_FUN_TABLE ASM_X64_REG_FUN_TABLE -#define ASM_T asm_x64_t -#define ASM_END_PASS asm_x64_end_pass -#define ASM_ENTRY asm_x64_entry -#define ASM_EXIT asm_x64_exit +#define ASM_T asm_x64_t +#define ASM_END_PASS asm_x64_end_pass +#define ASM_ENTRY(as, num_locals, name) asm_x64_entry((as), (num_locals)) +#define ASM_EXIT asm_x64_exit -#define ASM_JUMP asm_x64_jmp_label +#define ASM_JUMP asm_x64_jmp_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ do { \ if (bool_test) { \ @@ -206,18 +206,21 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x64_sub_r64_r64((as), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x64_mul_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem64_to_r64((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (word_offset), (reg_dest)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem8_to_r64zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 2 * (uint16_offset), (reg_dest)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 0, (reg_dest)) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, qword_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (qword_offset), (reg_dest)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_x64_mov_mem8_to_r64zx((as), (reg_base), (byte_offset), (reg_dest)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 2 * (word_offset), (reg_dest)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, dword_offset) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 4 * (dword_offset), (reg_dest)) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, qword_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (qword_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_src, reg_base, byte_offset) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), (byte_offset)) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 2 * (word_offset)) +#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)) #endif // GENERIC_ASM_API diff --git a/py/asmx86.h b/py/asmx86.h index 7796d69762617..f5d37228a2f04 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -150,12 +150,12 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r // Holds a pointer to mp_fun_table #define REG_FUN_TABLE ASM_X86_REG_FUN_TABLE -#define ASM_T asm_x86_t -#define ASM_END_PASS asm_x86_end_pass -#define ASM_ENTRY asm_x86_entry -#define ASM_EXIT asm_x86_exit +#define ASM_T asm_x86_t +#define ASM_END_PASS asm_x86_end_pass +#define ASM_ENTRY(as, num_locals, name) asm_x86_entry((as), (num_locals)) +#define ASM_EXIT asm_x86_exit -#define ASM_JUMP asm_x86_jmp_label +#define ASM_JUMP asm_x86_jmp_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ do { \ if (bool_test) { \ @@ -201,18 +201,21 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x86_sub_r32_r32((as), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x86_mul_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (word_offset), (reg_dest)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem8_to_r32zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 2 * (uint16_offset), (reg_dest)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, dword_offset) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (dword_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_x86_mov_mem8_to_r32zx((as), (reg_base), (byte_offset), (reg_dest)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 2 * (word_offset), (reg_dest)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, dword_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (dword_offset), (reg_dest)) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, dword_offset) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), (dword_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_src, reg_base, byte_offset) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), (byte_offset)) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 2 * (word_offset)) +#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)) #endif // GENERIC_ASM_API diff --git a/py/asmxtensa.c b/py/asmxtensa.c index 0fbe351dcf3c7..bc3e717d9f3a0 100644 --- a/py/asmxtensa.c +++ b/py/asmxtensa.c @@ -34,9 +34,20 @@ #include "py/asmxtensa.h" +#if N_XTENSAWIN +#define REG_TEMP ASM_XTENSA_REG_TEMPORARY_WIN +#else +#define REG_TEMP ASM_XTENSA_REG_TEMPORARY +#endif + #define WORD_SIZE (4) +#define SIGNED_FIT6(x) ((((x) & 0xffffffe0) == 0) || (((x) & 0xffffffe0) == 0xffffffe0)) #define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)) #define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)) +#define SIGNED_FIT18(x) ((((x) & 0xfffe0000) == 0) || (((x) & 0xfffe0000) == 0xfffe0000)) + +#define ET_OUT_OF_RANGE MP_ERROR_TEXT("ERROR: xtensa %q out of range") +#define ET_NOT_ALIGNED MP_ERROR_TEXT("ERROR: %q %q not word-aligned") void asm_xtensa_end_pass(asm_xtensa_t *as) { as->num_const = as->cur_const; @@ -47,9 +58,9 @@ void asm_xtensa_end_pass(asm_xtensa_t *as) { if (as->base.pass == MP_ASM_PASS_EMIT) { uint8_t *d = as->base.code_base; printf("XTENSA ASM:"); - for (int i = 0; i < ((as->base.code_size + 15) & ~15); ++i) { + for (size_t i = 0; i < ((as->base.code_size + 15) & ~15); ++i) { if (i % 16 == 0) { - printf("\n%08x:", (uint32_t)&d[i]); + printf("\n%p:", &d[i]); } if (i % 2 == 0) { printf(" "); @@ -62,10 +73,12 @@ void asm_xtensa_end_pass(asm_xtensa_t *as) { } void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) { - // jump over the constants - asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4); - mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte - as->const_table = (uint32_t *)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); + if (as->num_const > 0) { + // jump over the constants + asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4); + mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte + as->const_table = (uint32_t *)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); + } // adjust the stack-pointer to store a0, a12, a13, a14, a15 and locals, 16-byte aligned as->stack_adjust = (((ASM_XTENSA_NUM_REGS_SAVED + num_locals) * WORD_SIZE) + 15) & ~15; @@ -146,22 +159,60 @@ void asm_xtensa_j_label(asm_xtensa_t *as, uint label) { asm_xtensa_op_j(as, rel); } +static bool calculate_branch_displacement(asm_xtensa_t *as, uint label, ptrdiff_t *displacement) { + assert(displacement != NULL && "Displacement pointer is NULL"); + + uint32_t label_offset = get_label_dest(as, label); + *displacement = (ptrdiff_t)(label_offset - as->base.code_offset - 4); + return (label_offset != (uint32_t)-1) && (*displacement < 0); +} + void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label) { - uint32_t dest = get_label_dest(as, label); - int32_t rel = dest - as->base.code_offset - 4; - if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT12(rel)) { - printf("ERROR: xtensa bccz out of range\n"); + ptrdiff_t rel = 0; + bool can_emit_short_jump = calculate_branch_displacement(as, label, &rel); + + if (can_emit_short_jump && SIGNED_FIT12(rel)) { + // Backwards BCCZ opcodes with an offset that fits in 12 bits can + // be emitted without any change. + asm_xtensa_op_bccz(as, cond, reg, rel); + return; } - asm_xtensa_op_bccz(as, cond, reg, rel); + + // Range is effectively extended to 18 bits, as a more complex jump code + // sequence is emitted. + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT18(rel - 6)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bccz); + } + + // ~BCCZ skip ; +0 <- Condition is flipped here (EQ -> NE, etc.) + // J addr ; +3 + // skip: ; +6 + asm_xtensa_op_bccz(as, cond ^ 1, reg, 6 - 4); + asm_xtensa_op_j(as, rel - 3); } void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label) { - uint32_t dest = get_label_dest(as, label); - int32_t rel = dest - as->base.code_offset - 4; - if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT8(rel)) { - printf("ERROR: xtensa bcc out of range\n"); + ptrdiff_t rel = 0; + bool can_emit_short_jump = calculate_branch_displacement(as, label, &rel); + + if (can_emit_short_jump && SIGNED_FIT8(rel)) { + // Backwards BCC opcodes with an offset that fits in 8 bits can + // be emitted without any change. + asm_xtensa_op_bcc(as, cond, reg1, reg2, rel); + return; + } + + // Range is effectively extended to 18 bits, as a more complex jump code + // sequence is emitted. + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT18(rel - 6)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bcc); } - asm_xtensa_op_bcc(as, cond, reg1, reg2, rel); + + // ~BCC skip ; +0 <- Condition is flipped here (EQ -> NE, etc.) + // J addr ; +3 + // skip: ; +6 + asm_xtensa_op_bcc(as, cond ^ 8, reg1, reg2, 6 - 4); + asm_xtensa_op_j(as, rel - 3); } // convenience function; reg_dest must be different from reg_src[12] @@ -179,6 +230,8 @@ size_t asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32) { // store the constant in the table if (as->const_table != NULL) { as->const_table[as->cur_const] = i32; + } else { + assert((as->base.pass != MP_ASM_PASS_EMIT) && "Constants table was not built."); } ++as->cur_const; return loc; @@ -240,17 +293,53 @@ void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, u } else if (word_offset < 256) { asm_xtensa_op_l32i(as, reg_dest, reg_base, word_offset); } else { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow")); + asm_xtensa_mov_reg_i32_optimised(as, reg_dest, word_offset * 4); + asm_xtensa_op_add_n(as, reg_dest, reg_base, reg_dest); + asm_xtensa_op_l32i_n(as, reg_dest, reg_dest, 0); } } -void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) { - if (word_offset < 16) { - asm_xtensa_op_s32i_n(as, reg_src, reg_base, word_offset); - } else if (word_offset < 256) { - asm_xtensa_op_s32i(as, reg_src, reg_base, word_offset); +void asm_xtensa_load_reg_reg_offset(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint offset, uint operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); + + if (operation_size == 2 && MP_FIT_UNSIGNED(4, offset)) { + asm_xtensa_op_l32i_n(as, reg_dest, reg_base, offset); + return; + } + + if (MP_FIT_UNSIGNED(8, offset)) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, operation_size, reg_base, reg_dest, offset)); + return; + } + + asm_xtensa_mov_reg_i32_optimised(as, reg_dest, offset << operation_size); + asm_xtensa_op_add_n(as, reg_dest, reg_base, reg_dest); + if (operation_size == 2) { + asm_xtensa_op_l32i_n(as, reg_dest, reg_dest, 0); } else { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow")); + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, operation_size, reg_dest, reg_dest, 0)); + } +} + +void asm_xtensa_store_reg_reg_offset(asm_xtensa_t *as, uint reg_src, uint reg_base, uint offset, uint operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); + + if (operation_size == 2 && MP_FIT_UNSIGNED(4, offset)) { + asm_xtensa_op_s32i_n(as, reg_src, reg_base, offset); + return; + } + + if (MP_FIT_UNSIGNED(8, offset)) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 0x04 | operation_size, reg_base, reg_src, offset)); + return; + } + + asm_xtensa_mov_reg_i32_optimised(as, REG_TEMP, offset << operation_size); + asm_xtensa_op_add_n(as, REG_TEMP, reg_base, REG_TEMP); + if (operation_size == 2) { + asm_xtensa_op_s32i_n(as, reg_src, REG_TEMP, 0); + } else { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 0x04 | operation_size, REG_TEMP, reg_src, 0)); } } @@ -264,4 +353,47 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx) { asm_xtensa_op_callx8(as, ASM_XTENSA_REG_A8); } +void asm_xtensa_bit_branch(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t bit, mp_uint_t label, mp_uint_t condition) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 4; + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT8(rel)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bit_branch); + } + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(7, condition | ((bit >> 4) & 0x01), reg, bit & 0x0F, rel & 0xFF)); +} + +void asm_xtensa_call0(asm_xtensa_t *as, mp_uint_t label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 3; + if (as->base.pass == MP_ASM_PASS_EMIT) { + if ((dest & 0x03) != 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_NOT_ALIGNED, MP_QSTR_call0, MP_QSTR_target); + } + if ((rel & 0x03) != 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_NOT_ALIGNED, MP_QSTR_call0, MP_QSTR_location); + } + if (!SIGNED_FIT18(rel)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_call0); + } + } + asm_xtensa_op_call0(as, rel); +} + +void asm_xtensa_l32r(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset; + if (as->base.pass == MP_ASM_PASS_EMIT) { + if ((dest & 0x03) != 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_NOT_ALIGNED, MP_QSTR_l32r, MP_QSTR_target); + } + if ((rel & 0x03) != 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_NOT_ALIGNED, MP_QSTR_l32r, MP_QSTR_location); + } + if (!SIGNED_FIT18(rel) || (rel >= 0)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_l32r); + } + } + asm_xtensa_op_l32r(as, reg, as->base.code_offset, dest); +} + #endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN diff --git a/py/asmxtensa.h b/py/asmxtensa.h index a8c39206bd008..559b3cacd5d45 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -64,9 +64,11 @@ #define ASM_XTENSA_REG_A14 (14) #define ASM_XTENSA_REG_A15 (15) -// for bccz +// for bccz and bcci #define ASM_XTENSA_CCZ_EQ (0) #define ASM_XTENSA_CCZ_NE (1) +#define ASM_XTENSA_CCZ_LT (2) +#define ASM_XTENSA_CCZ_GE (3) // for bcc and setcc #define ASM_XTENSA_CC_NONE (0) @@ -291,17 +293,25 @@ void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src); void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num); void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num); void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label); -void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset); -void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset); +void asm_xtensa_load_reg_reg_offset(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint offset, uint operation_size); +void asm_xtensa_store_reg_reg_offset(asm_xtensa_t *as, uint reg_src, uint reg_base, uint offset, uint operation_size); void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx); void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); +void asm_xtensa_bit_branch(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t bit, mp_uint_t label, mp_uint_t condition); +void asm_xtensa_immediate_branch(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t immediate, mp_uint_t label, mp_uint_t cond); +void asm_xtensa_call0(asm_xtensa_t *as, mp_uint_t label); +void asm_xtensa_l32r(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t label); // Holds a pointer to mp_fun_table #define ASM_XTENSA_REG_FUN_TABLE ASM_XTENSA_REG_A15 #define ASM_XTENSA_REG_FUN_TABLE_WIN ASM_XTENSA_REG_A7 -// CIRCUITPY-CHANGE: prevent #if warning -#if defined(GENERIC_ASM_API) && GENERIC_ASM_API +// Internal temporary register (currently aliased to REG_ARG_5 for xtensa, +// and to REG_TEMP2 for xtensawin). +#define ASM_XTENSA_REG_TEMPORARY ASM_XTENSA_REG_A6 +#define ASM_XTENSA_REG_TEMPORARY_WIN ASM_XTENSA_REG_A12 + +#if GENERIC_ASM_API // The following macros provide a (mostly) arch-independent API to // generate native code, and are used by the native emitter. @@ -330,9 +340,9 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED #define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE -#define ASM_ENTRY(as, nlocal) asm_xtensa_entry((as), (nlocal)) -#define ASM_EXIT(as) asm_xtensa_exit((as)) -#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx)) +#define ASM_ENTRY(as, nlocal, name) asm_xtensa_entry((as), (nlocal)) +#define ASM_EXIT(as) asm_xtensa_exit((as)) +#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx)) #else // Configuration for windowed calls with window size 8 @@ -360,9 +370,9 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED_WIN #define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE_WIN -#define ASM_ENTRY(as, nlocal) asm_xtensa_entry_win((as), (nlocal)) -#define ASM_EXIT(as) asm_xtensa_exit_win((as)) -#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind_win((as), (idx)) +#define ASM_ENTRY(as, nlocal, name) asm_xtensa_entry_win((as), (nlocal)) +#define ASM_EXIT(as) asm_xtensa_exit_win((as)) +#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind_win((as), (idx)) #endif @@ -408,16 +418,51 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_sub((as), (reg_dest), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mull((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_l32i_optimised((as), (reg_dest), (reg_base), (word_offset)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), (uint16_offset)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_xtensa_load_reg_reg_offset((as), (reg_dest), (reg_base), (byte_offset), 0) +#define ASM_LOAD8_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_xtensa_op_add_n((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0); \ + } while (0) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, halfword_offset) asm_xtensa_load_reg_reg_offset((as), (reg_dest), (reg_base), (halfword_offset), 1) +#define ASM_LOAD16_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_xtensa_op_addx2((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0); \ + } while (0) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_load_reg_reg_offset((as), (reg_dest), (reg_base), (word_offset), 2) +#define ASM_LOAD32_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_xtensa_op_addx4((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0); \ + } while (0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_s32i_optimised((as), (reg_dest), (reg_base), (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s8i((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s16i((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s32i_n((as), (reg_src), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) ASM_STORE32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_src, reg_base, byte_offset) asm_xtensa_store_reg_reg_offset((as), (reg_src), (reg_base), (byte_offset), 0) +#define ASM_STORE8_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_xtensa_op_add_n((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_s8i((as), (reg_val), (reg_base), 0); \ + } while (0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_src, reg_base, halfword_offset) asm_xtensa_store_reg_reg_offset((as), (reg_src), (reg_base), (halfword_offset), 1) +#define ASM_STORE16_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_xtensa_op_addx2((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_s16i((as), (reg_val), (reg_base), 0); \ + } while (0) +#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_dest, reg_base, word_offset) asm_xtensa_store_reg_reg_offset((as), (reg_dest), (reg_base), (word_offset), 2) +#define ASM_STORE32_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_xtensa_op_addx4((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_s32i_n((as), (reg_val), (reg_base), 0); \ + } while (0) #endif // GENERIC_ASM_API diff --git a/py/bc.c b/py/bc.c index c2956030e38e1..4caa022e3d8b4 100644 --- a/py/bc.c +++ b/py/bc.c @@ -88,7 +88,7 @@ const byte *mp_decode_uint_skip(const byte *ptr) { return ptr; } -static NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { +static MP_NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE // generic message, used also for other argument issues (void)f; diff --git a/py/bc.h b/py/bc.h index 7658f66414f17..c299560ef2b79 100644 --- a/py/bc.h +++ b/py/bc.h @@ -226,6 +226,7 @@ typedef struct _mp_compiled_module_t { bool has_native; size_t n_qstr; size_t n_obj; + size_t arch_flags; #endif } mp_compiled_module_t; @@ -316,25 +317,35 @@ static inline void mp_module_context_alloc_tables(mp_module_context_t *context, #endif } +typedef struct _mp_code_lineinfo_t { + size_t bc_increment; + size_t line_increment; +} mp_code_lineinfo_t; + +static inline mp_code_lineinfo_t mp_bytecode_decode_lineinfo(const byte **line_info) { + mp_code_lineinfo_t result; + size_t c = (*line_info)[0]; + if ((c & 0x80) == 0) { + // 0b0LLBBBBB encoding + result.bc_increment = c & 0x1f; + result.line_increment = c >> 5; + *line_info += 1; + } else { + // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + result.bc_increment = c & 0xf; + result.line_increment = ((c << 4) & 0x700) | (*line_info)[1]; + *line_info += 2; + } + return result; +} + static inline size_t mp_bytecode_get_source_line(const byte *line_info, const byte *line_info_top, size_t bc_offset) { size_t source_line = 1; while (line_info < line_info_top) { - size_t c = *line_info; - size_t b, l; - if ((c & 0x80) == 0) { - // 0b0LLBBBBB encoding - b = c & 0x1f; - l = c >> 5; - line_info += 1; - } else { - // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) - b = c & 0xf; - l = ((c << 4) & 0x700) | line_info[1]; - line_info += 2; - } - if (bc_offset >= b) { - bc_offset -= b; - source_line += l; + mp_code_lineinfo_t decoded = mp_bytecode_decode_lineinfo(&line_info); + if (bc_offset >= decoded.bc_increment) { + bc_offset -= decoded.bc_increment; + source_line += decoded.line_increment; } else { // found source line corresponding to bytecode offset break; diff --git a/py/binary.c b/py/binary.c index 728f29815cd78..59353b918f913 100644 --- a/py/binary.c +++ b/py/binary.c @@ -71,8 +71,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { case 'Q': size = 8; break; - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': case 'O': case 'S': @@ -128,8 +127,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { align = alignof(long long); size = sizeof(long long); break; - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': case 'O': case 'S': @@ -208,7 +206,7 @@ static float mp_decode_half_float(uint16_t hf) { ++e; } - fpu.i = ((hf & 0x8000) << 16) | (e << 23) | (m << 13); + fpu.i = ((hf & 0x8000u) << 16) | (e << 23) | (m << 13); return fpu.f; } @@ -289,20 +287,20 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) { #if MICROPY_PY_BUILTINS_FLOAT case 'f': return mp_obj_new_float_from_f(((float *)p)[index]); + // CIRCUITPY-CHANGE: #if MICROPY_PY_DOUBLE_TYPECODE case 'd': return mp_obj_new_float_from_d(((double *)p)[index]); #endif #endif - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES - // Extension to CPython: array of objects + // Extension to CPython: array of objects + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'O': return ((mp_obj_t *)p)[index]; // Extension to CPython: array of pointers case 'P': return mp_obj_new_int((mp_int_t)(uintptr_t)((void **)p)[index]); - #endif + #endif } return MP_OBJ_NEW_SMALL_INT(val); } @@ -352,14 +350,11 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * long long val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p); - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - if (MICROPY_NONSTANDARD_TYPECODES && (val_type == 'O')) { + if (MICROPY_PY_STRUCT_UNSAFE_TYPECODES && val_type == 'O') { return (mp_obj_t)(mp_uint_t)val; - #if MICROPY_NONSTANDARD_TYPECODES - } else if (val_type == 'S') { + } else if (MICROPY_PY_STRUCT_UNSAFE_TYPECODES && val_type == 'S') { const char *s_val = (const char *)(uintptr_t)(mp_uint_t)val; return mp_obj_new_str_from_cstr(s_val); - #endif #if MICROPY_PY_BUILTINS_FLOAT } else if (val_type == 'e') { return mp_obj_new_float_from_f(mp_decode_half_float(val)); @@ -369,6 +364,7 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * float f; } fpu = {val}; return mp_obj_new_float_from_f(fpu.f); + // CIRCUITPY-CHANGE #if MICROPY_PY_DOUBLE_TYPECODE } else if (val_type == 'd') { union { @@ -430,8 +426,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p mp_uint_t val; switch (val_type) { - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'O': val = (mp_uint_t)val_in; break; @@ -449,6 +444,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p val = fp_sp.i; break; } + // CIRCUITPY-CHANGE #if MICROPY_PY_DOUBLE_TYPECODE case 'd': { union { @@ -513,8 +509,7 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_ break; #endif #endif - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES // Extension to CPython: array of objects case 'O': ((mp_obj_t *)p)[index] = val_in; @@ -582,18 +577,18 @@ void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_i case 'f': ((float *)p)[index] = (float)val; break; + // CIRCUITPY-CHANGE #if MICROPY_PY_DOUBLE_TYPECODE case 'd': ((double *)p)[index] = (double)val; break; #endif #endif - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES - // Extension to CPython: array of pointers + // Extension to CPython: array of pointers + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': ((void **)p)[index] = (void *)(uintptr_t)val; break; - #endif + #endif } } diff --git a/py/builtin.h b/py/builtin.h index 6efe3e8facabd..388bc8470053d 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -138,6 +138,7 @@ extern const mp_obj_module_t mp_module_sys; extern const mp_obj_module_t mp_module_errno; extern const mp_obj_module_t mp_module_uctypes; extern const mp_obj_module_t mp_module_machine; +extern const mp_obj_module_t mp_module_math; extern const char MICROPY_PY_BUILTINS_HELP_TEXT[]; diff --git a/py/builtinhelp.c b/py/builtinhelp.c index d041d6915a63a..9ab2a12d21455 100644 --- a/py/builtinhelp.c +++ b/py/builtinhelp.c @@ -162,9 +162,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); } } } diff --git a/py/builtinimport.c b/py/builtinimport.c index e4e06d8e2cc8b..8fcac22ccb491 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -118,7 +118,7 @@ static mp_import_stat_t stat_module(vstr_t *path) { // path (i.e. "/mod_name(.py)"). static mp_import_stat_t stat_top_level(qstr mod_name, vstr_t *dest) { DEBUG_printf("stat_top_level: '%s'\n", qstr_str(mod_name)); - #if MICROPY_PY_SYS + #if MICROPY_PY_SYS && MICROPY_PY_SYS_PATH size_t path_num; mp_obj_t *path_items; mp_obj_get_array(mp_sys_path, &path_num, &path_items); @@ -155,7 +155,7 @@ static mp_import_stat_t stat_top_level(qstr mod_name, vstr_t *dest) { #if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER static void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ qstr source_name = lex->source_name; mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #endif @@ -168,7 +168,7 @@ static void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { #if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY static void do_execute_proto_fun(const mp_module_context_t *context, mp_proto_fun_t proto_fun, qstr source_name) { - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #else (void)source_name; @@ -227,7 +227,7 @@ static void do_load(mp_module_context_t *module_obj, vstr_t *file) { if (frozen_type == MP_FROZEN_MPY) { const mp_frozen_module_t *frozen = modref; module_obj->constants = frozen->constants; - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ qstr frozen_file_qstr = qstr_from_str(file_str + frozen_path_prefix_len); #else qstr frozen_file_qstr = MP_QSTRnull; @@ -270,7 +270,7 @@ static void do_load(mp_module_context_t *module_obj, vstr_t *file) { // Convert a relative (to the current module) import, going up "level" levels, // into an absolute import. -static void evaluate_relative_import(mp_int_t level, const char **module_name, size_t *module_name_len) { +static void evaluate_relative_import(mp_int_t level, const char **module_name, size_t *module_name_len, mp_obj_t globals) { // What we want to do here is to take the name of the current module, // remove trailing components, and concatenate the passed-in // module name. @@ -279,7 +279,7 @@ static void evaluate_relative_import(mp_int_t level, const char **module_name, s // module's position in the package hierarchy." // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name - mp_obj_t current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); + mp_obj_t current_module_name_obj = mp_obj_dict_get(globals, MP_OBJ_NEW_QSTR(MP_QSTR___name__)); assert(current_module_name_obj != MP_OBJ_NULL); #if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT && MICROPY_CPYTHON_COMPAT @@ -287,12 +287,12 @@ static void evaluate_relative_import(mp_int_t level, const char **module_name, s // This is a module loaded by -m command-line switch (e.g. unix port), // and so its __name__ has been set to "__main__". Get its real name // that we stored during import in the __main__ attribute. - current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); + current_module_name_obj = mp_obj_dict_get(globals, MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } #endif // If we have a __path__ in the globals dict, then we're a package. - bool is_pkg = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); + bool is_pkg = mp_map_lookup(mp_obj_dict_get_map(globals), MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); #if DEBUG_PRINT DEBUG_printf("Current module/package: "); @@ -370,7 +370,7 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, // Immediately return if the module at this level is already loaded. mp_map_elem_t *elem; - #if MICROPY_PY_SYS + #if MICROPY_PY_SYS && MICROPY_PY_SYS_PATH // If sys.path is empty, the intention is to force using a built-in. This // means we should also ignore any loaded modules with the same name // which may have come from the filesystem. @@ -406,6 +406,7 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, // all the locations in sys.path. stat = stat_top_level(level_mod_name, &path); + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES // If filesystem failed, now try and see if it matches an extensible // built-in module. if (stat == MP_IMPORT_STAT_NO_EXIST) { @@ -414,6 +415,7 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, return module_obj; } } + #endif } else { DEBUG_printf("Searching for sub-module\n"); @@ -577,10 +579,19 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { const char *module_name = mp_obj_str_get_data(module_name_obj, &module_name_len); if (level != 0) { + // This is the dict with all global symbols. + mp_obj_t globals = MP_OBJ_FROM_PTR(mp_globals_get()); + if (n_args >= 2 && args[1] != mp_const_none) { + globals = args[1]; + if (!mp_obj_is_type(globals, &mp_type_dict)) { + mp_raise_TypeError(NULL); + } + } + // Turn "foo.bar" with level=3 into ".foo.bar". // Current module name is extracted from globals().__name__. - evaluate_relative_import(level, &module_name, &module_name_len); // module_name is now an absolute module path. + evaluate_relative_import(level, &module_name, &module_name_len, globals); } if (module_name_len == 0) { @@ -656,11 +667,13 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { if (module_obj != MP_OBJ_NULL) { return module_obj; } + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES // Now try as an extensible built-in (e.g. `time`). module_obj = mp_module_get_builtin(module_name_qstr, true); if (module_obj != MP_OBJ_NULL) { return module_obj; } + #endif // Couldn't find the module, so fail #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index f6310dfaf6855..fc74144dc807e 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -88,7 +88,6 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_MEM_STATS (0) #define MICROPY_MODULE_BUILTIN_INIT (1) #define MICROPY_MODULE_BUILTIN_SUBPACKAGES (1) -#define MICROPY_NONSTANDARD_TYPECODES (0) #define MICROPY_OPT_COMPUTED_GOTO (1) #define MICROPY_OPT_COMPUTED_GOTO_SAVE_SPACE (CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE) #define MICROPY_OPT_LOAD_ATTR_FAST_PATH (CIRCUITPY_OPT_LOAD_ATTR_FAST_PATH) @@ -141,6 +140,7 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_PY_RE (CIRCUITPY_RE) // Supplanted by shared-bindings/struct #define MICROPY_PY_STRUCT (0) +#define MICROPY_PY_STRUCT_UNSAFE_TYPECODES (0) #define MICROPY_PY_SYS (CIRCUITPY_SYS) #define MICROPY_PY_SYS_MAXSIZE (1) #define MICROPY_PY_SYS_STDFILES (1) diff --git a/py/compile.c b/py/compile.c index c7dd2fd476136..c8704131234f1 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, }; @@ -3295,7 +3296,9 @@ static void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind } // check structure of parse node - assert(MP_PARSE_NODE_IS_STRUCT(pns2->nodes[0])); + if (!MP_PARSE_NODE_IS_STRUCT(pns2->nodes[0])) { + goto not_an_instruction; + } if (!MP_PARSE_NODE_IS_NULL(pns2->nodes[1])) { goto not_an_instruction; } @@ -3587,6 +3590,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; @@ -3619,6 +3629,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 diff --git a/py/cstack.h b/py/cstack.h index b12a18e13fcad..bcd919d31f932 100644 --- a/py/cstack.h +++ b/py/cstack.h @@ -35,7 +35,7 @@ void mp_cstack_init_with_sp_here(size_t stack_size); -inline static void mp_cstack_init_with_top(void *top, size_t stack_size) { +static inline void mp_cstack_init_with_top(void *top, size_t stack_size) { MP_STATE_THREAD(stack_top) = (char *)top; #if MICROPY_STACK_CHECK @@ -54,7 +54,7 @@ void mp_cstack_check(void); #else -inline static void mp_cstack_check(void) { +static inline void mp_cstack_check(void) { // No-op when stack checking is disabled } diff --git a/py/dynruntime.h b/py/dynruntime.h index e87cf6591c4b6..b41a31f48f7a5 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -70,7 +70,7 @@ #define m_realloc(ptr, new_num_bytes) (m_realloc_dyn((ptr), (new_num_bytes))) #define m_realloc_maybe(ptr, new_num_bytes, allow_move) (m_realloc_maybe_dyn((ptr), (new_num_bytes), (allow_move))) -static NORETURN inline void m_malloc_fail_dyn(size_t num_bytes) { +static MP_NORETURN inline void m_malloc_fail_dyn(size_t num_bytes) { mp_fun_table.raise_msg( mp_fun_table.load_global(MP_QSTR_MemoryError), "memory allocation failed"); @@ -301,14 +301,14 @@ static inline mp_obj_t mp_obj_new_exception_arg1_dyn(const mp_obj_type_t *exc_ty return mp_call_function_n_kw(MP_OBJ_FROM_PTR(exc_type), 1, 0, &args[0]); } -static NORETURN inline void mp_raise_dyn(mp_obj_t o) { +static MP_NORETURN inline void mp_raise_dyn(mp_obj_t o) { mp_fun_table.raise(o); for (;;) { } } // CIRCUITPY-CHANGE: new routine -static NORETURN inline void mp_raise_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { +static MP_NORETURN inline void mp_raise_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { mp_fun_table.raise(mp_obj_new_exception_arg1_dyn(exc_type, arg)); for (;;) { } diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 807befb464a84..030728cfc9adf 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -63,14 +63,14 @@ else ifeq ($(ARCH),armv6m) # thumb CROSS = arm-none-eabi- CFLAGS_ARCH += -mthumb -mcpu=cortex-m0 -MICROPY_FLOAT_IMPL ?= none +MICROPY_FLOAT_IMPL ?= float else ifeq ($(ARCH),armv7m) # thumb CROSS = arm-none-eabi- CFLAGS_ARCH += -mthumb -mcpu=cortex-m3 -MICROPY_FLOAT_IMPL ?= none +MICROPY_FLOAT_IMPL ?= float else ifeq ($(ARCH),armv7emsp) @@ -124,6 +124,10 @@ else $(error architecture '$(ARCH)' not supported) endif +ifneq ($(findstring -musl,$(shell $(CROSS)gcc -dumpmachine)),) +USE_MUSL := 1 +endif + MICROPY_FLOAT_IMPL_UPPER = $(shell echo $(MICROPY_FLOAT_IMPL) | tr '[:lower:]' '[:upper:]') CFLAGS += $(CFLAGS_ARCH) -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_$(MICROPY_FLOAT_IMPL_UPPER) @@ -147,6 +151,8 @@ ifeq ($(LINK_RUNTIME),1) # distribution. ifeq ($(USE_PICOLIBC),1) LIBM_NAME := libc.a +else ifeq ($(USE_MUSL),1) +LIBM_NAME := libc.a else LIBM_NAME := libm.a endif @@ -166,6 +172,9 @@ endif endif MPY_LD_FLAGS += $(addprefix -l, $(LIBGCC_PATH) $(LIBM_PATH)) endif +ifneq ($(MPY_EXTERN_SYM_FILE),) +MPY_LD_FLAGS += --externs "$(realpath $(MPY_EXTERN_SYM_FILE))" +endif CFLAGS += $(CFLAGS_EXTRA) diff --git a/py/emitcommon.c b/py/emitcommon.c index a9eb6e2021fc8..1f701db80a0a9 100644 --- a/py/emitcommon.c +++ b/py/emitcommon.c @@ -25,6 +25,7 @@ */ #include +#include #include "py/emit.h" #include "py/nativeglue.h" @@ -72,7 +73,21 @@ static bool strictly_equal(mp_obj_t a, mp_obj_t b) { } return true; } else { - return mp_obj_equal(a, b); + if (!mp_obj_equal(a, b)) { + return false; + } + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_COMP_CONST_FLOAT + if (a_type == &mp_type_float) { + mp_float_t a_val = mp_obj_float_get(a); + if (a_val == (mp_float_t)0.0) { + // Although 0.0 == -0.0, they are not strictly_equal and + // must be stored as two different constants in .mpy files + mp_float_t b_val = mp_obj_float_get(b); + return signbit(a_val) == signbit(b_val); + } + } + #endif + return true; } } diff --git a/py/emitglue.c b/py/emitglue.c index 8cc14bdc54eb5..c6ad3e3bc3845 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" @@ -133,6 +134,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; diff --git a/py/emitinlinerv32.c b/py/emitinlinerv32.c index a539242b84d95..e81b152087de3 100644 --- a/py/emitinlinerv32.c +++ b/py/emitinlinerv32.c @@ -196,7 +196,6 @@ typedef enum { CALL_R, // Opcode Register CALL_RL, // Opcode Register, Label CALL_N, // Opcode - CALL_I, // Opcode Immediate CALL_RII, // Opcode Register, Register, Immediate CALL_RIR, // Opcode Register, Immediate(Register) CALL_COUNT @@ -210,7 +209,6 @@ typedef enum { #define U (1 << 2) // Unsigned immediate #define Z (1 << 3) // Non-zero -typedef void (*call_l_t)(asm_rv32_t *state, mp_uint_t label_index); typedef void (*call_ri_t)(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate); typedef void (*call_rri_t)(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_int_t immediate); typedef void (*call_rii_t)(asm_rv32_t *state, mp_uint_t rd, mp_uint_t immediate1, mp_int_t immediate2); @@ -225,7 +223,7 @@ typedef struct _opcode_t { uint16_t argument1_mask : 4; uint16_t argument2_mask : 4; uint16_t argument3_mask : 4; - uint16_t arguments_count : 2; + uint16_t parse_nodes : 2; // 2 bits available here uint32_t calling_convention : 4; uint32_t argument1_kind : 4; @@ -234,7 +232,8 @@ typedef struct _opcode_t { uint32_t argument2_shift : 4; uint32_t argument3_kind : 4; uint32_t argument3_shift : 4; - // 4 bits available here + uint32_t required_extensions : 1; + // 3 bits available here void *emitter; } opcode_t; @@ -297,88 +296,91 @@ static const uint32_t OPCODE_MASKS[] = { }; static const opcode_t OPCODES[] = { - { MP_QSTR_add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_add }, - { MP_QSTR_addi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_addi }, - { MP_QSTR_and_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_and }, - { MP_QSTR_andi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_andi }, - { MP_QSTR_auipc, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, asm_rv32_opcode_auipc }, - { MP_QSTR_beq, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_beq }, - { MP_QSTR_bge, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bge }, - { MP_QSTR_bgeu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bgeu }, - { MP_QSTR_blt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_blt }, - { MP_QSTR_bltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bltu }, - { MP_QSTR_bne, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bne }, - { MP_QSTR_csrrc, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrc }, - { MP_QSTR_csrrs, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrs }, - { MP_QSTR_csrrw, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrw }, - { MP_QSTR_csrrci, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrci }, - { MP_QSTR_csrrsi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrsi }, - { MP_QSTR_csrrwi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrwi }, - { MP_QSTR_c_add, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cadd }, - { MP_QSTR_c_addi, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, IZ, 0, N, 0, asm_rv32_opcode_caddi }, - { MP_QSTR_c_addi4spn, MASK_0000FF00, MASK_000003FC, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 0, N, 0, asm_rv32_opcode_caddi4spn }, - { MP_QSTR_c_and, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cand }, - { MP_QSTR_c_andi, MASK_0000FF00, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, RC, 0, I, 0, N, 0, asm_rv32_opcode_candi }, - { MP_QSTR_c_beqz, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, asm_rv32_opcode_cbeqz }, - { MP_QSTR_c_bnez, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, asm_rv32_opcode_cbnez }, - { MP_QSTR_c_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_cebreak }, - { MP_QSTR_c_j, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, asm_rv32_opcode_cj }, - { MP_QSTR_c_jal, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, asm_rv32_opcode_cjal }, - { MP_QSTR_c_jalr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, asm_rv32_opcode_cjalr }, - { MP_QSTR_c_jr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, asm_rv32_opcode_cjr }, - { MP_QSTR_c_li, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_cli }, - { MP_QSTR_c_lui, MASK_FFFFFFFA, MASK_0001F800, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 12, N, 0, asm_rv32_opcode_clui }, - { MP_QSTR_c_lw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 3, CALL_RIR, RC, 0, I, 0, RC, 0, asm_rv32_opcode_clw }, - { MP_QSTR_c_lwsp, MASK_FFFFFFFE, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_clwsp }, - { MP_QSTR_c_mv, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cmv }, - { MP_QSTR_c_nop, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_cnop }, - { MP_QSTR_c_or, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cor }, - { MP_QSTR_c_slli, MASK_FFFFFFFE, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, R, 0, IU, 0, N, 0, asm_rv32_opcode_cslli }, - { MP_QSTR_c_srai, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, asm_rv32_opcode_csrai }, - { MP_QSTR_c_srli, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, asm_rv32_opcode_csrli }, - { MP_QSTR_c_sub, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_csub }, - { MP_QSTR_c_sw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 3, CALL_RIR, RC, 0, I, 0, RC, 0, asm_rv32_opcode_csw }, - { MP_QSTR_c_swsp, MASK_FFFFFFFF, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_cswsp }, - { MP_QSTR_c_xor, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cxor }, - { MP_QSTR_div, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_div }, - { MP_QSTR_divu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_divu }, - { MP_QSTR_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_ebreak }, - { MP_QSTR_ecall, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_ecall }, - { MP_QSTR_jal, MASK_FFFFFFFF, MASK_001FFFFE, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, asm_rv32_opcode_jal }, - { MP_QSTR_jalr, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_jalr }, - { MP_QSTR_la, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, opcode_la }, - { MP_QSTR_lb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lb }, - { MP_QSTR_lbu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lbu }, - { MP_QSTR_lh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lh }, - { MP_QSTR_lhu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lhu }, - { MP_QSTR_li, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, opcode_li }, - { MP_QSTR_lui, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, asm_rv32_opcode_lui }, - { MP_QSTR_lw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lw }, - { MP_QSTR_mv, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cmv }, - { MP_QSTR_mul, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mul }, - { MP_QSTR_mulh, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulh }, - { MP_QSTR_mulhsu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulhsu }, - { MP_QSTR_mulhu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulhu }, - { MP_QSTR_or_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_or }, - { MP_QSTR_ori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_ori }, - { MP_QSTR_rem, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_rem }, - { MP_QSTR_remu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_remu }, - { MP_QSTR_sb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sb }, - { MP_QSTR_sh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sh }, - { MP_QSTR_sll, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sll }, - { MP_QSTR_slli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_slli }, - { MP_QSTR_slt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_slt }, - { MP_QSTR_slti, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_slti }, - { MP_QSTR_sltiu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_sltiu }, - { MP_QSTR_sltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sltu }, - { MP_QSTR_sra, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sra }, - { MP_QSTR_srai, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_srai }, - { MP_QSTR_srl, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_srl }, - { MP_QSTR_srli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_srli }, - { MP_QSTR_sub, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sub }, - { MP_QSTR_sw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sw }, - { MP_QSTR_xor, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_xor }, - { MP_QSTR_xori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_xori }, + { MP_QSTR_add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_add }, + { MP_QSTR_addi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_addi }, + { MP_QSTR_and_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_and }, + { MP_QSTR_andi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_andi }, + { MP_QSTR_auipc, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, RV32_EXT_NONE, asm_rv32_opcode_auipc }, + { MP_QSTR_beq, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_beq }, + { MP_QSTR_bge, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_bge }, + { MP_QSTR_bgeu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_bgeu }, + { MP_QSTR_blt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_blt }, + { MP_QSTR_bltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_bltu }, + { MP_QSTR_bne, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_bne }, + { MP_QSTR_csrrc, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrc }, + { MP_QSTR_csrrs, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrs }, + { MP_QSTR_csrrw, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrw }, + { MP_QSTR_csrrci, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrci }, + { MP_QSTR_csrrsi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrsi }, + { MP_QSTR_csrrwi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrwi }, + { MP_QSTR_c_add, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cadd }, + { MP_QSTR_c_addi, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, IZ, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_caddi }, + { MP_QSTR_c_addi4spn, MASK_0000FF00, MASK_000003FC, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_caddi4spn }, + { MP_QSTR_c_and, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cand }, + { MP_QSTR_c_andi, MASK_0000FF00, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, RC, 0, I, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_candi }, + { MP_QSTR_c_beqz, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cbeqz }, + { MP_QSTR_c_bnez, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cbnez }, + { MP_QSTR_c_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cebreak }, + { MP_QSTR_c_j, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cj }, + { MP_QSTR_c_jal, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cjal }, + { MP_QSTR_c_jalr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cjalr }, + { MP_QSTR_c_jr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cjr }, + { MP_QSTR_c_li, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cli }, + { MP_QSTR_c_lui, MASK_FFFFFFFA, MASK_0001F800, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 12, N, 0, RV32_EXT_NONE, asm_rv32_opcode_clui }, + { MP_QSTR_c_lw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 2, CALL_RIR, RC, 0, I, 0, RC, 0, RV32_EXT_NONE, asm_rv32_opcode_clw }, + { MP_QSTR_c_lwsp, MASK_FFFFFFFE, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_clwsp }, + { MP_QSTR_c_mv, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cmv }, + { MP_QSTR_c_nop, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cnop }, + { MP_QSTR_c_or, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cor }, + { MP_QSTR_c_slli, MASK_FFFFFFFE, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, R, 0, IU, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cslli }, + { MP_QSTR_c_srai, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_csrai }, + { MP_QSTR_c_srli, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_csrli }, + { MP_QSTR_c_sub, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_csub }, + { MP_QSTR_c_sw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 2, CALL_RIR, RC, 0, I, 0, RC, 0, RV32_EXT_NONE, asm_rv32_opcode_csw }, + { MP_QSTR_c_swsp, MASK_FFFFFFFF, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cswsp }, + { MP_QSTR_c_xor, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cxor }, + { MP_QSTR_div, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_div }, + { MP_QSTR_divu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_divu }, + { MP_QSTR_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_ebreak }, + { MP_QSTR_ecall, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_ecall }, + { MP_QSTR_jal, MASK_FFFFFFFF, MASK_001FFFFE, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_jal }, + { MP_QSTR_jalr, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_jalr }, + { MP_QSTR_la, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, RV32_EXT_NONE, opcode_la }, + { MP_QSTR_lb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lb }, + { MP_QSTR_lbu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lbu }, + { MP_QSTR_lh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lh }, + { MP_QSTR_lhu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lhu }, + { MP_QSTR_li, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, RV32_EXT_NONE, opcode_li }, + { MP_QSTR_lui, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, RV32_EXT_NONE, asm_rv32_opcode_lui }, + { MP_QSTR_lw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lw }, + { MP_QSTR_mv, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cmv }, + { MP_QSTR_mul, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_mul }, + { MP_QSTR_mulh, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_mulh }, + { MP_QSTR_mulhsu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_mulhsu }, + { MP_QSTR_mulhu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_mulhu }, + { MP_QSTR_or_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_or }, + { MP_QSTR_ori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_ori }, + { MP_QSTR_rem, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_rem }, + { MP_QSTR_remu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_remu }, + { MP_QSTR_sb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sb }, + { MP_QSTR_sh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sh }, + { MP_QSTR_sh1add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_ZBA, asm_rv32_opcode_sh1add }, + { MP_QSTR_sh2add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_ZBA, asm_rv32_opcode_sh2add }, + { MP_QSTR_sh3add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_ZBA, asm_rv32_opcode_sh3add }, + { MP_QSTR_sll, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sll }, + { MP_QSTR_slli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_slli }, + { MP_QSTR_slt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_slt }, + { MP_QSTR_slti, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_slti }, + { MP_QSTR_sltiu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_sltiu }, + { MP_QSTR_sltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sltu }, + { MP_QSTR_sra, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sra }, + { MP_QSTR_srai, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_srai }, + { MP_QSTR_srl, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_srl }, + { MP_QSTR_srli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_srli }, + { MP_QSTR_sub, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sub }, + { MP_QSTR_sw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sw }, + { MP_QSTR_xor, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_xor }, + { MP_QSTR_xori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_xori }, }; #undef RC @@ -388,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))); @@ -435,9 +437,9 @@ static bool validate_integer(mp_uint_t value, mp_uint_t mask, mp_uint_t flags) { #define ET_WRONG_ARGUMENTS_COUNT MP_ERROR_TEXT("opcode '%q': expecting %d arguments") #define ET_OUT_OF_RANGE MP_ERROR_TEXT("opcode '%q' argument %d: out of range") -static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, - const opcode_t *opcode, mp_parse_node_t node, mp_uint_t node_index) { +static bool serialise_argument(emit_inline_asm_t *emit, const opcode_t *opcode, mp_parse_node_t node, mp_uint_t node_index, mp_uint_t *serialised) { assert((node_index < 3) && "Invalid argument node number."); + assert(serialised && "Serialised value pointer is NULL."); uint32_t kind = 0; uint32_t shift = 0; @@ -466,17 +468,19 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, break; } + mp_uint_t serialised_value = 0; + switch (kind & 0x03) { case N: assert(mask == OPCODE_MASKS[MASK_NOT_USED] && "Invalid mask index for missing operand."); - return true; + break; case R: { mp_uint_t register_index; if (!parse_register_node(node, ®ister_index, false)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_register)); + ET_WRONG_ARGUMENT_KIND, opcode->qstring, node_index + 1, MP_QSTR_register)); return false; } @@ -484,11 +488,11 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("opcode '%q' argument %d: unknown register"), - opcode_qstr, node_index + 1)); + opcode->qstring, node_index + 1)); return false; } - return true; + serialised_value = (kind & C) ? RV32_MAP_IN_C_REGISTER_WINDOW(register_index) : register_index; } break; @@ -497,7 +501,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, if (!mp_parse_node_get_int_maybe(node, &object)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_integer)); + ET_WRONG_ARGUMENT_KIND, opcode->qstring, node_index + 1, MP_QSTR_integer)); return false; } @@ -516,7 +520,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, goto zero_immediate; } - return true; + serialised_value = immediate; } break; @@ -524,7 +528,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, if (!MP_PARSE_NODE_IS_ID(node)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_label)); + ET_WRONG_ARGUMENT_KIND, opcode->qstring, node_index + 1, MP_QSTR_label)); return false; } @@ -534,7 +538,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("opcode '%q' argument %d: undefined label '%q'"), - opcode_qstr, node_index + 1, qstring)); + opcode->qstring, node_index + 1, qstring)); return false; } @@ -548,43 +552,45 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, goto out_of_range; } } - return true; + + serialised_value = displacement; } break; default: assert(!"Unknown argument kind"); + MP_UNREACHABLE; break; } - return false; + *serialised = serialised_value; + return true; out_of_range: emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode_qstr, node_index + 1)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode->qstring, node_index + 1)); return false; zero_immediate: emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("opcode '%q' argument %d: must not be zero"), - opcode_qstr, node_index + 1)); + opcode->qstring, node_index + 1)); return false; } -static bool parse_register_offset_node(emit_inline_asm_t *emit, qstr opcode_qstr, const opcode_t *opcode_data, mp_parse_node_t node, mp_uint_t node_index, mp_parse_node_t *register_node, mp_parse_node_t *offset_node, bool *negative) { - assert(register_node != NULL && "Register node pointer is NULL."); - assert(offset_node != NULL && "Offset node pointer is NULL."); - assert(negative != NULL && "Negative pointer is NULL."); +static bool serialise_register_offset_node(emit_inline_asm_t *emit, const opcode_t *opcode_data, mp_parse_node_t node, mp_uint_t node_index, mp_uint_t *offset, mp_uint_t *base) { + assert(offset && "Attempting to store the offset value into NULL."); + assert(base && "Attempting to store the base register into NULL."); if (!MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_atom_expr_normal) && !MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_factor_2)) { goto invalid_structure; } mp_parse_node_struct_t *node_struct = (mp_parse_node_struct_t *)node; - *negative = false; + bool negative = false; if (MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_factor_2)) { if (MP_PARSE_NODE_IS_TOKEN_KIND(node_struct->nodes[0], MP_TOKEN_OP_MINUS)) { - *negative = true; + negative = true; } else { if (!MP_PARSE_NODE_IS_TOKEN_KIND(node_struct->nodes[0], MP_TOKEN_OP_PLUS)) { goto invalid_structure; @@ -596,186 +602,97 @@ static bool parse_register_offset_node(emit_inline_asm_t *emit, qstr opcode_qstr node_struct = (mp_parse_node_struct_t *)node_struct->nodes[1]; } - if (*negative) { + if (negative) { // If the value is negative, RULE_atom_expr_normal's first token will be the // offset stripped of its negative marker; range check will then fail if the // default method is used, so a custom check is used instead. mp_obj_t object; if (!mp_parse_node_get_int_maybe(node_struct->nodes[0], &object)) { emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_WRONG_ARGUMENT_KIND, opcode_qstr, 2, MP_QSTR_integer)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_WRONG_ARGUMENT_KIND, opcode_data->qstring, 2, MP_QSTR_integer)); return false; } mp_uint_t value = mp_obj_get_int_truncated(object); value = (~value + 1) & (mp_uint_t)-1; if (!validate_integer(value << opcode_data->argument2_shift, OPCODE_MASKS[opcode_data->argument2_mask], opcode_data->argument2_kind)) { emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode_qstr, 2)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode_data->qstring, 2)); return false; } + *offset = value; } else { - if (!validate_argument(emit, opcode_qstr, opcode_data, node_struct->nodes[0], 1)) { + if (!serialise_argument(emit, opcode_data, node_struct->nodes[0], 1, offset)) { return false; } } - *offset_node = node_struct->nodes[0]; node_struct = (mp_parse_node_struct_t *)node_struct->nodes[1]; - if (!validate_argument(emit, opcode_qstr, opcode_data, node_struct->nodes[0], 2)) { + if (!serialise_argument(emit, opcode_data, node_struct->nodes[0], 2, base)) { return false; } - *register_node = node_struct->nodes[0]; return true; invalid_structure: emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_offset)); + ET_WRONG_ARGUMENT_KIND, opcode_data->qstring, node_index + 1, MP_QSTR_offset)); return false; } -static void handle_opcode(emit_inline_asm_t *emit, qstr opcode, const opcode_t *opcode_data, mp_parse_node_t *arguments) { - mp_uint_t rd = 0; - mp_uint_t rs1 = 0; - mp_uint_t rs2 = 0; - +static void handle_opcode(emit_inline_asm_t *emit, const opcode_t *opcode_data, mp_uint_t *arguments) { switch (opcode_data->calling_convention) { - case CALL_RRR: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); - parse_register_node(arguments[2], &rs2, opcode_data->argument3_kind & C); - ((call_rrr_t)opcode_data->emitter)(&emit->as, rd, rs1, rs2); + case CALL_RRR: + ((call_rrr_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1], arguments[2]); break; - } - case CALL_RR: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); - ((call_rr_t)opcode_data->emitter)(&emit->as, rd, rs1); + case CALL_RR: + ((call_rr_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1]); break; - } - case CALL_RRI: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); - mp_obj_t object; - mp_parse_node_get_int_maybe(arguments[2], &object); - mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument3_shift; - ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, immediate); + case CALL_RRI: + ((call_rri_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1], arguments[2]); break; - } - case CALL_RI: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - mp_obj_t object; - mp_parse_node_get_int_maybe(arguments[1], &object); - mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; - ((call_ri_t)opcode_data->emitter)(&emit->as, rd, immediate); + case CALL_RI: + ((call_ri_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1]); break; - } - case CALL_R: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - ((call_r_t)opcode_data->emitter)(&emit->as, rd); + case CALL_R: + ((call_r_t)opcode_data->emitter)(&emit->as, arguments[0]); break; - } - case CALL_RRL: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); - qstr qstring; - mp_uint_t label_index = lookup_label(emit, arguments[2], &qstring); - ptrdiff_t displacement = label_code_offset(emit, label_index); - ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, displacement); + case CALL_RRL: + ((call_rri_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1], (ptrdiff_t)arguments[2]); break; - } - case CALL_RL: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - qstr qstring; - mp_uint_t label_index = lookup_label(emit, arguments[1], &qstring); - ptrdiff_t displacement = label_code_offset(emit, label_index); - ((call_ri_t)opcode_data->emitter)(&emit->as, rd, displacement); + case CALL_RL: + ((call_ri_t)opcode_data->emitter)(&emit->as, arguments[0], (ptrdiff_t)arguments[1]); break; - } - case CALL_L: { - qstr qstring; - mp_uint_t label_index = lookup_label(emit, arguments[0], &qstring); - ptrdiff_t displacement = label_code_offset(emit, label_index); - ((call_i_t)opcode_data->emitter)(&emit->as, displacement); + case CALL_L: + ((call_i_t)opcode_data->emitter)(&emit->as, (ptrdiff_t)arguments[0]); break; - } case CALL_N: ((call_n_t)opcode_data->emitter)(&emit->as); break; - case CALL_I: { - mp_obj_t object; - mp_parse_node_get_int_maybe(arguments[0], &object); - mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument1_shift; - ((call_i_t)opcode_data->emitter)(&emit->as, immediate); + case CALL_RII: + ((call_rii_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1], arguments[2]); break; - } - - case CALL_RII: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - mp_obj_t object; - mp_parse_node_get_int_maybe(arguments[1], &object); - mp_uint_t immediate1 = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; - mp_parse_node_get_int_maybe(arguments[2], &object); - mp_uint_t immediate2 = mp_obj_get_int_truncated(object) << opcode_data->argument3_shift; - ((call_rii_t)opcode_data->emitter)(&emit->as, rd, immediate1, immediate2); - break; - } case CALL_RIR: - assert(!"Should not get here."); + // The last two arguments indices are swapped on purpose. + ((call_rri_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[2], arguments[1]); break; default: assert(!"Unhandled call convention."); + MP_UNREACHABLE; break; } } -static bool handle_load_store_opcode_with_offset(emit_inline_asm_t *emit, qstr opcode, const opcode_t *opcode_data, mp_parse_node_t *argument_nodes) { - mp_parse_node_t nodes[3] = {0}; - if (!validate_argument(emit, opcode, opcode_data, argument_nodes[0], 0)) { - return false; - } - nodes[0] = argument_nodes[0]; - bool negative = false; - if (!parse_register_offset_node(emit, opcode, opcode_data, argument_nodes[1], 1, &nodes[1], &nodes[2], &negative)) { - return false; - } - - mp_uint_t rd = 0; - mp_uint_t rs1 = 0; - if (!parse_register_node(nodes[0], &rd, opcode_data->argument1_kind & C)) { - return false; - } - if (!parse_register_node(nodes[1], &rs1, opcode_data->argument3_kind & C)) { - return false; - } - - mp_obj_t object; - mp_parse_node_get_int_maybe(nodes[2], &object); - mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; - if (negative) { - immediate = (~immediate + 1) & (mp_uint_t)-1; - } - if (!is_in_signed_mask(OPCODE_MASKS[opcode_data->argument2_mask], immediate)) { - emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode, 2)); - return false; - } - - ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, immediate); - return true; -} - static void emit_inline_rv32_opcode(emit_inline_asm_t *emit, qstr opcode, mp_uint_t arguments_count, mp_parse_node_t *argument_nodes) { const opcode_t *opcode_data = NULL; for (mp_uint_t index = 0; index < MP_ARRAY_SIZE(OPCODES); index++) { @@ -785,10 +702,9 @@ static void emit_inline_rv32_opcode(emit_inline_asm_t *emit, qstr opcode, mp_uin } } - if (!opcode_data) { - emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("unknown RV32 instruction '%q'"), opcode)); + if (!opcode_data || (asm_rv32_allowed_extensions() & opcode_data->required_extensions) != opcode_data->required_extensions) { + emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("invalid RV32 instruction '%q'"), opcode)); return; } @@ -796,36 +712,36 @@ static void emit_inline_rv32_opcode(emit_inline_asm_t *emit, qstr opcode, mp_uin assert((opcode_data->argument2_mask < MP_ARRAY_SIZE(OPCODE_MASKS)) && "Argument #2 opcode mask index out of bounds."); assert((opcode_data->argument3_mask < MP_ARRAY_SIZE(OPCODE_MASKS)) && "Argument #3 opcode mask index out of bounds."); assert((opcode_data->calling_convention < CALL_COUNT) && "Calling convention index out of bounds."); - if (opcode_data->calling_convention != CALL_RIR) { - if (opcode_data->arguments_count != arguments_count) { - emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENTS_COUNT, opcode, opcode_data->arguments_count)); - return; - } - if (opcode_data->arguments_count >= 1 && !validate_argument(emit, opcode, opcode_data, argument_nodes[0], 0)) { + mp_uint_t serialised_arguments[3] = { 0 }; + if (arguments_count != opcode_data->parse_nodes) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + ET_WRONG_ARGUMENTS_COUNT, opcode, opcode_data->parse_nodes)); + return; + } + + if (opcode_data->parse_nodes >= 1 && !serialise_argument(emit, opcode_data, argument_nodes[0], 0, &serialised_arguments[0])) { + return; + } + if (opcode_data->calling_convention == CALL_RIR) { + // "register, offset(base)" calls require some preprocessing to + // split the offset and base nodes - not to mention that if the offset + // is negative, the parser won't see the offset as a single node but as + // a sequence of the minus sign token followed by the number itself. + + if (!serialise_register_offset_node(emit, opcode_data, argument_nodes[1], 1, &serialised_arguments[1], &serialised_arguments[2])) { return; } - if (opcode_data->arguments_count >= 2 && !validate_argument(emit, opcode, opcode_data, argument_nodes[1], 1)) { + } else { + if (opcode_data->parse_nodes >= 2 && !serialise_argument(emit, opcode_data, argument_nodes[1], 1, &serialised_arguments[1])) { return; } - if (opcode_data->arguments_count >= 3 && !validate_argument(emit, opcode, opcode_data, argument_nodes[2], 2)) { + if (opcode_data->parse_nodes >= 3 && !serialise_argument(emit, opcode_data, argument_nodes[2], 2, &serialised_arguments[2])) { return; } - handle_opcode(emit, opcode, opcode_data, argument_nodes); - return; - } - - assert((opcode_data->argument2_kind & U) == 0 && "Offset must not be unsigned."); - assert((opcode_data->argument2_kind & Z) == 0 && "Offset can be zero."); - - if (arguments_count != 2) { - emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_WRONG_ARGUMENTS_COUNT, opcode, 2)); - return; } - handle_load_store_opcode_with_offset(emit, opcode, opcode_data, argument_nodes); + handle_opcode(emit, opcode_data, serialised_arguments); } #undef N diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c index fed259cfc6b20..d0eb3d566fce3 100644 --- a/py/emitinlinextensa.c +++ b/py/emitinlinextensa.c @@ -173,7 +173,7 @@ static int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_ #define RRI8_B (2) typedef struct _opcode_table_3arg_t { - uint16_t name; // actually a qstr, which should fit in 16 bits + qstr_short_t name; uint8_t type; uint8_t a0 : 4; uint8_t a1 : 4; @@ -187,6 +187,13 @@ static const opcode_table_3arg_t opcode_table_3arg[] = { {MP_QSTR_add, RRR, 0, 8}, {MP_QSTR_sub, RRR, 0, 12}, {MP_QSTR_mull, RRR, 2, 8}, + {MP_QSTR_addx2, RRR, 0, 9}, + {MP_QSTR_addx4, RRR, 0, 10}, + {MP_QSTR_addx8, RRR, 0, 11}, + {MP_QSTR_subx2, RRR, 0, 13}, + {MP_QSTR_subx4, RRR, 0, 14}, + {MP_QSTR_subx8, RRR, 0, 15}, + {MP_QSTR_src, RRR, 1, 8}, // load/store/addi opcodes: reg, reg, imm // upper nibble of type encodes the range of the immediate arg @@ -208,21 +215,62 @@ static const opcode_table_3arg_t opcode_table_3arg[] = { {MP_QSTR_bge, RRI8_B, ASM_XTENSA_CC_GE, 0}, {MP_QSTR_bgeu, RRI8_B, ASM_XTENSA_CC_GEU, 0}, {MP_QSTR_blt, RRI8_B, ASM_XTENSA_CC_LT, 0}, + {MP_QSTR_bltu, RRI8_B, ASM_XTENSA_CC_LTU, 0}, {MP_QSTR_bnall, RRI8_B, ASM_XTENSA_CC_NALL, 0}, {MP_QSTR_bne, RRI8_B, ASM_XTENSA_CC_NE, 0}, {MP_QSTR_bnone, RRI8_B, ASM_XTENSA_CC_NONE, 0}, }; +// The index of the first four qstrs matches the CCZ condition value to be +// embedded into the opcode. +static const qstr_short_t BCCZ_OPCODES[] = { + MP_QSTR_beqz, MP_QSTR_bnez, MP_QSTR_bltz, MP_QSTR_bgez, + MP_QSTR_beqz_n, MP_QSTR_bnez_n +}; + +#if MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES +typedef struct _single_opcode_t { + qstr_short_t name; + uint16_t value; +} single_opcode_t; + +static const single_opcode_t NOARGS_OPCODES[] = { + {MP_QSTR_dsync, 0x2030}, + {MP_QSTR_esync, 0x2020}, + {MP_QSTR_extw, 0x20D0}, + {MP_QSTR_ill, 0x0000}, + {MP_QSTR_isync, 0x2000}, + {MP_QSTR_memw, 0x20C0}, + {MP_QSTR_rsync, 0x2010}, +}; +#endif + static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) { size_t op_len; const char *op_str = (const char *)qstr_data(op, &op_len); if (n_args == 0) { - if (op == MP_QSTR_ret_n) { + if (op == MP_QSTR_ret_n || op == MP_QSTR_ret) { asm_xtensa_op_ret_n(&emit->as); - } else { - goto unknown_op; + return; + } else if (op == MP_QSTR_nop) { + asm_xtensa_op24(&emit->as, 0x20F0); + return; + } else if (op == MP_QSTR_nop_n) { + asm_xtensa_op16(&emit->as, 0xF03D); + return; } + #if MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES + for (size_t index = 0; index < MP_ARRAY_SIZE(NOARGS_OPCODES); index++) { + const single_opcode_t *opcode = &NOARGS_OPCODES[index]; + if (op == opcode->name) { + asm_xtensa_op24(&emit->as, opcode->value); + return; + } + } + #endif + + goto unknown_op; } else if (n_args == 1) { if (op == MP_QSTR_callx0) { @@ -234,19 +282,45 @@ static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ } else if (op == MP_QSTR_jx) { uint r0 = get_arg_reg(emit, op_str, pn_args[0]); asm_xtensa_op_jx(&emit->as, r0); + } else if (op == MP_QSTR_ssl) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op_ssl(&emit->as, r0); + } else if (op == MP_QSTR_ssr) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op_ssr(&emit->as, r0); + } else if (op == MP_QSTR_ssai) { + mp_uint_t sa = get_arg_i(emit, op_str, pn_args[0], 0, 31); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 4, sa & 0x0F, (sa >> 4) & 0x01)); + } else if (op == MP_QSTR_ssa8b) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 3, r0, 0)); + } else if (op == MP_QSTR_ssa8l) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 2, r0, 0)); + } else if (op == MP_QSTR_call0) { + mp_uint_t label = get_arg_label(emit, op_str, pn_args[0]); + asm_xtensa_call0(&emit->as, label); + #if MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES + } else if (op == MP_QSTR_fsync) { + mp_uint_t imm3 = get_arg_i(emit, op_str, pn_args[0], 0, 7); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 0, 2, 8 | imm3, 0)); + } else if (op == MP_QSTR_ill_n) { + asm_xtensa_op16(&emit->as, 0xF06D); + #endif } else { goto unknown_op; } } else if (n_args == 2) { uint r0 = get_arg_reg(emit, op_str, pn_args[0]); - if (op == MP_QSTR_beqz) { - int label = get_arg_label(emit, op_str, pn_args[1]); - asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_EQ, r0, label); - } else if (op == MP_QSTR_bnez) { - int label = get_arg_label(emit, op_str, pn_args[1]); - asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_NE, r0, label); - } else if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) { + for (size_t index = 0; index < MP_ARRAY_SIZE(BCCZ_OPCODES); index++) { + if (op == BCCZ_OPCODES[index]) { + mp_uint_t label = get_arg_label(emit, op_str, pn_args[1]); + asm_xtensa_bccz_reg_label(&emit->as, index & 0x03, r0, label); + return; + } + } + if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) { // we emit mov.n for both "mov" and "mov_n" opcodes uint r1 = get_arg_reg(emit, op_str, pn_args[1]); asm_xtensa_op_mov_n(&emit->as, r0, r1); @@ -254,7 +328,53 @@ static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ // for convenience we emit l32r if the integer doesn't fit in movi uint32_t imm = get_arg_i(emit, op_str, pn_args[1], 0, 0); asm_xtensa_mov_reg_i32(&emit->as, r0, imm); - } else { + } else if (op == MP_QSTR_abs_) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 6, r0, 1, r1)); + } else if (op == MP_QSTR_neg) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 6, r0, 0, r1)); + } else if (op == MP_QSTR_sll) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 10, r0, r1, 0)); + } else if (op == MP_QSTR_sra) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 11, r0, 0, r1)); + } else if (op == MP_QSTR_srl) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 9, r0, 0, r1)); + } else if (op == MP_QSTR_nsa) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 14, r1, r0)); + } else if (op == MP_QSTR_nsau) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 15, r1, r0)); + } else if (op == MP_QSTR_l32r) { + mp_uint_t label = get_arg_label(emit, op_str, pn_args[1]); + asm_xtensa_l32r(&emit->as, r0, label); + } else if (op == MP_QSTR_movi_n) { + mp_int_t imm = get_arg_i(emit, op_str, pn_args[1], -32, 95); + asm_xtensa_op_movi_n(&emit->as, r0, imm); + } else + #if MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES + if (op == MP_QSTR_rsr) { + mp_uint_t sr = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RSR(0, 3, 0, sr, r0)); + } else if (op == MP_QSTR_rur) { + mp_uint_t imm8 = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 3, 14, r0, (imm8 >> 4) & 0x0F, imm8 & 0x0F)); + } else if (op == MP_QSTR_wsr) { + mp_uint_t sr = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RSR(0, 3, 1, sr, r0)); + } else if (op == MP_QSTR_wur) { + mp_uint_t sr = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RSR(0, 3, 15, sr, r0)); + } else if (op == MP_QSTR_xsr) { + mp_uint_t sr = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RSR(0, 1, 6, sr, r0)); + } else + #endif + { goto unknown_op; } @@ -288,7 +408,72 @@ static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ return; } } - goto unknown_op; + + if (op == MP_QSTR_add_n) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t r2 = get_arg_reg(emit, op_str, pn_args[2]); + asm_xtensa_op16(&emit->as, ASM_XTENSA_ENCODE_RRRN(10, r0, r1, r2)); + } else if (op == MP_QSTR_addi_n) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_int_t imm4 = get_arg_i(emit, op_str, pn_args[2], -1, 15); + asm_xtensa_op16(&emit->as, ASM_XTENSA_ENCODE_RRRN(11, r0, r1, (imm4 != 0 ? imm4 : -1))); + } else if (op == MP_QSTR_addmi) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_int_t imm8 = get_arg_i(emit, op_str, pn_args[2], -128 * 256, 127 * 256); + if ((imm8 & 0xFF) != 0) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("%d is not a multiple of %d"), imm8, 256)); + } else { + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRI8(2, 13, r1, r0, imm8 >> 8)); + } + } else if (op == MP_QSTR_bbci) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t bit = get_arg_i(emit, op_str, pn_args[1], 0, 31); + mp_int_t label = get_arg_label(emit, op_str, pn_args[2]); + asm_xtensa_bit_branch(&emit->as, r0, bit, label, 6); + } else if (op == MP_QSTR_bbsi) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t bit = get_arg_i(emit, op_str, pn_args[1], 0, 31); + mp_uint_t label = get_arg_label(emit, op_str, pn_args[2]); + asm_xtensa_bit_branch(&emit->as, r0, bit, label, 14); + } else if (op == MP_QSTR_slli) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t bits = 32 - get_arg_i(emit, op_str, pn_args[2], 1, 31); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 0 | ((bits >> 4) & 0x01), r0, r1, bits & 0x0F)); + } else if (op == MP_QSTR_srai) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t bits = get_arg_i(emit, op_str, pn_args[2], 0, 31); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 2 | ((bits >> 4) & 0x01), r0, bits & 0x0F, r1)); + } else if (op == MP_QSTR_srli) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t bits = get_arg_i(emit, op_str, pn_args[2], 0, 15); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 4, r0, bits, r1)); + } else if (op == MP_QSTR_l32i_n) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t imm = get_arg_i(emit, op_str, pn_args[2], 0, 60); + if ((imm & 0x03) != 0) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("%d is not a multiple of %d"), imm, 4)); + } else { + asm_xtensa_op_l32i_n(&emit->as, r0, r1, imm >> 2); + } + } else if (op == MP_QSTR_s32i_n) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t imm = get_arg_i(emit, op_str, pn_args[2], 0, 60); + if ((imm & 0x03) != 0) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("%d is not a multiple of %d"), imm, 4)); + } else { + asm_xtensa_op_s32i_n(&emit->as, r0, r1, imm >> 2); + } + } else { + goto unknown_op; + } } else { goto unknown_op; diff --git a/py/emitnative.c b/py/emitnative.c index a888418e5dfed..6f12200f7e171 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -222,12 +222,6 @@ static const uint8_t reg_local_table[MAX_REGS_FOR_LOCAL_VARS] = {REG_LOCAL_1, RE *emit->error_slot = mp_obj_new_exception_msg_varg(&mp_type_ViperTypeError, __VA_ARGS__); \ } while (0) -#if N_RV32 -#define FIT_SIGNED(value, bits) \ - ((((value) & ~((1U << ((bits) - 1)) - 1)) == 0) || \ - (((value) & ~((1U << ((bits) - 1)) - 1)) == ~((1U << ((bits) - 1)) - 1))) -#endif - typedef enum { STACK_VALUE, STACK_REG, @@ -335,12 +329,27 @@ struct _emit_t { #define ASM_CLR_REG(state, rd) ASM_XOR_REG_REG(state, rd, rd) #endif +#if N_RV32 +#define ASM_MOV_LOCAL_MP_OBJ_NULL(as, local_num, reg_temp) \ + ASM_MOV_LOCAL_REG(as, local_num, REG_ZERO) +#else +#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_MOV_LOCAL_REG(as, local_num, reg_temp) +#endif + static void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj); static void emit_native_global_exc_entry(emit_t *emit); static void emit_native_global_exc_exit(emit_t *emit); static void emit_native_load_const_obj(emit_t *emit, mp_obj_t obj); emit_t *EXPORT_FUN(new)(mp_emit_common_t * emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels) { + // Generated code performing exception handling assumes that MP_OBJ_NULL + // equals to 0 to simplify some checks, leveraging dedicated opcodes for + // comparisons against 0. If this assumption does not hold true anymore + // then generated code won't work correctly. + MP_STATIC_ASSERT(MP_OBJ_NULL == 0); + emit_t *emit = m_new0(emit_t, 1); emit->emit_common = emit_common; emit->error_slot = error_slot; @@ -467,6 +476,30 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->stack_info[i].vtype = VTYPE_UNBOUND; } + char *qualified_name = NULL; + + #if N_DEBUG + scope_t *current_scope = scope; + vstr_t *qualified_name_vstr = vstr_new(qstr_len(current_scope->simple_name)); + size_t fragment_length = 0; + const byte *fragment_pointer; + for (;;) { + fragment_pointer = qstr_data(current_scope->simple_name, &fragment_length); + vstr_hint_size(qualified_name_vstr, fragment_length); + memmove(qualified_name_vstr->buf + fragment_length, qualified_name_vstr->buf, qualified_name_vstr->len); + memcpy(qualified_name_vstr->buf, fragment_pointer, fragment_length); + qualified_name_vstr->len += fragment_length; + if (current_scope->parent == NULL || current_scope->parent->simple_name == MP_QSTR__lt_module_gt_) { + break; + } + vstr_ins_char(qualified_name_vstr, 0, '.'); + current_scope = current_scope->parent; + } + qualified_name = vstr_null_terminated_str(qualified_name_vstr); + #else + (void)qualified_name; + #endif + mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); // generate code for entry to function @@ -513,7 +546,7 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop } // Entry to function - ASM_ENTRY(emit->as, emit->stack_start + emit->n_state - num_locals_in_regs); + ASM_ENTRY(emit->as, emit->stack_start + emit->n_state - num_locals_in_regs, qualified_name); #if N_X86 asm_x86_mov_arg_to_r32(emit->as, 0, REG_PARENT_ARG_1); @@ -584,7 +617,7 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->start_offset); - ASM_ENTRY(emit->as, emit->code_state_start); + ASM_ENTRY(emit->as, emit->code_state_start, qualified_name); // Reset the state size for the state pointed to by REG_GENERATOR_STATE emit->code_state_start = 0; @@ -616,7 +649,7 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->stack_start = emit->code_state_start + SIZEOF_CODE_STATE; // Allocate space on C-stack for code_state structure, which includes state - ASM_ENTRY(emit->as, emit->stack_start + emit->n_state); + ASM_ENTRY(emit->as, emit->stack_start + emit->n_state, qualified_name); // Prepare incoming arguments for call to mp_setup_code_state @@ -682,6 +715,10 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop } } } + + #if N_DEBUG + vstr_free(qualified_name_vstr); + #endif } static inline void emit_native_write_code_info_byte(emit_t *emit, byte val) { @@ -1311,8 +1348,7 @@ static void emit_native_global_exc_entry(emit_t *emit) { // Check LOCAL_IDX_THROW_VAL for any injected value ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_THROW_VAL(emit)); - ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); + ASM_MOV_LOCAL_MP_OBJ_NULL(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); emit_call(emit, MP_F_NATIVE_RAISE); } } @@ -1579,87 +1615,50 @@ static void emit_native_load_subscr(emit_t *emit) { switch (vtype_base) { case VTYPE_PTR8: { // pointer to 8-bit memory - // TODO optimise to use thumb ldrb r1, [r2, r3] + #ifdef ASM_LOAD8_REG_REG_OFFSET + ASM_LOAD8_REG_REG_OFFSET(emit->as, REG_RET, reg_base, index_value); + #else if (index_value != 0) { // index is non-zero - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_ldrb_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 12)) { - asm_rv32_opcode_lbu(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_op_l8ui(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base reg_base = reg_index; } ASM_LOAD8_REG_REG(emit->as, REG_RET, reg_base); // load from (base+index) + #endif break; } case VTYPE_PTR16: { // pointer to 16-bit memory + #ifdef ASM_LOAD16_REG_REG_OFFSET + ASM_LOAD16_REG_REG_OFFSET(emit->as, REG_RET, reg_base, index_value); + #else if (index_value != 0) { // index is a non-zero immediate - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_ldrh_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 11)) { - asm_rv32_opcode_lhu(emit->as, REG_RET, reg_base, index_value << 1); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_op_l16ui(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base reg_base = reg_index; } ASM_LOAD16_REG_REG(emit->as, REG_RET, reg_base); // load from (base+2*index) + #endif break; } case VTYPE_PTR32: { // pointer to 32-bit memory + #ifdef ASM_LOAD32_REG_REG_OFFSET + ASM_LOAD32_REG_REG_OFFSET(emit->as, REG_RET, reg_base, index_value); + #else if (index_value != 0) { // index is a non-zero immediate - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_ldr_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 10)) { - asm_rv32_opcode_lw(emit->as, REG_RET, reg_base, index_value << 2); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_l32i_optimised(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base reg_base = reg_index; } ASM_LOAD32_REG_REG(emit->as, REG_RET, reg_base); // load from (base+4*index) + #endif break; } default: @@ -1680,40 +1679,36 @@ static void emit_native_load_subscr(emit_t *emit) { switch (vtype_base) { case VTYPE_PTR8: { // pointer to 8-bit memory - // TODO optimise to use thumb ldrb r1, [r2, r3] + #ifdef ASM_LOAD8_REG_REG_REG + ASM_LOAD8_REG_REG_REG(emit->as, REG_RET, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_LOAD8_REG_REG(emit->as, REG_RET, REG_ARG_1); // store value to (base+index) + #endif break; } case VTYPE_PTR16: { // pointer to 16-bit memory - #if N_XTENSA || N_XTENSAWIN - asm_xtensa_op_addx2(emit->as, REG_ARG_1, reg_index, REG_ARG_1); - asm_xtensa_op_l16ui(emit->as, REG_RET, REG_ARG_1, 0); - break; - #endif + #ifdef ASM_LOAD16_REG_REG_REG + ASM_LOAD16_REG_REG_REG(emit->as, REG_RET, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_LOAD16_REG_REG(emit->as, REG_RET, REG_ARG_1); // load from (base+2*index) + #endif break; } case VTYPE_PTR32: { // pointer to word-size memory - #if N_RV32 - asm_rv32_opcode_slli(emit->as, REG_TEMP2, reg_index, 2); - asm_rv32_opcode_cadd(emit->as, REG_ARG_1, REG_TEMP2); - asm_rv32_opcode_lw(emit->as, REG_RET, REG_ARG_1, 0); - break; - #elif N_XTENSA || N_XTENSAWIN - asm_xtensa_op_addx4(emit->as, REG_ARG_1, reg_index, REG_ARG_1); - asm_xtensa_op_l32i_n(emit->as, REG_RET, REG_ARG_1, 0); - break; - #endif + #ifdef ASM_LOAD32_REG_REG_REG + ASM_LOAD32_REG_REG_REG(emit->as, REG_RET, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_LOAD32_REG_REG(emit->as, REG_RET, REG_ARG_1); // load from (base+4*index) + #endif break; } default: @@ -1856,92 +1851,47 @@ static void emit_native_store_subscr(emit_t *emit) { switch (vtype_base) { case VTYPE_PTR8: { // pointer to 8-bit memory - // TODO optimise to use thumb strb r1, [r2, r3] + #ifdef ASM_STORE8_REG_REG_OFFSET + ASM_STORE8_REG_REG_OFFSET(emit->as, reg_value, reg_base, index_value); + #else if (index_value != 0) { // index is non-zero - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_strb_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 12)) { - asm_rv32_opcode_sb(emit->as, reg_value, reg_base, index_value); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_op_s8i(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value); - #if N_ARM - asm_arm_strb_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); - return; - #endif ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base reg_base = reg_index; } ASM_STORE8_REG_REG(emit->as, reg_value, reg_base); // store value to (base+index) + #endif break; } case VTYPE_PTR16: { // pointer to 16-bit memory + #ifdef ASM_STORE16_REG_REG_OFFSET + ASM_STORE16_REG_REG_OFFSET(emit->as, reg_value, reg_base, index_value); + #else if (index_value != 0) { // index is a non-zero immediate - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_strh_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 11)) { - asm_rv32_opcode_sh(emit->as, reg_value, reg_base, index_value << 1); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_op_s16i(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base reg_base = reg_index; } ASM_STORE16_REG_REG(emit->as, reg_value, reg_base); // store value to (base+2*index) + #endif break; } case VTYPE_PTR32: { // pointer to 32-bit memory + #ifdef ASM_STORE32_REG_REG_OFFSET + ASM_STORE32_REG_REG_OFFSET(emit->as, reg_value, reg_base, index_value); + #else if (index_value != 0) { // index is a non-zero immediate - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_str_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 10)) { - asm_rv32_opcode_sw(emit->as, reg_value, reg_base, index_value << 2); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_s32i_optimised(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_ARM - ASM_MOV_REG_IMM(emit->as, reg_index, index_value); - asm_arm_str_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); - return; - #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base reg_base = reg_index; } ASM_STORE32_REG_REG(emit->as, reg_value, reg_base); // store value to (base+4*index) + #endif break; } default: @@ -1972,50 +1922,36 @@ static void emit_native_store_subscr(emit_t *emit) { switch (vtype_base) { case VTYPE_PTR8: { // pointer to 8-bit memory - // TODO optimise to use thumb strb r1, [r2, r3] - #if N_ARM - asm_arm_strb_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); - break; - #endif + #ifdef ASM_STORE8_REG_REG_REG + ASM_STORE8_REG_REG_REG(emit->as, reg_value, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_STORE8_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+index) + #endif break; } case VTYPE_PTR16: { // pointer to 16-bit memory - #if N_ARM - asm_arm_strh_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); - break; - #elif N_XTENSA || N_XTENSAWIN - asm_xtensa_op_addx2(emit->as, REG_ARG_1, reg_index, REG_ARG_1); - asm_xtensa_op_s16i(emit->as, reg_value, REG_ARG_1, 0); - break; - #endif + #ifdef ASM_STORE16_REG_REG_REG + ASM_STORE16_REG_REG_REG(emit->as, reg_value, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_STORE16_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+2*index) + #endif break; } case VTYPE_PTR32: { // pointer to 32-bit memory - #if N_ARM - asm_arm_str_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); - break; - #elif N_RV32 - asm_rv32_opcode_slli(emit->as, REG_TEMP2, reg_index, 2); - asm_rv32_opcode_cadd(emit->as, REG_ARG_1, REG_TEMP2); - asm_rv32_opcode_sw(emit->as, reg_value, REG_ARG_1, 0); - break; - #elif N_XTENSA || N_XTENSAWIN - asm_xtensa_op_addx4(emit->as, REG_ARG_1, reg_index, REG_ARG_1); - asm_xtensa_op_s32i_n(emit->as, reg_value, REG_ARG_1, 0); - break; - #endif + #ifdef ASM_STORE32_REG_REG_REG + ASM_STORE32_REG_REG_REG(emit->as, reg_value, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_STORE32_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+4*index) + #endif break; } default: @@ -2328,8 +2264,7 @@ static void emit_native_with_cleanup(emit_t *emit, mp_uint_t label) { // Replace exception with MP_OBJ_NULL. emit_native_label_assign(emit, *emit->label_slot); - ASM_MOV_REG_IMM(emit->as, REG_TEMP0, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); + ASM_MOV_LOCAL_MP_OBJ_NULL(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); // end of with cleanup nlr_catch block emit_native_label_assign(emit, *emit->label_slot + 1); @@ -2436,8 +2371,7 @@ static void emit_native_for_iter_end(emit_t *emit) { static void emit_native_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) { if (within_exc_handler) { // Cancel any active exception so subsequent handlers don't see it - ASM_MOV_REG_IMM(emit->as, REG_TEMP0, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); + ASM_MOV_LOCAL_MP_OBJ_NULL(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); } else { emit_native_leave_exc_stack(emit, false); } @@ -3122,8 +3056,7 @@ static void emit_native_yield(emit_t *emit, int kind) { if (kind == MP_EMIT_YIELD_VALUE) { // Check LOCAL_IDX_THROW_VAL for any injected value ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_THROW_VAL(emit)); - ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); + ASM_MOV_LOCAL_MP_OBJ_NULL(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); emit_call(emit, MP_F_NATIVE_RAISE); } else { // Label loop entry diff --git a/py/emitndebug.c b/py/emitndebug.c index bd896a75c8ddf..e49c5cdbffa7b 100644 --- a/py/emitndebug.c +++ b/py/emitndebug.c @@ -108,8 +108,8 @@ static void asm_debug_end_pass(asm_debug_t *as) { (void)as; } -static void asm_debug_entry(asm_debug_t *as, int num_locals) { - asm_debug_printf(as, "ENTRY(num_locals=%d)\n", num_locals); +static void asm_debug_entry(asm_debug_t *as, int num_locals, char *name) { + asm_debug_printf(as, "ENTRY(%s, num_locals=%d)\n", name != NULL ? name : "?", num_locals); } static void asm_debug_exit(asm_debug_t *as) { @@ -195,8 +195,8 @@ static void asm_debug_setcc_reg_reg_reg(asm_debug_t *as, int op, int reg1, int r #define ASM_T asm_debug_t #define ASM_END_PASS asm_debug_end_pass -#define ASM_ENTRY(as, num_locals) \ - asm_debug_entry(as, num_locals) +#define ASM_ENTRY(as, num_locals, name) \ + asm_debug_entry(as, num_locals, name) #define ASM_EXIT(as) \ asm_debug_exit(as) @@ -251,8 +251,6 @@ static void asm_debug_setcc_reg_reg_reg(asm_debug_t *as, int op, int reg1, int r #define ASM_MUL_REG_REG(as, reg_dest, reg_src) \ asm_debug_reg_reg(as, "mul", reg_dest, reg_src) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) \ - asm_debug_reg_reg(as, "load", reg_dest, reg_base) #define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) \ asm_debug_reg_reg_offset(as, "load", reg_dest, reg_base, word_offset) #define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) \ @@ -264,8 +262,6 @@ static void asm_debug_setcc_reg_reg_reg(asm_debug_t *as, int op, int reg1, int r #define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) \ asm_debug_reg_reg(as, "load32", reg_dest, reg_base) -#define ASM_STORE_REG_REG(as, reg_src, reg_base) \ - asm_debug_reg_reg(as, "store", reg_src, reg_base) #define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) \ asm_debug_reg_reg_offset(as, "store", reg_src, reg_base, word_offset) #define ASM_STORE8_REG_REG(as, reg_src, reg_base) \ diff --git a/py/formatfloat.c b/py/formatfloat.c index b4348122ff428..1ea34f84bf7fb 100644 --- a/py/formatfloat.c +++ b/py/formatfloat.c @@ -33,394 +33,537 @@ #include #include #include "py/formatfloat.h" +#include "py/parsenum.h" /*********************************************************************** Routine for converting a arbitrary floating point number into a string. - The code in this function was inspired from Fred Bayer's pdouble.c. - Since pdouble.c was released as Public Domain, I'm releasing this - code as public domain as well. + The code in this function was inspired from Dave Hylands's previous + version, which was itself inspired from Fred Bayer's pdouble.c. The original code can be found in https://github.com/dhylands/format-float - Dave Hylands - ***********************************************************************/ -#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT -// 1 sign bit, 8 exponent bits, and 23 mantissa bits. -// exponent values 0 and 255 are reserved, exponent can be 1 to 254. -// exponent is stored with a bias of 127. -// The min and max floats are on the order of 1x10^37 and 1x10^-37 - -#define FPTYPE float -#define FPCONST(x) x##F -#define FPROUND_TO_ONE 0.9999995F -#define FPDECEXP 32 -#define FPMIN_BUF_SIZE 6 // +9e+99 +// Float formatting debug code is intended for use in ports/unix only, +// as it uses the libc float printing function as a reference. +#define DEBUG_FLOAT_FORMATTING 0 + +#if DEBUG_FLOAT_FORMATTING +#define DEBUG_PRINTF(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG_PRINTF(...) +#endif + +#if MICROPY_FLOAT_FORMAT_IMPL == MICROPY_FLOAT_FORMAT_IMPL_EXACT || MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define MP_FFUINT_FMT "%lu" +#else +#define MP_FFUINT_FMT "%u" +#endif + +static inline int fp_expval(mp_float_t x) { + mp_float_union_t fb = { x }; + return (int)fb.p.exp - MP_FLOAT_EXP_OFFSET; +} -#define FLT_SIGN_MASK 0x80000000 +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE -static inline int fp_signbit(float x) { - mp_float_union_t fb = {x}; - return fb.i & FLT_SIGN_MASK; +static inline int fp_isless1(mp_float_t x) { + return x < 1.0; } -#define fp_isnan(x) isnan(x) -#define fp_isinf(x) isinf(x) -static inline int fp_iszero(float x) { - mp_float_union_t fb = {x}; - return fb.i == 0; + +static inline int fp_iszero(mp_float_t x) { + return x == 0.0; } -static inline int fp_isless1(float x) { - mp_float_union_t fb = {x}; + +#if MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_APPROX +static inline int fp_equal(mp_float_t x, mp_float_t y) { + return x == y; +} +#else +static inline mp_float_t fp_diff(mp_float_t x, mp_float_t y) { + return x - y; +} +#endif + +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + +// The functions below are roughly equivalent to the ones above, +// but they are optimized to reduce code footprint by skipping +// handling for special values such as nan, inf, +/-0.0 +// for ports where FP support is done in software. +// +// They also take into account lost bits of REPR_C as needed. + +static inline int fp_isless1(mp_float_t x) { + mp_float_union_t fb = { x }; return fb.i < 0x3f800000; } -#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +static inline int fp_iszero(mp_float_t x) { + mp_float_union_t x_check = { x }; + return !x_check.i; // this is valid for REPR_C as well +} -// CIRCUITPY-CHANGE: prevent warnings -#pragma GCC diagnostic ignored "-Wfloat-equal" -#define FPTYPE double -#define FPCONST(x) x -#define FPROUND_TO_ONE 0.999999999995 -#define FPDECEXP 256 -#define FPMIN_BUF_SIZE 7 // +9e+199 -#define fp_signbit(x) signbit(x) -#define fp_isnan(x) isnan(x) -#define fp_isinf(x) isinf(x) -#define fp_iszero(x) (x == 0) -#define fp_isless1(x) (x < 1.0) +#if MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_APPROX +static inline int fp_equal(mp_float_t x, mp_float_t y) { + mp_float_union_t x_check = { x }; + mp_float_union_t y_check = { y }; + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + return (x_check.i & ~3) == (y_check.i & ~3); + #else + return x_check.i == y_check.i; + #endif +} +#else +static inline mp_float_t fp_diff(mp_float_t x, mp_float_t y) { + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + mp_float_union_t x_check = { x }; + mp_float_union_t y_check = { y }; + x_check.i &= ~3; + y_check.i &= ~3; + return x_check.f - y_check.f; + #else + return x - y; + #endif +} +#endif -#endif // MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT/DOUBLE +#endif -static inline int fp_expval(FPTYPE x) { - mp_float_union_t fb = {x}; - return (int)((fb.i >> MP_FLOAT_FRAC_BITS) & (~(0xFFFFFFFF << MP_FLOAT_EXP_BITS))) - MP_FLOAT_EXP_OFFSET; +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define FPMIN_BUF_SIZE 6 // +9e+99 +#define MAX_MANTISSA_DIGITS (9) +#define SAFE_MANTISSA_DIGITS (6) +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define FPMIN_BUF_SIZE 7 // +9e+199 +#define MAX_MANTISSA_DIGITS (19) +#define SAFE_MANTISSA_DIGITS (16) +#endif + +// Internal formatting flags +#define FMT_MODE_E 0x01 // render using scientific notation (%e) +#define FMT_MODE_G 0x02 // render using general format (%g) +#define FMT_MODE_F 0x04 // render using using expanded fixed-point format (%f) +#define FMT_E_CASE 0x20 // don't change this value (used for case conversion!) + +static char *mp_prepend_zeros(char *s, int cnt) { + *s++ = '0'; + *s++ = '.'; + while (cnt > 0) { + *s++ = '0'; + cnt--; + } + return s; } -int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) { +// Helper to convert a decimal mantissa (provided as an mp_large_float_uint_t) to string +static int mp_format_mantissa(mp_large_float_uint_t mantissa, mp_large_float_uint_t mantissa_cap, char *buf, char *s, + int num_digits, int max_exp_zeros, int trailing_zeros, int dec, int e, int fmt_flags) { - char *s = buf; + DEBUG_PRINTF("mantissa=" MP_FFUINT_FMT " exp=%d (cap=" MP_FFUINT_FMT "):\n", mantissa, e, mantissa_cap); - if (buf_size <= FPMIN_BUF_SIZE) { - // FPMIN_BUF_SIZE is the minimum size needed to store any FP number. - // If the buffer does not have enough room for this (plus null terminator) - // then don't try to format the float. + if (mantissa) { + // If rounding/searching created an extra digit or removed too many, fix mantissa first + if (mantissa >= mantissa_cap) { + if (fmt_flags & FMT_MODE_F) { + assert(e >= 0); + num_digits++; + dec++; + } else { + mantissa /= 10; + e++; + } + } + } - if (buf_size >= 2) { - *s++ = '?'; + // When 'g' format is used, replace small exponents by explicit zeros + if ((fmt_flags & FMT_MODE_G) && e != 0) { + if (e >= 0) { + // If 0 < e < max_exp_zeros, expand positive exponent into trailing zeros + if (e < max_exp_zeros) { + dec += e; + if (dec >= num_digits) { + trailing_zeros = dec - (num_digits - 1); + } + e = 0; + } + } else { + // If -4 <= e < 0, expand negative exponent without losing significant digits + if (e >= -4) { + int cnt = 0; + while (e < 0 && !(mantissa % 10)) { + mantissa /= 10; + cnt++; + e++; + } + num_digits -= cnt; + s = mp_prepend_zeros(s, cnt - e - 1); + dec = 255; + e = 0; + } } - if (buf_size >= 1) { - *s = '\0'; + } + + // Convert the integer mantissa to string + for (int digit = num_digits - 1; digit >= 0; digit--) { + int digit_ofs = (digit > dec ? digit + 1 : digit); + s[digit_ofs] = '0' + (int)(mantissa % 10); + mantissa /= 10; + } + int dot = (dec >= 255); + if (dec + 1 < num_digits) { + dot = 1; + s++; + s[dec] = '.'; + } + s += num_digits; + #if DEBUG_FLOAT_FORMATTING + *s = 0; + DEBUG_PRINTF(" = %s exp=%d num_digits=%d zeros=%d dec=%d\n", buf, e, num_digits, trailing_zeros, dec); + #endif + + // Append or remove trailing zeros, as required by format + if (trailing_zeros) { + dec -= num_digits - 1; + while (trailing_zeros--) { + if (!dec--) { + *s++ = '.'; + dot = 1; + } + *s++ = '0'; } - return buf_size >= 2; } - if (fp_signbit(f) && !fp_isnan(f)) { - *s++ = '-'; - f = -f; - } else { - if (sign) { - *s++ = sign; + if (fmt_flags & FMT_MODE_G) { + // 'g' format requires to remove trailing zeros after decimal point + if (dot) { + while (s[-1] == '0') { + s--; + } + if (s[-1] == '.') { + s--; + } + } + } + + // Append the exponent if needed + if (((e != 0) || (fmt_flags & FMT_MODE_E)) && !(fmt_flags & FMT_MODE_F)) { + *s++ = 'E' | (fmt_flags & FMT_E_CASE); + if (e >= 0) { + *s++ = '+'; + } else { + *s++ = '-'; + e = -e; } + if (e >= 100) { + *s++ = '0' + (e / 100); + } + *s++ = '0' + ((e / 10) % 10); + *s++ = '0' + (e % 10); } + *s = '\0'; + DEBUG_PRINTF(" ===> %s\n", buf); - // buf_remaining contains bytes available for digits and exponent. - // It is buf_size minus room for the sign and null byte. - int buf_remaining = buf_size - 1 - (s - buf); + return s - buf; +} +// minimal value expected for buf_size, to avoid checking everywhere for overflow +#define MIN_BUF_SIZE (MAX_MANTISSA_DIGITS + 10) + +int mp_format_float(mp_float_t f_entry, char *buf_entry, size_t buf_size, char fmt, int prec, char sign) { + assert(buf_size >= MIN_BUF_SIZE); + + // Handle sign + mp_float_t f = f_entry; + char *buf = buf_entry; + if (signbit(f_entry) && !isnan(f_entry)) { + f = -f; + sign = '-'; + } + if (sign) { + *buf++ = sign; + buf_size--; + } + + // Handle inf/nan + char uc = fmt & 0x20; { - char uc = fmt & 0x20; - if (fp_isinf(f)) { + char *s = buf; + if (isinf(f)) { *s++ = 'I' ^ uc; *s++ = 'N' ^ uc; *s++ = 'F' ^ uc; goto ret; - } else if (fp_isnan(f)) { + } else if (isnan(f)) { *s++ = 'N' ^ uc; *s++ = 'A' ^ uc; *s++ = 'N' ^ uc; ret: *s = '\0'; - return s - buf; + return s - buf_entry; } } + // Decode format character + int fmt_flags = (unsigned char)uc; // setup FMT_E_CASE, clear all other bits + char lofmt = (char)(fmt | 0x20); // fmt in lowercase + if (lofmt == 'f') { + fmt_flags |= FMT_MODE_F; + } else if (lofmt == 'g') { + fmt_flags |= FMT_MODE_G; + } else { + fmt_flags |= FMT_MODE_E; + } + + // When precision is unspecified, default to 6 if (prec < 0) { prec = 6; } - char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt - fmt |= 0x20; // Force fmt to be lowercase - char org_fmt = fmt; - if (fmt == 'g' && prec == 0) { - prec = 1; + // Use high precision for `repr`, but switch to exponent mode + // after 16 digits in any case to match CPython behaviour + int max_exp_zeros = (prec < (int)buf_size - 3 ? prec : (int)buf_size - 3); + if (prec == MP_FLOAT_REPR_PREC) { + prec = MAX_MANTISSA_DIGITS; + max_exp_zeros = 16; } - int e; - int dec = 0; - char e_sign = '\0'; - int num_digits = 0; - int signed_e = 0; - // Approximate power of 10 exponent from binary exponent. - // abs(e_guess) is lower bound on abs(power of 10 exponent). - int e_guess = (int)(fp_expval(f) * FPCONST(0.3010299956639812)); // 1/log2(10). - if (fp_iszero(f)) { - e = 0; - if (fmt == 'f') { - // Truncate precision to prevent buffer overflow - if (prec + 2 > buf_remaining) { - prec = buf_remaining - 2; - } - num_digits = prec + 1; - } else { - // Truncate precision to prevent buffer overflow - if (prec + 6 > buf_remaining) { - prec = buf_remaining - 6; - } - if (fmt == 'e') { - e_sign = '+'; - } + // Precompute the exact decimal exponent of f, such that + // abs(e) is lower bound on abs(power of 10 exponent). + int e = 0; + if (!fp_iszero(f)) { + // Approximate power of 10 exponent from binary exponent. + e = (int)(fp_expval(f) * MICROPY_FLOAT_CONST(0.3010299956639812)); // 1/log2(10). + int positive_exp = !fp_isless1(f); + mp_float_t u_base = (mp_float_t)mp_decimal_exp((mp_large_float_t)1.0, e + positive_exp); + while ((f >= u_base) == positive_exp) { + e += (positive_exp ? 1 : -1); + u_base = (mp_float_t)mp_decimal_exp((mp_large_float_t)1.0, e + positive_exp); } - } else if (fp_isless1(f)) { - FPTYPE f_entry = f; // Save f in case we go to 'f' format. - // Build negative exponent - e = -e_guess; - FPTYPE u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e); - while (u_base > f) { - ++e; - u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e); - } - // Normalize out the inferred unit. Use divide because - // pow(10, e) * pow(10, -e) is slightly < 1 for some e in float32 - // (e.g. print("%.12f" % ((1e13) * (1e-13)))) - f /= u_base; - - // If the user specified 'g' format, and e is <= 4, then we'll switch - // to the fixed format ('f') - - if (fmt == 'f' || (fmt == 'g' && e <= 4)) { - fmt = 'f'; - dec = 0; + } - if (org_fmt == 'g') { - prec += (e - 1); - } + // For 'e' format, prec is # digits after the decimal + // For 'f' format, prec is # digits after the decimal + // For 'g' format, prec is the max number of significant digits + // + // For 'e' & 'g' format, there will be a single digit before the decimal + // For 'f' format, zeros must be expanded instead of using an exponent. + // Make sure there is enough room in the buffer for them, or switch to format 'g'. + if ((fmt_flags & FMT_MODE_F) && e > 0) { + int req_size = e + prec + 2; + if (req_size > (int)buf_size) { + fmt_flags ^= FMT_MODE_F; + fmt_flags |= FMT_MODE_G; + prec++; + } + } - // truncate precision to prevent buffer overflow - if (prec + 2 > buf_remaining) { - prec = buf_remaining - 2; + // To work independently of the format, we precompute: + // - the max number of significant digits to produce + // - the number of leading zeros to prepend (mode f only) + // - the number of trailing zeros to append + int max_digits = prec; + int lead_zeros = 0; + int trail_zeros = 0; + if (fmt_flags & FMT_MODE_F) { + if (max_digits > (int)buf_size - 3) { + // cannot satisfy requested number of decimals given buf_size, sorry + max_digits = (int)buf_size - 3; + } + if (e < 0) { + if (max_digits > 2 && e < -2) { + // Insert explicit leading zeros + lead_zeros = (-e < max_digits ? -e : max_digits) - 2; + max_digits -= lead_zeros; + } else { + max_digits++; } - - num_digits = prec; - signed_e = 0; - f = f_entry; - ++num_digits; } else { - // For e & g formats, we'll be printing the exponent, so set the - // sign. - e_sign = '-'; - dec = 0; - - if (prec > (buf_remaining - FPMIN_BUF_SIZE)) { - prec = buf_remaining - FPMIN_BUF_SIZE; - if (fmt == 'g') { - prec++; - } - } - signed_e = -e; + max_digits += e + 1; } } else { - // Build positive exponent. - // We don't modify f at this point to avoid inaccuracies from - // scaling it. Instead, we find the product of powers of 10 - // that is not greater than it, and use that to start the - // mantissa. - e = e_guess; - FPTYPE next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1); - while (f >= next_u) { - ++e; - next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1); + if (!(fmt_flags & FMT_MODE_G) || max_digits == 0) { + max_digits++; } + } + if (max_digits > MAX_MANTISSA_DIGITS) { + // use trailing zeros to avoid overflowing the mantissa + trail_zeros = max_digits - MAX_MANTISSA_DIGITS; + max_digits = MAX_MANTISSA_DIGITS; + } + int overhead = (fmt_flags & FMT_MODE_F ? 3 : FPMIN_BUF_SIZE + 1); + if (trail_zeros > (int)buf_size - max_digits - overhead) { + // cannot satisfy requested number of decimals given buf_size, sorry + trail_zeros = (int)buf_size - max_digits - overhead; + } - // If the user specified fixed format (fmt == 'f') and e makes the - // number too big to fit into the available buffer, then we'll - // switch to the 'e' format. - - if (fmt == 'f') { - if (e >= buf_remaining) { - fmt = 'e'; - } else if ((e + prec + 2) > buf_remaining) { - prec = buf_remaining - e - 2; - if (prec < 0) { - // This means no decimal point, so we can add one back - // for the decimal. - prec++; - } - } - } - if (fmt == 'e' && prec > (buf_remaining - FPMIN_BUF_SIZE)) { - prec = buf_remaining - FPMIN_BUF_SIZE; - } - if (fmt == 'g') { - // Truncate precision to prevent buffer overflow - if (prec + (FPMIN_BUF_SIZE - 1) > buf_remaining) { - prec = buf_remaining - (FPMIN_BUF_SIZE - 1); - } - } - // If the user specified 'g' format, and e is < prec, then we'll switch - // to the fixed format. + // When the caller asks for more precision than available for sure, + // Look for a shorter (rounded) representation first, and only dig + // into more digits if there is no short representation. + int num_digits = (SAFE_MANTISSA_DIGITS < max_digits ? SAFE_MANTISSA_DIGITS : max_digits); +try_again: + ; - if (fmt == 'g' && e < prec) { - fmt = 'f'; - prec -= (e + 1); - } - if (fmt == 'f') { - dec = e; - num_digits = prec + e + 1; + char *s = buf; + int extra_zeros = trail_zeros + (max_digits - num_digits); + int decexp; + int dec = 0; + + if (fp_iszero(f)) { + // no need for scaling 0.0 + decexp = 0; + } else if (fmt_flags & FMT_MODE_F) { + decexp = num_digits - 1; + if (e < 0) { + // Negative exponent: we keep a single leading zero in the mantissa, + // as using more would waste precious digits needed for accuracy. + if (lead_zeros > 0) { + // We are using leading zeros + s = mp_prepend_zeros(s, lead_zeros); + decexp += lead_zeros + 1; + dec = 255; // no decimal dot + } else { + // Small negative exponent, work directly on the mantissa + dec = 0; + } } else { - e_sign = '+'; + // Positive exponent: we will add trailing zeros separately + decexp -= e; + dec = e; } - signed_e = e; + } else { + decexp = num_digits - e - 1; } - if (prec < 0) { - // This can happen when the prec is trimmed to prevent buffer overflow - prec = 0; + DEBUG_PRINTF("input=%.19g e=%d fmt=%c max_d=%d num_d=%d decexp=%d dec=%d l0=%d r0=%d\n", + (double)f, e, lofmt, max_digits, num_digits, decexp, dec, lead_zeros, extra_zeros); + + // At this point, + // - buf points to beginning of output buffer for the unsigned representation + // - num_digits == the number of mantissa digits to add + // - (dec + 1) == the number of digits to print before adding a decimal point + // - decexp == the power of 10 exponent to apply to f to get the decimal mantissa + // - e == the power of 10 exponent to append ('e' or 'g' format) + mp_large_float_uint_t mantissa_cap = 10; + for (int n = 1; n < num_digits; n++) { + mantissa_cap *= 10; } - // At this point e contains the absolute value of the power of 10 exponent. - // (dec + 1) == the number of dgits before the decimal. - - // For e, prec is # digits after the decimal - // For f, prec is # digits after the decimal - // For g, prec is the max number of significant digits - // - // For e & g there will be a single digit before the decimal - // for f there will be e digits before the decimal - - if (fmt == 'e') { - num_digits = prec + 1; - } else if (fmt == 'g') { - if (prec == 0) { - prec = 1; + // Build the decimal mantissa into a large uint + mp_large_float_uint_t mantissa = 1; + if (sizeof(mp_large_float_t) == sizeof(mp_float_t) && num_digits > SAFE_MANTISSA_DIGITS && decexp > 1) { + // if we don't have large floats, use integer multiply to produce the last digits + if (num_digits > SAFE_MANTISSA_DIGITS + 1 && decexp > 2) { + mantissa = 100; + decexp -= 2; + } else { + mantissa = 10; + decexp -= 1; } - num_digits = prec; } - - int d = 0; - for (int digit_index = signed_e; num_digits >= 0; --digit_index) { - FPTYPE u_base = FPCONST(1.0); - if (digit_index > 0) { - // Generate 10^digit_index for positive digit_index. - u_base = MICROPY_FLOAT_C_FUN(pow)(10, digit_index); - } - for (d = 0; d < 9; ++d) { - if (f < u_base) { - break; - } - f -= u_base; - } - // We calculate one more digit than we display, to use in rounding - // below. So only emit the digit if it's one that we display. - if (num_digits > 0) { - // Emit this number (the leading digit). - *s++ = '0' + d; - if (dec == 0 && prec > 0) { - *s++ = '.'; - } - } - --dec; - --num_digits; - if (digit_index <= 0) { - // Once we get below 1.0, we scale up f instead of calculating - // negative powers of 10 in u_base. This provides better - // renditions of exact decimals like 1/16 etc. - f *= FPCONST(10.0); + mp_large_float_t mantissa_f = mp_decimal_exp((mp_large_float_t)f, decexp); + mantissa *= (mp_large_float_uint_t)(mantissa_f + (mp_large_float_t)0.5); + DEBUG_PRINTF("input=%.19g fmt=%c num_digits=%d dec=%d mantissa=" MP_FFUINT_FMT " r0=%d\n", (double)f, lofmt, num_digits, dec, mantissa, extra_zeros); + + // Finally convert the decimal mantissa to a floating-point string, according to formatting rules + int reprlen = mp_format_mantissa(mantissa, mantissa_cap, buf, s, num_digits, max_exp_zeros, extra_zeros, dec, e, fmt_flags); + assert(reprlen + 1 <= (int)buf_size); + + #if MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_APPROX + + if (num_digits < max_digits) { + // The initial precision might not be sufficient for an exact representation + // for all numbers. If the result is not exact, restart using next precision. + // parse the resulting number and compare against the original + mp_float_t check; + DEBUG_PRINTF("input=%.19g, compare to float('%s')\n", (double)f, buf); + mp_parse_float_internal(buf, reprlen, &check); + if (!fp_equal(check, f)) { + num_digits++; + DEBUG_PRINTF("Not perfect, retry using more digits (%d)\n", num_digits); + goto try_again; } } - // Rounding. If the next digit to print is >= 5, round up. - if (d >= 5) { - char *rs = s; - rs--; - while (1) { - if (*rs == '.') { - rs--; - continue; - } - if (*rs < '0' || *rs > '9') { - // + or - - rs++; // So we sit on the digit to the right of the sign - break; + + #else + + // The initial decimal mantissa might not have been be completely accurate due + // to the previous loating point operations. The best way to verify this is to + // parse the resulting number and compare against the original + mp_float_t check; + DEBUG_PRINTF("input=%.19g, compare to float('%s')\n", (double)f, buf); + mp_parse_float_internal(buf, reprlen, &check); + mp_float_t diff = fp_diff(check, f); + mp_float_t best_diff = diff; + mp_large_float_uint_t best_mantissa = mantissa; + + if (fp_iszero(diff)) { + // we have a perfect match + DEBUG_PRINTF(MP_FFUINT_FMT ": perfect match (direct)\n", mantissa); + } else { + // In order to get the best possible representation, we will perform a + // dichotomic search for a reversible representation. + // This will also provide optimal rounding on the fly. + unsigned err_range = 1; + if (num_digits > SAFE_MANTISSA_DIGITS) { + err_range <<= 3 * (num_digits - SAFE_MANTISSA_DIGITS); + } + int maxruns = 3 + 3 * (MAX_MANTISSA_DIGITS - SAFE_MANTISSA_DIGITS); + while (maxruns-- > 0) { + // update mantissa according to dichotomic search + if (signbit(diff)) { + mantissa += err_range; + } else { + // mantissa is expected to always have more significant digits than err_range + assert(mantissa >= err_range); + mantissa -= err_range; } - if (*rs < '9') { - (*rs)++; + // retry conversion + reprlen = mp_format_mantissa(mantissa, mantissa_cap, buf, s, num_digits, max_exp_zeros, extra_zeros, dec, e, fmt_flags); + assert(reprlen + 1 <= (int)buf_size); + DEBUG_PRINTF("input=%.19g, compare to float('%s')\n", (double)f, buf); + mp_parse_float_internal(buf, reprlen, &check); + DEBUG_PRINTF("check=%.19g num_digits=%d e=%d mantissa=" MP_FFUINT_FMT "\n", (double)check, num_digits, e, mantissa); + diff = fp_diff(check, f); + if (fp_iszero(diff)) { + // we have a perfect match + DEBUG_PRINTF(MP_FFUINT_FMT ": perfect match\n", mantissa); break; } - *rs = '0'; - if (rs == buf) { - break; + // keep track of our best estimate + mp_float_t delta = MICROPY_FLOAT_C_FUN(fabs)(diff) - MICROPY_FLOAT_C_FUN(fabs)(best_diff); + if (signbit(delta) || (fp_iszero(delta) && !(mantissa % 10u))) { + best_diff = diff; + best_mantissa = mantissa; } - rs--; - } - if (*rs == '0') { - // We need to insert a 1 - if (rs[1] == '.' && fmt != 'f') { - // We're going to round 9.99 to 10.00 - // Move the decimal point - rs[0] = '.'; - rs[1] = '0'; - if (e_sign == '-') { - e--; - if (e == 0) { - e_sign = '+'; - } - } else { - e++; - } + // string repr is not perfect: continue a dichotomic improvement + DEBUG_PRINTF(MP_FFUINT_FMT ": %.19g, err_range=%d\n", mantissa, (double)check, err_range); + if (err_range > 1) { + err_range >>= 1; } else { - // Need at extra digit at the end to make room for the leading '1' - // but if we're at the buffer size limit, just drop the final digit. - if ((size_t)(s + 1 - buf) < buf_size) { - s++; + // We have tried all possible mantissa, without finding a reversible repr. + // Check if we have an alternate precision to try. + if (num_digits < max_digits) { + num_digits++; + DEBUG_PRINTF("Failed to find a perfect match, try with more digits (%d)\n", num_digits); + goto try_again; } + // Otherwise, keep the closest one, which is either the first one or the last one. + if (mantissa == best_mantissa) { + // Last guess is the best one + DEBUG_PRINTF(MP_FFUINT_FMT ": last guess was the best one\n", mantissa); + } else { + // We had a better guess earlier + DEBUG_PRINTF(MP_FFUINT_FMT ": use best guess\n", mantissa); + reprlen = mp_format_mantissa(best_mantissa, mantissa_cap, buf, s, num_digits, max_exp_zeros, extra_zeros, dec, e, fmt_flags); + } + break; } - char *ss = s; - while (ss > rs) { - *ss = ss[-1]; - ss--; - } - *rs = '1'; } } + #endif - // verify that we did not overrun the input buffer so far - assert((size_t)(s + 1 - buf) <= buf_size); - - if (org_fmt == 'g' && prec > 0) { - // Remove trailing zeros and a trailing decimal point - while (s[-1] == '0') { - s--; - } - if (s[-1] == '.') { - s--; - } - } - // Append the exponent - if (e_sign) { - *s++ = e_char; - *s++ = e_sign; - if (FPMIN_BUF_SIZE == 7 && e >= 100) { - *s++ = '0' + (e / 100); - } - *s++ = '0' + ((e / 10) % 10); - *s++ = '0' + (e % 10); - } - *s = '\0'; - - // verify that we did not overrun the input buffer - assert((size_t)(s + 1 - buf) <= buf_size); - - return s - buf; + return buf + reprlen - buf_entry; } #endif // MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE diff --git a/py/formatfloat.h b/py/formatfloat.h index 9a1643b4ddff5..7b1414672b77b 100644 --- a/py/formatfloat.h +++ b/py/formatfloat.h @@ -29,6 +29,7 @@ #include "py/mpconfig.h" #if MICROPY_PY_BUILTINS_FLOAT +#define MP_FLOAT_REPR_PREC (99) // magic `prec` value for optimal `repr` behaviour int mp_format_float(mp_float_t f, char *buf, size_t bufSize, char fmt, int prec, char sign); #endif diff --git a/py/gc.c b/py/gc.c index fc7de6c4d3b4f..757c27e3de696 100644 --- a/py/gc.c +++ b/py/gc.c @@ -1447,7 +1447,7 @@ void gc_dump_alloc_table(const mp_print_t *print) { } if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) { // there are at least 2 lines containing only free blocks, so abbreviate their printing - mp_printf(print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE); + mp_printf(print, "\n (%u lines all free)", (uint)((bl2 - bl) / DUMP_BYTES_PER_LINE)); bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1)); if (bl >= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB) { // got to end of heap @@ -1490,7 +1490,7 @@ void gc_dump_alloc_table(const mp_print_t *print) { break; } */ - /* this prints the uPy object type of the head block */ + /* this prints the MicroPython object type of the head block */ case AT_HEAD: { // CIRCUITPY-CHANGE: compiler warning avoidance #pragma GCC diagnostic push diff --git a/py/gc.h b/py/gc.h index 0752478d1f286..f9028d637c5c6 100644 --- a/py/gc.h +++ b/py/gc.h @@ -71,6 +71,11 @@ bool gc_alloc_possible(void); // Use this function to sweep the whole heap and run all finalisers void gc_sweep_all(void); +// These functions are used to manage weakrefs. +void gc_weakref_mark(void *ptr); +void gc_weakref_about_to_be_freed(void *ptr); +void gc_weakref_sweep(void); + enum { GC_ALLOC_FLAG_HAS_FINALISER = 1, // CIRCUITPY-CHANGE diff --git a/py/make_root_pointers.py b/py/make_root_pointers.py index efe398b8227ca..5da343c1270ec 100644 --- a/py/make_root_pointers.py +++ b/py/make_root_pointers.py @@ -6,8 +6,6 @@ "struct _mp_state_vm_t" in py/mpstate.h """ -from __future__ import print_function - import argparse import io import re diff --git a/py/makecompresseddata.py b/py/makecompresseddata.py index 1bce3e8e8374a..a6fa7db00b280 100644 --- a/py/makecompresseddata.py +++ b/py/makecompresseddata.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import collections import re import sys diff --git a/py/makemoduledefs.py b/py/makemoduledefs.py index 29162ab387c8b..c8fdc21f6fbe2 100644 --- a/py/makemoduledefs.py +++ b/py/makemoduledefs.py @@ -14,8 +14,6 @@ the built-in version. """ -from __future__ import print_function - import sys import re import io @@ -87,19 +85,25 @@ def generate_module_table_header(modules): ) ) + # There should always be at least one module (__main__ in runtime.c) + assert mod_defs + print("\n#define MICROPY_REGISTERED_MODULES \\") for mod_def in sorted(mod_defs): print(" {mod_def} \\".format(mod_def=mod_def)) - print("// MICROPY_REGISTERED_MODULES") - print("\n#define MICROPY_REGISTERED_EXTENSIBLE_MODULES \\") + # There are not necessarily any extensible modules (e.g., bare-arm or minimal x86) + print("\n#define MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES ", len(extensible_mod_defs)) - for mod_def in sorted(extensible_mod_defs): - print(" {mod_def} \\".format(mod_def=mod_def)) + if extensible_mod_defs: + print("\n#define MICROPY_REGISTERED_EXTENSIBLE_MODULES \\") + + for mod_def in sorted(extensible_mod_defs): + print(" {mod_def} \\".format(mod_def=mod_def)) - print("// MICROPY_REGISTERED_EXTENSIBLE_MODULES") + print("// MICROPY_REGISTERED_EXTENSIBLE_MODULES") def generate_module_delegations(delegations): diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 998328bf1d250..3fdb2d5476b8a 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -1,31 +1,13 @@ """ Process raw qstr file and output qstr data with length, hash and data bytes. -This script works with Python 2.7, 3.3 and 3.4. - -CIRCUITPY-CHANGE: -For documentation about the format of compressed translated strings, see -supervisor/shared/translate/translate.h +This script works with Python 3.3+. """ -from __future__ import print_function - - import re import sys -# CIRCUITPY-CHANGE -if hasattr(sys.stdout, "reconfigure"): - sys.stdout.reconfigure(encoding="utf-8") - sys.stderr.reconfigure(errors="backslashreplace") - -# Python 2/3 compatibility: -# - iterating through bytes is different -# - codepoint2name from html.entities is hard-coded -if sys.version_info[0] == 2: - bytes_cons = lambda val, enc=None: bytearray(val) -elif sys.version_info[0] == 3: # Also handles MicroPython - bytes_cons = bytes +bytes_cons = bytes # fmt: off codepoint2name = { @@ -67,7 +49,6 @@ 253: "yacute", 165: "yen", 255: "yuml", 950: "zeta", 8205: "zwj", 8204: "zwnj" } # fmt: on -# end compatibility code codepoint2name[ord("-")] = "hyphen" diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index a403985769489..99ba6e2f9e5a0 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -2,11 +2,9 @@ This script processes the output from the C preprocessor and extracts all qstr. Each qstr is transformed into a qstr definition of the form 'Q(...)'. -This script works with Python 3.x (CIRCUITPY-CHANGE: not 2.x) +This script works with Python 3.3+. """ -from __future__ import print_function - import io import os import re diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 307bfac1e9adb..09d67d87f8ad2 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -5,8 +5,6 @@ This script works with Python 3.7 and newer """ -from __future__ import print_function - import argparse import sys import os @@ -29,6 +27,25 @@ def cannot_determine_version(): make fetch-tags""" ) + with open(os.path.join(repo_path, "py", "mpconfig.h")) as f: + for line in f: + if line.startswith("#define MICROPY_VERSION_MAJOR "): + ver_major = int(line.strip().split()[2]) + elif line.startswith("#define MICROPY_VERSION_MINOR "): + ver_minor = int(line.strip().split()[2]) + elif line.startswith("#define MICROPY_VERSION_MICRO "): + ver_micro = int(line.strip().split()[2]) + elif line.startswith("#define MICROPY_VERSION_PRERELEASE "): + ver_prerelease = int(line.strip().split()[2]) + git_tag = "v%d.%d.%d%s" % ( + ver_major, + ver_minor, + ver_micro, + "-preview" if ver_prerelease else "", + ) + return git_tag + return None + def make_version_header(repo_path, filename): # Get version info using git (required) diff --git a/py/malloc.c b/py/malloc.c index fcf930ecfaa83..c2a20cb817b85 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -24,7 +24,6 @@ * THE SOFTWARE. */ -#include #include #include #include @@ -54,9 +53,11 @@ // freely accessed - for interfacing with system and 3rd-party libs for // example. On the other hand, some (e.g. bare-metal) ports may use GC // heap as system heap, so, to avoid warnings, we do undef's first. -// CIRCUITPY-CHANGE: Add selective collect support to malloc to optimize GC for large buffers +#undef malloc #undef free #undef realloc +#define malloc(b) gc_alloc((b), false) +#define malloc_with_finaliser(b) gc_alloc((b), true) #define free gc_free #define realloc(ptr, n) gc_realloc(ptr, n, true) #define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv) @@ -85,7 +86,7 @@ static void *realloc_ext(void *ptr, size_t n_bytes, bool allow_move) { #endif // MICROPY_ENABLE_GC -// CIRCUITPY-CHANGE: Add malloc helper with flags instead of a list of bools. +// CIRCUITPY-CHANGE: Add malloc helper to factor out flag handling and allow combinations. void *m_malloc_helper(size_t num_bytes, uint8_t flags) { void *ptr; #if MICROPY_ENABLE_GC @@ -112,7 +113,6 @@ void *m_malloc_helper(size_t num_bytes, uint8_t flags) { MP_STATE_MEM(current_bytes_allocated) += num_bytes; UPDATE_PEAK(); #endif - // CIRCUITPY-CHANGE // If this config is set then the GC clears all memory, so we don't need to. #if !MICROPY_GC_CONSERVATIVE_CLEAR if (flags & M_MALLOC_ENSURE_ZEROED) { @@ -133,17 +133,25 @@ void *m_malloc_maybe(size_t num_bytes) { return m_malloc_helper(num_bytes, M_MALLOC_COLLECT); } +#if MICROPY_ENABLE_FINALISER +void *m_malloc_with_finaliser(size_t num_bytes) { + // CIRCUITPY-CHANGE + return m_malloc_helper(num_bytes, M_MALLOC_COLLECT | M_MALLOC_WITH_FINALISER); +#endif + void *m_malloc0(size_t num_bytes) { + // CIRCUITPY-CHANGE return m_malloc_helper(num_bytes, M_MALLOC_ENSURE_ZEROED | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); } +// CIRCUITPY-CHANGE: selective collect void *m_malloc_without_collect(size_t num_bytes) { - // CIRCUITPY-CHANGE return m_malloc_helper(num_bytes, M_MALLOC_RAISE_ERROR); } +// CIRCUITPY-CHANGE: selective collect void *m_malloc_maybe_without_collect(size_t num_bytes) { - // CIRCUITPY-CHANGE + return m_malloc_helper(num_bytes, 0); } @@ -224,6 +232,31 @@ void m_free(void *ptr) #if MICROPY_TRACKED_ALLOC +#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL +// If there's no GIL, use the GC recursive mutex to protect the tracked node linked list +// under m_tracked_head. +// +// (For ports with GIL, the expectation is to only call tracked alloc functions +// while holding the GIL.) + +static inline void m_tracked_node_lock(void) { + mp_thread_recursive_mutex_lock(&MP_STATE_MEM(gc_mutex), 1); +} + +static inline void m_tracked_node_unlock(void) { + mp_thread_recursive_mutex_unlock(&MP_STATE_MEM(gc_mutex)); +} + +#else + +static inline void m_tracked_node_lock(void) { +} + +static inline void m_tracked_node_unlock(void) { +} + +#endif + #define MICROPY_TRACKED_ALLOC_STORE_SIZE (!MICROPY_ENABLE_GC) typedef struct _m_tracked_node_t { @@ -237,6 +270,7 @@ typedef struct _m_tracked_node_t { #if MICROPY_DEBUG_VERBOSE static size_t m_tracked_count_links(size_t *nb) { + m_tracked_node_lock(); m_tracked_node_t *node = MP_STATE_VM(m_tracked_head); size_t n = 0; *nb = 0; @@ -249,6 +283,7 @@ static size_t m_tracked_count_links(size_t *nb) { #endif node = node->next; } + m_tracked_node_unlock(); return n; } #endif @@ -263,12 +298,14 @@ void *m_tracked_calloc(size_t nmemb, size_t size) { size_t n = m_tracked_count_links(&nb); DEBUG_printf("m_tracked_calloc(%u, %u) -> (%u;%u) %p\n", (int)nmemb, (int)size, (int)n, (int)nb, node); #endif + m_tracked_node_lock(); if (MP_STATE_VM(m_tracked_head) != NULL) { MP_STATE_VM(m_tracked_head)->prev = node; } node->prev = NULL; node->next = MP_STATE_VM(m_tracked_head); MP_STATE_VM(m_tracked_head) = node; + m_tracked_node_unlock(); #if MICROPY_TRACKED_ALLOC_STORE_SIZE node->size = nmemb * size; #endif @@ -282,8 +319,7 @@ void m_tracked_free(void *ptr_in) { if (ptr_in == NULL) { return; } - // CIRCUITPY-CHANGE: cast to avoid compiler warning - m_tracked_node_t *node = (m_tracked_node_t *)(void *)((uint8_t *)ptr_in - sizeof(m_tracked_node_t)); + m_tracked_node_t *node = (m_tracked_node_t *)((uint8_t *)ptr_in - sizeof(m_tracked_node_t)); #if MICROPY_DEBUG_VERBOSE size_t data_bytes; #if MICROPY_TRACKED_ALLOC_STORE_SIZE @@ -294,7 +330,8 @@ void m_tracked_free(void *ptr_in) { size_t nb; size_t n = m_tracked_count_links(&nb); DEBUG_printf("m_tracked_free(%p, [%p, %p], nbytes=%u, links=%u;%u)\n", node, node->prev, node->next, (int)data_bytes, (int)n, (int)nb); - #endif + #endif // MICROPY_DEBUG_VERBOSE + m_tracked_node_lock(); if (node->next != NULL) { node->next->prev = node->prev; } @@ -303,6 +340,7 @@ void m_tracked_free(void *ptr_in) { } else { MP_STATE_VM(m_tracked_head) = node->next; } + m_tracked_node_unlock(); m_free(node #if MICROPY_MALLOC_USES_ALLOCATED_SIZE #if MICROPY_TRACKED_ALLOC_STORE_SIZE diff --git a/py/misc.h b/py/misc.h index 22f32147124fc..d977ee6a848fa 100644 --- a/py/misc.h +++ b/py/misc.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_PY_MISC_H #define MICROPY_INCLUDED_PY_MISC_H +#include "py/mpconfig.h" + // a mini library of useful types and functions /** types *******************************************************/ @@ -34,9 +36,6 @@ #include #include -// CIRCUITPY-CHANGE: include directly instead of depending on previous includes -#include "mpconfig.h" - typedef unsigned char byte; typedef unsigned int uint; @@ -54,6 +53,7 @@ typedef unsigned int uint; #define MP_STRINGIFY(x) MP_STRINGIFY_HELPER(x) // Static assertion macro +// CIRCUITPY-CHANGE: defined() #if defined(__cplusplus) #define MP_STATIC_ASSERT(cond) static_assert((cond), #cond) #elif __GNUC__ >= 5 || __STDC_VERSION__ >= 201112L @@ -70,7 +70,7 @@ typedef unsigned int uint; #if defined(_MSC_VER) || defined(__cplusplus) #define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)1) #else -#if defined(__clang__) +// CIRCUITPY-CHANGE: defined()#if defined(__clang__) #pragma GCC diagnostic ignored "-Wgnu-folding-constant" #endif #define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)sizeof(char[1 - 2 * !(cond)])) @@ -98,9 +98,9 @@ typedef unsigned int uint; #define m_new0(type, num) ((type *)(m_malloc0(sizeof(type) * (num)))) #define m_new_obj(type) (m_new(type, 1)) #define m_new_obj_maybe(type) (m_new_maybe(type, 1)) -#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_helper(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num), M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT)) -#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_helper(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num), M_MALLOC_ENSURE_ZEROED | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT)) -#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_helper(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num), M_MALLOC_COLLECT)) +#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc0(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_maybe(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) #if MICROPY_MALLOC_USES_ALLOCATED_SIZE #define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num)))) #define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num), (allow_move)))) @@ -126,9 +126,13 @@ typedef unsigned int uint; void *m_malloc_helper(size_t num_bytes, uint8_t flags); void *m_malloc(size_t num_bytes); void *m_malloc_maybe(size_t num_bytes); +void *m_malloc_with_finaliser(size_t num_bytes); void *m_malloc0(size_t num_bytes); + +// CIRCUITPY-CHANGE: _without_collect void *m_malloc_without_collect(size_t num_bytes); void *m_malloc_maybe_without_collect(size_t num_bytes); + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes); void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes, bool allow_move); @@ -138,7 +142,7 @@ void *m_realloc(void *ptr, size_t new_num_bytes); void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move); void m_free(void *ptr); #endif -NORETURN void m_malloc_fail(size_t num_bytes); +MP_NORETURN void m_malloc_fail(size_t num_bytes); #if MICROPY_TRACKED_ALLOC // These alloc/free functions track the pointers in a linked list so the GC does not reclaim @@ -161,7 +165,7 @@ size_t m_get_peak_bytes_allocated(void); // align ptr to the nearest multiple of "alignment" #define MP_ALIGN(ptr, alignment) (void *)(((uintptr_t)(ptr) + ((alignment) - 1)) & ~((alignment) - 1)) -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: addition #define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) /** unichar / UTF-8 *********************************************/ @@ -308,6 +312,25 @@ typedef union _mp_float_union_t { mp_float_uint_t i; } mp_float_union_t; +#if MICROPY_FLOAT_FORMAT_IMPL == MICROPY_FLOAT_FORMAT_IMPL_EXACT + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +// Exact float conversion requires using internally a bigger sort of floating point +typedef double mp_large_float_t; +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +typedef long double mp_large_float_t; +#endif +// Always use a 64 bit mantissa for formatting and parsing +typedef uint64_t mp_large_float_uint_t; + +#else // MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_EXACT + +// No bigger floating points +typedef mp_float_t mp_large_float_t; +typedef mp_float_uint_t mp_large_float_uint_t; + +#endif + #endif // MICROPY_PY_BUILTINS_FLOAT /** ROM string compression *************/ @@ -413,26 +436,30 @@ static inline bool mp_check(bool value) { static inline uint32_t mp_popcount(uint32_t x) { return __popcnt(x); } -#else +#else // _MSC_VER #define mp_clz(x) __builtin_clz(x) #define mp_clzl(x) __builtin_clzl(x) #define mp_clzll(x) __builtin_clzll(x) #define mp_ctz(x) __builtin_ctz(x) #define mp_check(x) (x) +// CIRCUITPY-CHANGE: defined() #if defined __has_builtin #if __has_builtin(__builtin_popcount) #define mp_popcount(x) __builtin_popcount(x) -#endif -#endif -#if !defined(mp_popcount) +#else static inline uint32_t mp_popcount(uint32_t x) { x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; - return x * 0x01010101; + return (x * 0x01010101) >> 24; } -#endif -#endif +#endif // __has_builtin(__builtin_popcount) +#endif // _MSC_VER + +#define MP_FIT_UNSIGNED(bits, value) (((value) & (~0U << (bits))) == 0) +#define MP_FIT_SIGNED(bits, value) \ + (MP_FIT_UNSIGNED(((bits) - 1), (value)) || \ + (((value) & (~0U << ((bits) - 1))) == (~0U << ((bits) - 1)))) // mp_int_t can be larger than long, i.e. Windows 64-bit, nan-box variants static inline uint32_t mp_clz_mpi(mp_int_t x) { @@ -448,16 +475,107 @@ static inline uint32_t mp_clz_mpi(mp_int_t x) { } return zeroes; #else - MP_STATIC_ASSERT(sizeof(mp_int_t) == sizeof(long long) - || sizeof(mp_int_t) == sizeof(long)); + #if MP_INT_MAX == INT_MAX + return mp_clz((unsigned)x); + #elif MP_INT_MAX == LONG_MAX + return mp_clzl((unsigned long)x); + #elif MP_INT_MAX == LLONG_MAX + return mp_clzll((unsigned long long)x); + #else + #error Unexpected MP_INT_MAX value + #endif + #endif +} + +// Overflow-checked operations + +// Integer overflow builtins were added to GCC 5, but __has_builtin only in GCC 10 +// +// Note that the builtins has a defined result when overflow occurs, whereas the custom +// functions below don't update the result if an overflow would occur (to avoid UB). +#define MP_GCC_HAS_BUILTIN_OVERFLOW (__GNUC__ >= 5) + +#if MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC + +#define mp_mul_ull_overflow __builtin_umulll_overflow +#define mp_mul_ll_overflow __builtin_smulll_overflow +static inline bool mp_mul_mp_int_t_overflow(mp_int_t x, mp_int_t y, mp_int_t *res) { + // __builtin_mul_overflow is a type-generic function, this inline ensures the argument + // types are checked to match mp_int_t. + return __builtin_mul_overflow(x, y, res); +} + +#else - // ugly, but should compile to single intrinsic unless O0 is set - if (mp_check(sizeof(mp_int_t) == sizeof(long))) { - return mp_clzl((unsigned long)x); +bool mp_mul_ll_overflow(long long int x, long long int y, long long int *res); +bool mp_mul_mp_int_t_overflow(mp_int_t x, mp_int_t y, mp_int_t *res); +static inline bool mp_mul_ull_overflow(unsigned long long int x, unsigned long long int y, unsigned long long int *res) { + if (y > 0 && x > (ULLONG_MAX / y)) { + return true; // overflow + } + *res = x * y; + return false; +} + +#endif + +#if __has_builtin(__builtin_saddll_overflow) || MP_GCC_HAS_BUILTIN_OVERFLOW +#define mp_add_ll_overflow __builtin_saddll_overflow +#else +static inline bool mp_add_ll_overflow(long long int lhs, long long int rhs, long long int *res) { + bool overflow; + + if (rhs > 0) { + overflow = (lhs > LLONG_MAX - rhs); } else { - return mp_clzll((unsigned long long)x); + overflow = (lhs < LLONG_MIN - rhs); } - #endif + + if (!overflow) { + *res = lhs + rhs; + } + + return overflow; } +#endif + +#if __has_builtin(__builtin_ssubll_overflow) || MP_GCC_HAS_BUILTIN_OVERFLOW +#define mp_sub_ll_overflow __builtin_ssubll_overflow +#else +static inline bool mp_sub_ll_overflow(long long int lhs, long long int rhs, long long int *res) { + bool overflow; + + if (rhs > 0) { + overflow = (lhs < LLONG_MIN + rhs); + } else { + overflow = (lhs > LLONG_MAX + rhs); + } + + if (!overflow) { + *res = lhs - rhs; + } + + return overflow; +} +#endif + + +// Helper macros for detecting if sanitizers are enabled +// +// Use sparingly, not for masking issues reported by sanitizers! +// +// Can be detected automatically in Clang and gcc>=14, need to be +// set manually otherwise. +#ifndef MP_UBSAN +#define MP_UBSAN __has_feature(undefined_behavior_sanitizer) +#endif + +#ifndef MP_ASAN +#define MP_ASAN __has_feature(address_sanitizer) +#endif + +#ifndef MP_SANITIZER_BUILD +#define MP_SANITIZER_BUILD (MP_UBSAN || MP_ASAN) +#endif #endif // MICROPY_INCLUDED_PY_MISC_H diff --git a/py/mkrules.cmake b/py/mkrules.cmake index 4374b8b4da3cb..e3d769cc59b5c 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -14,6 +14,9 @@ set(MICROPY_MODULEDEFS "${MICROPY_GENHDR_DIR}/moduledefs.h") set(MICROPY_ROOT_POINTERS_SPLIT "${MICROPY_GENHDR_DIR}/root_pointers.split") set(MICROPY_ROOT_POINTERS_COLLECTED "${MICROPY_GENHDR_DIR}/root_pointers.collected") set(MICROPY_ROOT_POINTERS "${MICROPY_GENHDR_DIR}/root_pointers.h") +set(MICROPY_COMPRESSED_SPLIT "${MICROPY_GENHDR_DIR}/compressed.split") +set(MICROPY_COMPRESSED_COLLECTED "${MICROPY_GENHDR_DIR}/compressed.collected") +set(MICROPY_COMPRESSED_DATA "${MICROPY_GENHDR_DIR}/compressed.data.h") if(NOT MICROPY_PREVIEW_VERSION_2) set(MICROPY_PREVIEW_VERSION_2 0) @@ -32,6 +35,13 @@ if(MICROPY_BOARD) ) endif() +# Need to do this before extracting MICROPY_CPP_DEF below. +if(MICROPY_ROM_TEXT_COMPRESSION) + target_compile_definitions(${MICROPY_TARGET} PUBLIC + MICROPY_ROM_TEXT_COMPRESSION=\(1\) + ) +endif() + # Need to do this before extracting MICROPY_CPP_DEF below. Rest of frozen # manifest handling is at the end of this file. if(MICROPY_FROZEN_MANIFEST) @@ -84,6 +94,18 @@ target_sources(${MICROPY_TARGET} PRIVATE ${MICROPY_ROOT_POINTERS} ) +if(MICROPY_ROM_TEXT_COMPRESSION) + target_sources(${MICROPY_TARGET} PRIVATE + ${MICROPY_COMPRESSED_DATA} + ) +endif() + +# Ensure genhdr directory is removed on clean + +set_property(TARGET ${MICROPY_TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${MICROPY_GENHDR_DIR}" +) + # Command to force the build of another command # Generate mpversion.h @@ -197,11 +219,41 @@ add_custom_command( DEPENDS ${MICROPY_ROOT_POINTERS_COLLECTED} ${MICROPY_PY_DIR}/make_root_pointers.py ) +# Generate compressed.data.h + +add_custom_command( + OUTPUT ${MICROPY_COMPRESSED_SPLIT} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py split compress ${MICROPY_QSTRDEFS_LAST} ${MICROPY_GENHDR_DIR}/compress _ + COMMAND touch ${MICROPY_COMPRESSED_SPLIT} + DEPENDS ${MICROPY_QSTRDEFS_LAST} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_COMPRESSED_COLLECTED} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat compress _ ${MICROPY_GENHDR_DIR}/compress ${MICROPY_COMPRESSED_COLLECTED} + BYPRODUCTS "${MICROPY_COMPRESSED_COLLECTED}.hash" + DEPENDS ${MICROPY_COMPRESSED_SPLIT} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_COMPRESSED_DATA} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makecompresseddata.py ${MICROPY_COMPRESSED_COLLECTED} > ${MICROPY_COMPRESSED_DATA} + DEPENDS ${MICROPY_COMPRESSED_COLLECTED} ${MICROPY_PY_DIR}/makecompresseddata.py +) + # Build frozen code if enabled if(MICROPY_FROZEN_MANIFEST) set(MICROPY_FROZEN_CONTENT "${CMAKE_BINARY_DIR}/frozen_content.c") + set_property(TARGET ${MICROPY_TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${CMAKE_BINARY_DIR}/frozen_mpy" + ) + target_sources(${MICROPY_TARGET} PRIVATE ${MICROPY_FROZEN_CONTENT} ) diff --git a/py/mkrules.mk b/py/mkrules.mk index f364297f0f209..03988996c71ce 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -11,6 +11,7 @@ ifeq ($(MICROPY_PREVIEW_VERSION_2),1) CFLAGS += -DMICROPY_PREVIEW_VERSION_2=1 endif +# CIRCUITPY-CHANGE: point to CircuitPython documentation HELP_BUILD_ERROR ?= "See \033[1;31mhttps://learn.adafruit.com/building-circuitpython; Adafruit Discord \#circuitpython-dev\033[0m" HELP_MPY_LIB_SUBMODULE ?= "\033[1;31mError: micropython-lib submodule is not initialized.\033[0m Run 'make submodules'" @@ -112,6 +113,14 @@ $(BUILD)/%.pp: %.c $(STEPECHO) "PreProcess $<" $(Q)$(CPP) $(CFLAGS) -Wp,-C,-dD,-dI -o $@ $< +.PHONY: $(BUILD)/%.sz +$(BUILD)/%.sz: $(BUILD)/%.o + $(Q)$(SIZE) $< + +# Special case for compiling auto-generated source files. +$(BUILD)/%.o: $(BUILD)/%.c + $(call compile_c) + # The following rule uses | to create an order only prerequisite. Order only # prerequisites only get built if they don't exist. They don't cause timestamp # checking to be performed. @@ -179,7 +188,7 @@ $(HEADER_BUILD)/compressed.collected: $(HEADER_BUILD)/compressed.split # will be created if they don't exist. OBJ_DIRS = $(sort $(dir $(OBJ))) $(OBJ): | $(OBJ_DIRS) -// CIRCUITPY-CHANGE: use $(Q) +# CIRCUITPY-CHANGE: use $(Q) $(OBJ_DIRS): $(Q)$(MKDIR) -p $@ @@ -260,7 +269,7 @@ submodules: $(ECHO) "Updating submodules: $(GIT_SUBMODULES)" ifneq ($(GIT_SUBMODULES),) $(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES) - $(Q)cd $(TOP) && git submodule update --init --filter=blob:none $(GIT_SUBMODULES) || \ + $(Q)cd $(TOP) && git submodule update --init --filter=blob:none $(GIT_SUBMODULES) 2>/dev/null || \ git submodule update --init $(GIT_SUBMODULES) endif .PHONY: submodules diff --git a/py/modio.c b/py/modio.c index d3e563dbcf447..9aeb42d30aa13 100644 --- a/py/modio.c +++ b/py/modio.c @@ -169,12 +169,13 @@ static mp_obj_t bufwriter_flush(mp_obj_t self_in) { int err; mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->len, &err); (void)out_sz; - // TODO: try to recover from a case of non-blocking stream, e.g. move - // remaining chunk to the beginning of buffer. - assert(out_sz == self->len); - self->len = 0; if (err != 0) { mp_raise_OSError(err); + } else { + // TODO: try to recover from a case of non-blocking stream, e.g. move + // remaining chunk to the beginning of buffer. + assert(out_sz == self->len); + self->len = 0; } } diff --git a/py/modmath.c b/py/modmath.c index 2b41bbcd7d15e..2a9ed1b7f2291 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -37,7 +37,7 @@ #define MP_PI_4 MICROPY_FLOAT_CONST(0.78539816339744830962) #define MP_3_PI_4 MICROPY_FLOAT_CONST(2.35619449019234492885) -static NORETURN void math_error(void) { +static MP_NORETURN void math_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("math domain error")); } @@ -99,12 +99,16 @@ mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) { MATH_FUN_1(sqrt, sqrt) // pow(x, y): returns x to the power of y #if MICROPY_PY_MATH_POW_FIX_NAN -mp_float_t pow_func(mp_float_t x, mp_float_t y) { +mp_float_t MICROPY_FLOAT_C_FUN(pow_func)(mp_float_t x, mp_float_t y) { // pow(base, 0) returns 1 for any base, even when base is NaN // pow(+1, exponent) returns 1 for any exponent, even when exponent is NaN if (x == MICROPY_FLOAT_CONST(1.0) || y == MICROPY_FLOAT_CONST(0.0)) { return MICROPY_FLOAT_CONST(1.0); } + // pow(base, NaN) returns NaN for any other value of base + if (isnan(y)) { + return y; + } return MICROPY_FLOAT_C_FUN(pow)(x, y); } MATH_FUN_2(pow, pow_func) @@ -161,6 +165,11 @@ MATH_FUN_2(atan2, atan2) MATH_FUN_1_TO_INT(ceil, ceil) // copysign(x, y) static mp_float_t MICROPY_FLOAT_C_FUN(copysign_func)(mp_float_t x, mp_float_t y) { + #if MICROPY_PY_MATH_COPYSIGN_FIX_NAN + if (isnan(y)) { + y = 0.0; + } + #endif return MICROPY_FLOAT_C_FUN(copysign)(x, y); } MATH_FUN_2(copysign, copysign_func) diff --git a/py/modmicropython.c b/py/modmicropython.c index ff25af8ff7eec..67bb32626c3fa 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -100,7 +100,7 @@ static mp_obj_t mp_micropython_qstr_info(size_t n_args, const mp_obj_t *args) { size_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes; qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes); mp_printf(&mp_plat_print, "qstr pool: n_pool=%u, n_qstr=%u, n_str_data_bytes=%u, n_total_bytes=%u\n", - n_pool, n_qstr, n_str_data_bytes, n_total_bytes); + (uint)n_pool, (uint)n_qstr, (uint)n_str_data_bytes, (uint)n_total_bytes); if (n_args == 1) { // arg given means dump qstr data qstr_dump_data(); diff --git a/py/modsys.c b/py/modsys.c index 2adbc0b7bd66f..bbae93d0c3f78 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -95,8 +95,16 @@ static const MP_DEFINE_STR_OBJ(mp_sys_implementation_machine_obj, MICROPY_BANNER MP_ROM_PTR(&mp_sys_implementation_machine_obj) #if MICROPY_PERSISTENT_CODE_LOAD +// Note: Adding architecture flags to _mpy will break if the flags information +// takes up more bits than what is available in a small-int value. +#if MICROPY_EMIT_RV32 +#include "py/asmrv32.h" +#define MPY_FILE_ARCH_FLAGS (MICROPY_RV32_EXTENSIONS << 16) +#else +#define MPY_FILE_ARCH_FLAGS (0) +#endif #define SYS_IMPLEMENTATION_ELEMS__MPY \ - , MP_ROM_INT(MPY_FILE_HEADER_INT) + , MP_ROM_INT(MPY_FILE_HEADER_INT | MPY_FILE_ARCH_FLAGS) #else #define SYS_IMPLEMENTATION_ELEMS__MPY #endif @@ -113,6 +121,18 @@ static const MP_DEFINE_STR_OBJ(mp_sys_implementation__build_obj, MICROPY_BOARD_B #define SYS_IMPLEMENTATION_ELEMS__BUILD #endif +#if MICROPY_PY_THREAD +#if MICROPY_PY_THREAD_GIL +#define SYS_IMPLEMENTATION_ELEMS__THREAD \ + , MP_ROM_QSTR(MP_QSTR_GIL) +#else +#define SYS_IMPLEMENTATION_ELEMS__THREAD \ + , MP_ROM_QSTR(MP_QSTR_unsafe) +#endif +#else +#define SYS_IMPLEMENTATION_ELEMS__THREAD +#endif + #if MICROPY_PREVIEW_VERSION_2 #define SYS_IMPLEMENTATION_ELEMS__V2 \ , MP_ROM_TRUE @@ -130,6 +150,9 @@ static const qstr impl_fields[] = { #if defined(MICROPY_BOARD_BUILD_NAME) MP_QSTR__build, #endif + #if MICROPY_PY_THREAD + MP_QSTR__thread, + #endif #if MICROPY_PREVIEW_VERSION_2 MP_QSTR__v2, #endif @@ -137,20 +160,21 @@ static const qstr impl_fields[] = { static MP_DEFINE_ATTRTUPLE( mp_sys_implementation_obj, impl_fields, - 3 + MICROPY_PERSISTENT_CODE_LOAD + MICROPY_BOARD_BUILD + MICROPY_PREVIEW_VERSION_2, + 3 + MICROPY_PERSISTENT_CODE_LOAD + MICROPY_BOARD_BUILD + MICROPY_PY_THREAD + MICROPY_PREVIEW_VERSION_2, SYS_IMPLEMENTATION_ELEMS_BASE SYS_IMPLEMENTATION_ELEMS__MPY SYS_IMPLEMENTATION_ELEMS__BUILD + SYS_IMPLEMENTATION_ELEMS__THREAD SYS_IMPLEMENTATION_ELEMS__V2 ); #else static const mp_rom_obj_tuple_t mp_sys_implementation_obj = { {&mp_type_tuple}, 3 + MICROPY_PERSISTENT_CODE_LOAD, - // Do not include SYS_IMPLEMENTATION_ELEMS__BUILD or SYS_IMPLEMENTATION_ELEMS__V2 - // because SYS_IMPLEMENTATION_ELEMS__MPY may be empty if + // Do not include SYS_IMPLEMENTATION_ELEMS__BUILD, SYS_IMPLEMENTATION_ELEMS__THREAD + // or SYS_IMPLEMENTATION_ELEMS__V2 because SYS_IMPLEMENTATION_ELEMS__MPY may be empty if // MICROPY_PERSISTENT_CODE_LOAD is disabled, which means they'll share - // the same index. Cannot query _build or _v2 if MICROPY_PY_ATTRTUPLE is + // the same index. Cannot query _build, _thread or _v2 if MICROPY_PY_ATTRTUPLE is // disabled. { SYS_IMPLEMENTATION_ELEMS_BASE diff --git a/py/mpconfig.h b/py/mpconfig.h index a48958200616b..5b09fc97c9230 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -26,6 +26,14 @@ #ifndef MICROPY_INCLUDED_PY_MPCONFIG_H #define MICROPY_INCLUDED_PY_MPCONFIG_H +#include + +#if defined(__cplusplus) // Required on at least one compiler to get ULLONG_MAX +#include +#else +#include +#endif + // CIRCUITPY-CHANGE // Is this a CircuitPython build? #ifndef CIRCUITPY @@ -40,7 +48,7 @@ // 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 25 +#define MICROPY_VERSION_MINOR 27 #define MICROPY_VERSION_MICRO 0 #define MICROPY_VERSION_PRERELEASE 0 @@ -173,6 +181,78 @@ #define MICROPY_OBJ_IMMEDIATE_OBJS (MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D) #endif +// Definition of the `mp_int_t` and `mp_uint_t` types and associated macros. +// Normally, it suffices for the platform to do nothing: A type as wide +// as a pointer is chosen, unless nanboxing (REPR_D) is selected, in +// which case a 64-bit type is chosen to match the assumed size of +// double-precision floats. +// +// In the case of exceptions, the port, board, or variant must define +// MP_INT_TYPE as MP_INT_TYPE_OTHER and provide all the typedefs and +// defines. +#define MP_INT_TYPE_INTPTR (0) +#define MP_INT_TYPE_INT64 (1) +#define MP_INT_TYPE_OTHER (2) + +#if !defined(MP_INT_TYPE) +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +#define MP_INT_TYPE (MP_INT_TYPE_INT64) +#else +#define MP_INT_TYPE (MP_INT_TYPE_INTPTR) +#endif +#endif + +#if MP_INT_TYPE == MP_INT_TYPE_INTPTR +typedef intptr_t mp_int_t; +typedef uintptr_t mp_uint_t; +#define MP_INT_MAX INTPTR_MAX +#define MP_INT_MIN INTPTR_MIN +#define MP_UINT_MAX INTPTR_UMAX +#elif MP_INT_TYPE == MP_INT_TYPE_INT64 +typedef int64_t mp_int_t; +typedef uint64_t mp_uint_t; +#define MP_INT_MAX INT64_MAX +#define MP_INT_MIN INT64_MIN +#define MP_UINT_MAX INT64_UMAX +#endif + +// mp_printf format support for mp_int_t. In the unusual case that MP_INT_MAX doesn't +// match any of the standard C types (int/long/long long), provide all 3 +// macros. Otherwise, rely on these automatic definitions. +#if !defined(INT_FMT) +#if MP_INT_MAX == INT_MAX +#define INT_FMT "%d" +#define UINT_FMT "%u" +#define HEX_FMT "%x" +#elif MP_INT_MAX == LONG_MAX +#define INT_FMT "%ld" +#define UINT_FMT "%lu" +#define HEX_FMT "%lx" +#elif MP_INT_MAX == LLONG_MAX +#define INT_FMT "%lld" +#define UINT_FMT "%llu" +#define HEX_FMT "%llx" +#else +#error Unexpected MP_INT_MAX value +#endif +#endif + +// mp_printf format support for size_t. In the unusual case that SIZE_MAX doesn't +// match any of the standard C types (int/long/long long), provide a +// macro. Otherwise, rely on these automatic definitions. +#if !defined(SIZE_FMT) +#if SIZE_MAX == UINT_MAX +#define SIZE_FMT "%u" +#elif SIZE_MAX == ULONG_MAX +#define SIZE_FMT "%lu" +#elif SIZE_MAX == ULLONG_MAX +#define SIZE_FMT "%llu" +#else +#error Unexpected SIZE_MAX value +#endif +#endif + + /*****************************************************************************/ /* Memory allocation policy */ @@ -418,6 +498,11 @@ #define MICROPY_EMIT_INLINE_XTENSA (0) #endif +// Whether to support uncommon Xtensa inline assembler opcodes +#ifndef MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES +#define MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES (0) +#endif + // Whether to emit Xtensa-Windowed native code #ifndef MICROPY_EMIT_XTENSAWIN #define MICROPY_EMIT_XTENSAWIN (0) @@ -428,9 +513,9 @@ #define MICROPY_EMIT_RV32 (0) #endif -// CIRCUITPY-CHANGE: make sure MICROPY_EMIT_NATIVE_DEBUG is defined -#ifndef MICROPY_EMIT_NATIVE_DEBUG -#define MICROPY_EMIT_NATIVE_DEBUG (0) +// Whether to emit RISC-V RV32 Zba opcodes in native code +#ifndef MICROPY_EMIT_RV32_ZBA +#define MICROPY_EMIT_RV32_ZBA (0) #endif // Whether to enable the RISC-V RV32 inline assembler @@ -438,6 +523,11 @@ #define MICROPY_EMIT_INLINE_RV32 (0) #endif +// Whether to enable the human-readable native instructions emitter +#ifndef MICROPY_EMIT_NATIVE_DEBUG +#define MICROPY_EMIT_NATIVE_DEBUG (0) +#endif + // Convenience definition for whether any native emitter is enabled #define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN || MICROPY_EMIT_RV32 || MICROPY_EMIT_NATIVE_DEBUG) @@ -497,6 +587,13 @@ #define MICROPY_COMP_CONST (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Whether to enable float constant folding like 1.2+3.4 (when MICROPY_COMP_CONST_FOLDING is also enabled) +// and constant optimisation like id = const(1.2) (when MICROPY_COMP_CONST is also enabled) +// and constant lookup like math.inf (when MICROPY_COMP_MODULE_CONST is also enabled) +#ifndef MICROPY_COMP_CONST_FLOAT +#define MICROPY_COMP_CONST_FLOAT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + // Whether to enable optimisation of: a, b = c, d // Costs 124 bytes (Thumb2) #ifndef MICROPY_COMP_DOUBLE_TUPLE_ASSIGN @@ -727,6 +824,13 @@ #define MICROPY_STACK_CHECK_MARGIN (0) #endif +// The size of a separate stack used for hard IRQ handlers, which should be +// checked instead of the main stack when running a hard callback. 0 implies +// there is no separate ISR stack to check. +#ifndef MICROPY_STACK_SIZE_HARD_IRQ +#define MICROPY_STACK_SIZE_HARD_IRQ (0) +#endif + // Whether to have an emergency exception buffer #ifndef MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0) @@ -896,8 +1000,25 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_COMPLEX (MICROPY_PY_BUILTINS_FLOAT) #endif -#ifndef MICROPY_PY_DOUBLE_TYPECODE -#define MICROPY_PY_DOUBLE_TYPECODE (MICROPY_PY_BUILTINS_FLOAT) +// Float to string conversion implementations +// +// Note that the EXACT method is only available if the compiler supports +// floating points larger than mp_float_t: +// - with MICROPY_FLOAT_IMPL_FLOAT, the compiler needs to support `double` +// - with MICROPY_FLOAT_IMPL_DOUBLE, the compiler needs to support `long double` +// +#define MICROPY_FLOAT_FORMAT_IMPL_BASIC (0) // smallest code, but inexact +#define MICROPY_FLOAT_FORMAT_IMPL_APPROX (1) // slightly bigger, almost perfect +#define MICROPY_FLOAT_FORMAT_IMPL_EXACT (2) // bigger code, and 100% exact repr + +#ifndef MICROPY_FLOAT_FORMAT_IMPL +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define MICROPY_FLOAT_FORMAT_IMPL (MICROPY_FLOAT_FORMAT_IMPL_APPROX) +#elif defined(__SIZEOF_LONG_DOUBLE__) && __SIZEOF_LONG_DOUBLE__ > __SIZEOF_DOUBLE__ +#define MICROPY_FLOAT_FORMAT_IMPL (MICROPY_FLOAT_FORMAT_IMPL_EXACT) +#else +#define MICROPY_FLOAT_FORMAT_IMPL (MICROPY_FLOAT_FORMAT_IMPL_APPROX) +#endif #endif // Whether to use the native _Float16 for 16-bit float support @@ -932,6 +1053,64 @@ typedef double mp_float_t; #define MICROPY_FULL_CHECKS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Ports can choose to use timestamps based on 2000-01-01 or 1970-01-01 +// Default is timestamps based on 2000-01-01 +#if !defined(MICROPY_EPOCH_IS_2000) && !defined(MICROPY_EPOCH_IS_1970) +#define MICROPY_EPOCH_IS_2000 (1) +#define MICROPY_EPOCH_IS_1970 (0) +#elif !defined(MICROPY_EPOCH_IS_1970) +#define MICROPY_EPOCH_IS_1970 (1 - (MICROPY_EPOCH_IS_2000)) +#elif !defined(MICROPY_EPOCH_IS_2000) +#define MICROPY_EPOCH_IS_2000 (1 - (MICROPY_EPOCH_IS_1970)) +#endif + +// To maintain reasonable compatibility with CPython on embedded systems, +// and avoid breaking anytime soon, time functions are defined to work +// at least between 1970 and 2099 (included) on any machine. +// +// Specific ports can enable extended date support +// - after 2099 using MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND +// - before 1970 using MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE +// The largest possible range is year 1600 to year 3000 +// +// By default, extended date support is only enabled for machines using 64 bit pointers, +// but it can be enabled by specific ports +#ifndef MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE +#if MP_SSIZE_MAX > 2147483647 +#define MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE (1) +#else +#define MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE (0) +#endif +#endif + +// When support for dates <1970 is enabled, supporting >=2100 does not cost anything +#ifndef MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND +#define MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND (MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE) +#endif + +// The type to be used to represent platform-specific timestamps depends on the choices above +#define MICROPY_TIMESTAMP_IMPL_LONG_LONG (0) +#define MICROPY_TIMESTAMP_IMPL_UINT (1) +#define MICROPY_TIMESTAMP_IMPL_TIME_T (2) + +#ifndef MICROPY_TIMESTAMP_IMPL +#if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE || MICROPY_EPOCH_IS_2000 +#define MICROPY_TIMESTAMP_IMPL (MICROPY_TIMESTAMP_IMPL_LONG_LONG) +#else +#define MICROPY_TIMESTAMP_IMPL (MICROPY_TIMESTAMP_IMPL_UINT) +#endif +#endif + +// `mp_timestamp_t` is the type that should be used by the port +// to represent timestamps, and is referenced to the platform epoch +#if MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_LONG_LONG +typedef long long mp_timestamp_t; +#elif MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_UINT +typedef mp_uint_t mp_timestamp_t; +#elif MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_TIME_T +typedef time_t mp_timestamp_t; +#endif + // Whether POSIX-semantics non-blocking streams are supported #ifndef MICROPY_STREAMS_NON_BLOCK #define MICROPY_STREAMS_NON_BLOCK (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -943,6 +1122,16 @@ typedef double mp_float_t; #define MICROPY_STREAMS_POSIX_API (0) #endif +// Whether to process __all__ when importing all public symbols from a module. +#ifndef MICROPY_MODULE___ALL__ +#define MICROPY_MODULE___ALL__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES) +#endif + +// Whether to set __file__ on imported modules. +#ifndef MICROPY_MODULE___FILE__ +#define MICROPY_MODULE___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + // Whether modules can use MP_REGISTER_MODULE_DELEGATION() to delegate failed // attribute lookups to a custom handler function. #ifndef MICROPY_MODULE_ATTR_DELEGATION @@ -1030,6 +1219,16 @@ typedef double mp_float_t; #define MICROPY_ENABLE_VM_ABORT (0) #endif +// Whether to handle abort behavior in pyexec code +#ifndef MICROPY_PYEXEC_ENABLE_VM_ABORT +#define MICROPY_PYEXEC_ENABLE_VM_ABORT (0) +#endif + +// Whether to set exit codes according to the exit reason (keyboard interrupt, crash, normal exit, ...) +#ifndef MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING +#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (0) +#endif + // Support for internal scheduler #ifndef MICROPY_ENABLE_SCHEDULER #define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -1065,6 +1264,11 @@ typedef double mp_float_t; #define MICROPY_VFS_POSIX (0) #endif +// Whether to include support for writable POSIX filesystems. +#ifndef MICROPY_VFS_POSIX_WRITABLE +#define MICROPY_VFS_POSIX_WRITABLE (1) +#endif + // Support for VFS FAT component, to mount a FAT filesystem within VFS #ifndef MICROPY_VFS_FAT #define MICROPY_VFS_FAT (0) @@ -1105,7 +1309,15 @@ typedef double mp_float_t; #define MICROPY_PY_FUNCTION_ATTRS_CODE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_FULL_FEATURES) #endif -// Whether to support the descriptors __get__, __set__, __delete__ +// Whether bound_method can just use == (feature disabled), or requires a call to +// mp_obj_equal (feature enabled), to test equality of the self and meth entities. +// This is only needed if objects and functions can be identical without being the +// same thing, eg when using an object proxy. +#ifndef MICROPY_PY_BOUND_METHOD_FULL_EQUALITY_CHECK +#define MICROPY_PY_BOUND_METHOD_FULL_EQUALITY_CHECK (0) +#endif + +// Whether to support the descriptors __get__, __set__, __delete__, __set_name__ // This costs some code size and makes load/store/delete of instance // attributes slower for the classes that use this feature #ifndef MICROPY_PY_DESCRIPTORS @@ -1362,11 +1574,6 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_HELP_MODULES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif -// Whether to set __file__ for imported modules -#ifndef MICROPY_PY___FILE__ -#define MICROPY_PY___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) -#endif - // Whether to provide mem-info related functions in micropython module #ifndef MICROPY_PY_MICROPYTHON_MEM_INFO #define MICROPY_PY_MICROPYTHON_MEM_INFO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -1482,6 +1689,7 @@ typedef double mp_float_t; #endif // Whether to provide fix for pow(1, NaN) and pow(NaN, 0), which both should be 1 not NaN. +// Also fixes pow(base, NaN) to return NaN for other values of base. #ifndef MICROPY_PY_MATH_POW_FIX_NAN #define MICROPY_PY_MATH_POW_FIX_NAN (0) #endif @@ -1518,7 +1726,7 @@ typedef double mp_float_t; // Whether to provide "io.IOBase" class to support user streams #ifndef MICROPY_PY_IO_IOBASE -#define MICROPY_PY_IO_IOBASE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#define MICROPY_PY_IO_IOBASE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif // Whether to provide "io.BytesIO" class @@ -1536,15 +1744,16 @@ typedef double mp_float_t; #define MICROPY_PY_STRUCT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif -// CIRCUITPY-CHANGE -// Whether to provide non-standard typecodes in "struct" module -#ifndef MICROPY_NONSTANDARD_TYPECODES -#define MICROPY_NONSTANDARD_TYPECODES (1) +// Whether struct module provides unsafe and non-standard typecodes O, P, S. +// These typecodes are not in CPython and can cause crashes by accessing arbitrary +// memory. +#ifndef MICROPY_PY_STRUCT_UNSAFE_TYPECODES +#define MICROPY_PY_STRUCT_UNSAFE_TYPECODES (1) #endif // Whether to provide "sys" module #ifndef MICROPY_PY_SYS -#define MICROPY_PY_SYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#define MICROPY_PY_SYS (1) #endif // Whether to initialise "sys.path" and "sys.argv" to their defaults in mp_init() @@ -1800,11 +2009,11 @@ typedef double mp_float_t; #endif #ifndef MICROPY_PY_HASHLIB_MD5 -#define MICROPY_PY_HASHLIB_MD5 (0) +#define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) #endif #ifndef MICROPY_PY_HASHLIB_SHA1 -#define MICROPY_PY_HASHLIB_SHA1 (0) +#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) #endif #ifndef MICROPY_PY_HASHLIB_SHA256 @@ -1812,7 +2021,7 @@ typedef double mp_float_t; #endif #ifndef MICROPY_PY_CRYPTOLIB -#define MICROPY_PY_CRYPTOLIB (0) +#define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) #endif // Depends on MICROPY_PY_CRYPTOLIB @@ -1935,6 +2144,11 @@ typedef double mp_float_t; #define MICROPY_PY_SSL_MBEDTLS_NEED_ACTIVE_CONTEXT (MICROPY_PY_SSL_ECDSA_SIGN_ALT) #endif +// Whether to support DTLS protocol (non-CPython feature) +#ifndef MICROPY_PY_SSL_DTLS +#define MICROPY_PY_SSL_DTLS (MICROPY_SSL_MBEDTLS && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + // Whether to provide the "vfs" module #ifndef MICROPY_PY_VFS #define MICROPY_PY_VFS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES && MICROPY_VFS) @@ -2027,7 +2241,7 @@ typedef double mp_float_t; /*****************************************************************************/ /* Miscellaneous settings */ -// All uPy objects in ROM must be aligned on at least a 4 byte boundary +// All MicroPython objects in ROM must be aligned on at least a 4 byte boundary // so that the small-int/qstr/pointer distinction can be made. For machines // that don't do this (eg 16-bit CPU), define the following macro to something // like __attribute__((aligned(4))). @@ -2166,25 +2380,14 @@ typedef double mp_float_t; #define MP_SSIZE_MAX SSIZE_MAX #endif -// printf format spec to use for mp_int_t and friends -#ifndef INT_FMT -#if defined(__LP64__) -// Archs where mp_int_t == long, long != int -#define UINT_FMT "%lu" -#define INT_FMT "%ld" -#elif defined(_WIN64) -#define UINT_FMT "%llu" -#define INT_FMT "%lld" -#else -// Archs where mp_int_t == int -#define UINT_FMT "%u" -#define INT_FMT "%d" +// Modifier for function which doesn't return +#ifndef MP_NORETURN +#define MP_NORETURN __attribute__((noreturn)) #endif #endif // INT_FMT -// Modifier for function which doesn't return -#ifndef NORETURN -#define NORETURN __attribute__((noreturn)) +#if !MICROPY_PREVIEW_VERSION_2 +#define MP_NORETURN MP_NORETURN #endif // Modifier for weak functions @@ -2279,4 +2482,23 @@ typedef double mp_float_t; #define MP_WARN_CAT(x) (NULL) #endif +// If true, use __builtin_mul_overflow (a gcc intrinsic supported by clang) for +// overflow checking when multiplying two small ints. Otherwise, use a portable +// algorithm. +// +// Most MCUs have a 32x32->64 bit multiply instruction, in which case the +// intrinsic is likely to be faster and generate smaller code. The main exception is +// cortex-m0 with __ARM_ARCH_ISA_THUMB == 1. +// +// The intrinsic is in GCC starting with version 5. +#ifndef MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC +#if defined(__ARM_ARCH_ISA_THUMB) && (__GNUC__ >= 5) +#define MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC (__ARM_ARCH_ISA_THUMB >= 2) +#elif (__GNUC__ >= 5) +#define MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC (1) +#else +#define MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC (0) +#endif +#endif + #endif // MICROPY_INCLUDED_PY_MPCONFIG_H diff --git a/py/mphal.h b/py/mphal.h index 95289ac856cb2..3907641be838a 100644 --- a/py/mphal.h +++ b/py/mphal.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_MPHAL_H #include +#include #include "py/mpconfig.h" #ifdef MICROPY_MPHALPORT_H diff --git a/py/mpprint.c b/py/mpprint.c index e4e25f5a82e43..605b8544f7f62 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -40,8 +40,20 @@ #include "py/formatfloat.h" #endif -static const char pad_spaces[] = " "; -static const char pad_zeroes[] = "0000000000000000"; +static const char pad_spaces[16] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; +#define pad_spaces_size (sizeof(pad_spaces)) +static const char pad_common[23] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '_', '0', '0', '0', ',', '0', '0'}; +// The contents of pad_common is arranged to provide the following padding +// strings with minimal flash size: +// 0000000000000000 <- pad_zeroes +// 0000_000 <- pad_zeroes_underscore (offset: 12, size 5) +// 000,00 <- pad_zeroes_comma (offset: 17, size 4) +#define pad_zeroes (pad_common + 0) +#define pad_zeroes_size (16) +#define pad_zeroes_underscore (pad_common + 12) +#define pad_zeroes_underscore_size (5) +#define pad_zeroes_comma (pad_common + 17) +#define pad_zeroes_comma_size (4) static void plat_print_strn(void *env, const char *str, size_t len) { (void)env; @@ -58,20 +70,37 @@ int mp_print_str(const mp_print_t *print, const char *str) { return len; } -int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width) { +int mp_print_strn(const mp_print_t *print, const char *str, size_t len, unsigned int flags, char fill, int width) { int left_pad = 0; int right_pad = 0; int pad = width - len; int pad_size; int total_chars_printed = 0; const char *pad_chars; + char grouping = flags >> PF_FLAG_SEP_POS; if (!fill || fill == ' ') { pad_chars = pad_spaces; - pad_size = sizeof(pad_spaces) - 1; - } else if (fill == '0') { + pad_size = pad_spaces_size; + } else if (fill == '0' && !grouping) { pad_chars = pad_zeroes; - pad_size = sizeof(pad_zeroes) - 1; + pad_size = pad_zeroes_size; + } else if (fill == '0') { + if (grouping == '_') { + pad_chars = pad_zeroes_underscore; + pad_size = pad_zeroes_underscore_size; + } else { + pad_chars = pad_zeroes_comma; + pad_size = pad_zeroes_comma_size; + } + // The result will never start with a grouping character. An extra leading zero is added. + // width is dead after this so we can use it in calculation + if (width % pad_size == 0) { + pad++; + width++; + } + // position the grouping character correctly within the pad repetition + pad_chars += pad_size - 1 - width % pad_size; } else { // Other pad characters are fairly unusual, so we'll take the hit // and output them 1 at a time. @@ -201,7 +230,7 @@ static int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, return len; } -int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) { +int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, unsigned int base, int base_char, int flags, char fill, int width, int prec) { // These are the only values for "base" that are required to be supported by this // function, since Python only allows the user to format integers in these bases. // If needed this function could be generalised to handle other values. @@ -248,10 +277,7 @@ int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char int prefix_len = prefix - prefix_buf; prefix = prefix_buf; - char comma = '\0'; - if (flags & PF_FLAG_SHOW_COMMA) { - comma = ','; - } + char comma = flags >> PF_FLAG_SEP_POS; // The size of this buffer is rather arbitrary. If it's not large // enough, a dynamic one will be allocated. @@ -340,8 +366,8 @@ int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char } #if MICROPY_PY_BUILTINS_FLOAT -int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec) { - char buf[32]; +int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, unsigned int flags, char fill, int width, int prec) { + char buf[36]; char sign = '\0'; int chrs = 0; @@ -352,11 +378,17 @@ int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, c sign = ' '; } - int len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign); + int len = mp_format_float(f, buf, sizeof(buf) - 3, fmt, prec, sign); char *s = buf; - if ((flags & PF_FLAG_ADD_PERCENT) && (size_t)(len + 1) < sizeof(buf)) { + if ((flags & PF_FLAG_ALWAYS_DECIMAL) && strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) { + buf[len++] = '.'; + buf[len++] = '0'; + buf[len] = '\0'; + } + + if (flags & PF_FLAG_ADD_PERCENT) { buf[len++] = '%'; buf[len] = '\0'; } @@ -424,8 +456,6 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { flags |= PF_FLAG_SHOW_SIGN; } else if (*fmt == ' ') { flags |= PF_FLAG_SPACE_SIGN; - } else if (*fmt == '!') { - flags |= PF_FLAG_NO_TRAILZ; } else if (*fmt == '0') { flags |= PF_FLAG_PAD_AFTER_SIGN; fill = '0'; @@ -459,16 +489,36 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { } } - // parse long specifiers (only for LP64 model where they make a difference) - #ifndef __LP64__ - const + // parse long and long long specifiers (only where they make a difference) + #if (MP_INT_MAX > INT_MAX && MP_INT_MAX == LONG_MAX) || defined(MICROPY_UNIX_COVERAGE) + #define SUPPORT_L_FORMAT (1) + #else + #define SUPPORT_L_FORMAT (0) #endif + #if SUPPORT_L_FORMAT bool long_arg = false; + #endif + + #if (MP_INT_MAX > LONG_MAX) || defined(MICROPY_UNIX_COVERAGE) + #define SUPPORT_LL_FORMAT (1) + #else + #define SUPPORT_LL_FORMAT (0) + #endif + #if SUPPORT_LL_FORMAT + bool long_long_arg = false; + #endif + if (*fmt == 'l') { ++fmt; - #ifdef __LP64__ + #if SUPPORT_L_FORMAT long_arg = true; #endif + #if SUPPORT_LL_FORMAT + if (*fmt == 'l') { + ++fmt; + long_long_arg = true; + } + #endif } if (*fmt == '\0') { @@ -522,6 +572,7 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { chrs += mp_print_strn(print, str, len, flags, fill, width); break; } + // CIRCUITPY-CHANGE: separate from p and P case 'd': { mp_int_t val; if (long_arg) { diff --git a/py/mpprint.h b/py/mpprint.h index e883cc27047f1..614c61ea41537 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -31,13 +31,13 @@ #define PF_FLAG_LEFT_ADJUST (0x001) #define PF_FLAG_SHOW_SIGN (0x002) #define PF_FLAG_SPACE_SIGN (0x004) -#define PF_FLAG_NO_TRAILZ (0x008) -#define PF_FLAG_SHOW_PREFIX (0x010) -#define PF_FLAG_SHOW_COMMA (0x020) -#define PF_FLAG_PAD_AFTER_SIGN (0x040) -#define PF_FLAG_CENTER_ADJUST (0x080) -#define PF_FLAG_ADD_PERCENT (0x100) -#define PF_FLAG_SHOW_OCTAL_LETTER (0x200) +#define PF_FLAG_SHOW_PREFIX (0x008) +#define PF_FLAG_PAD_AFTER_SIGN (0x010) +#define PF_FLAG_CENTER_ADJUST (0x020) +#define PF_FLAG_ADD_PERCENT (0x040) +#define PF_FLAG_SHOW_OCTAL_LETTER (0x080) +#define PF_FLAG_ALWAYS_DECIMAL (0x100) +#define PF_FLAG_SEP_POS (9) // must be above all the above PF_FLAGs #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES #define MP_PYTHON_PRINTER &mp_sys_stdout_print @@ -69,9 +69,9 @@ extern const mp_print_t mp_sys_stdout_print; #endif int mp_print_str(const mp_print_t *print, const char *str); -int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width); +int mp_print_strn(const mp_print_t *print, const char *str, size_t len, unsigned int flags, char fill, int width); #if MICROPY_PY_BUILTINS_FLOAT -int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec); +int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, unsigned int flags, char fill, int width, int prec); #endif int mp_printf(const mp_print_t *print, const char *fmt, ...); diff --git a/py/mpstate.h b/py/mpstate.h index e5e5f8d9fa3d1..7934e843f0651 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -71,6 +71,12 @@ enum { // This structure contains dynamic configuration for the compiler. #if MICROPY_DYNAMIC_COMPILER typedef struct mp_dynamic_compiler_t { + // This is used to let mpy-cross pass options to the emitter chosen with + // `native_arch`. The main use case for the time being is to give the + // RV32 emitter extended information about which extensions can be + // optionally used, in order to generate code that's better suited for the + // hardware platform the code will run on. + void *backend_options; uint8_t small_int_bits; // must be <= host small_int_bits uint8_t native_arch; uint8_t nlr_buf_num_regs; diff --git a/py/mpz.c b/py/mpz.c index 7d8bc03ca8610..74fd175463600 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1541,7 +1541,8 @@ mp_int_t mpz_hash(const mpz_t *z) { mp_uint_t val = 0; mpz_dig_t *d = z->dig + z->len; - while (d-- > z->dig) { + while (d > z->dig) { + d--; val = (val << DIG_SIZE) | *d; } @@ -1556,11 +1557,12 @@ bool mpz_as_int_checked(const mpz_t *i, mp_int_t *value) { mp_uint_t val = 0; mpz_dig_t *d = i->dig + i->len; - while (d-- > i->dig) { + while (d > i->dig) { if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> DIG_SIZE)) { // will overflow return false; } + d--; val = (val << DIG_SIZE) | *d; } @@ -1581,11 +1583,12 @@ bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) { mp_uint_t val = 0; mpz_dig_t *d = i->dig + i->len; - while (d-- > i->dig) { + while (d > i->dig) { if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) { // will overflow return false; } + d--; val = (val << DIG_SIZE) | *d; } @@ -1646,7 +1649,8 @@ mp_float_t mpz_as_float(const mpz_t *i) { mp_float_t val = 0; mpz_dig_t *d = i->dig + i->len; - while (d-- > i->dig) { + while (d > i->dig) { + d--; val = val * DIG_BASE + *d; } @@ -1676,6 +1680,8 @@ size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, ch size_t ilen = i->len; + int n_comma = (base == 10) ? 3 : 4; + char *s = str; if (ilen == 0) { if (prefix) { @@ -1721,7 +1727,7 @@ size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, ch break; } } - if (!done && comma && (s - last_comma) == 3) { + if (!done && comma && (s - last_comma) == n_comma) { *s++ = comma; last_comma = s; } diff --git a/py/nativeglue.h b/py/nativeglue.h index 00ed9f3f4fcb7..01825ac60edb4 100644 --- a/py/nativeglue.h +++ b/py/nativeglue.h @@ -143,7 +143,7 @@ typedef struct _mp_fun_table_t { int (*printf_)(const mp_print_t *print, const char *fmt, ...); int (*vprintf_)(const mp_print_t *print, const char *fmt, va_list args); #if defined(__GNUC__) - NORETURN // Only certain compilers support no-return attributes in function pointer declarations + MP_NORETURN // Only certain compilers support no-return attributes in function pointer declarations #endif // CIRCUITPY-CHANGE: raise_msg_str instead of raise_msg void (*raise_msg_str)(const mp_obj_type_t *exc_type, const char *msg); diff --git a/py/nlr.c b/py/nlr.c index 7ab0c0955a294..de2a38ceff3e9 100644 --- a/py/nlr.c +++ b/py/nlr.c @@ -81,7 +81,7 @@ void nlr_call_jump_callbacks(nlr_buf_t *nlr) { } #if MICROPY_ENABLE_VM_ABORT -NORETURN void nlr_jump_abort(void) { +MP_NORETURN void nlr_jump_abort(void) { MP_STATE_THREAD(nlr_top) = MP_STATE_VM(nlr_abort); nlr_jump(NULL); } diff --git a/py/nlr.h b/py/nlr.h index 340627b7aa1f0..446e78c7ebbf7 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -233,18 +233,18 @@ unsigned int nlr_push(nlr_buf_t *); unsigned int nlr_push_tail(nlr_buf_t *top); void nlr_pop(void); -NORETURN void nlr_jump(void *val); +MP_NORETURN void nlr_jump(void *val); #if MICROPY_ENABLE_VM_ABORT #define nlr_set_abort(buf) MP_STATE_VM(nlr_abort) = buf #define nlr_get_abort() MP_STATE_VM(nlr_abort) -NORETURN void nlr_jump_abort(void); +MP_NORETURN void nlr_jump_abort(void); #endif // This must be implemented by a port. It's called by nlr_jump // if no nlr buf has been pushed. It must not return, but rather // should bail out with a fatal error. -NORETURN void nlr_jump_fail(void *val); +MP_NORETURN void nlr_jump_fail(void *val); // use nlr_raise instead of nlr_jump so that debugging is easier #ifndef MICROPY_DEBUG_NLR diff --git a/py/nlraarch64.c b/py/nlraarch64.c index d6d87ebc50db8..3318004b5e037 100644 --- a/py/nlraarch64.c +++ b/py/nlraarch64.c @@ -56,7 +56,7 @@ __asm( #endif ); -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) MP_STATIC_ASSERT(offsetof(nlr_buf_t, regs) == 16); // asm assumes it diff --git a/py/nlrmips.c b/py/nlrmips.c index cba52b16a266a..5c55db7e268ec 100644 --- a/py/nlrmips.c +++ b/py/nlrmips.c @@ -57,7 +57,7 @@ __asm( ".end nlr_push \n" ); -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm( "move $4, %0 \n" diff --git a/py/nlrpowerpc.c b/py/nlrpowerpc.c index 8a69fe1eeca6b..cf140400e68f5 100644 --- a/py/nlrpowerpc.c +++ b/py/nlrpowerpc.c @@ -78,7 +78,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm__ volatile ( @@ -167,7 +167,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm__ volatile ( diff --git a/py/nlrrv32.c b/py/nlrrv32.c index 9a12ede400daa..565a8629db5f0 100644 --- a/py/nlrrv32.c +++ b/py/nlrrv32.c @@ -50,7 +50,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { ); } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( "add x10, x0, %0 \n" // Load nlr_buf address. diff --git a/py/nlrrv64.c b/py/nlrrv64.c index e7ba79797b857..b7d1467b8f6f6 100644 --- a/py/nlrrv64.c +++ b/py/nlrrv64.c @@ -50,7 +50,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { ); } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( "add x10, x0, %0 \n" // Load nlr_buf address. diff --git a/py/nlrthumb.c b/py/nlrthumb.c index 265052568ffe4..1791d8b49294e 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -104,7 +104,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { #endif } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( diff --git a/py/nlrx64.c b/py/nlrx64.c index d1ad91ff7d718..51224729fc90d 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -100,7 +100,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { #endif } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( diff --git a/py/nlrx86.c b/py/nlrx86.c index 085e30d2034a1..26bf0dc6ccb85 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -78,7 +78,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { #endif } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index ff7af6edeef98..2d1bf35e381a3 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -55,7 +55,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( diff --git a/py/obj.c b/py/obj.c index 29ae76557f8b5..ba3e6a9a9964d 100644 --- a/py/obj.c +++ b/py/obj.c @@ -157,7 +157,7 @@ void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t if (MP_OBJ_TYPE_HAS_SLOT(type, print)) { MP_OBJ_TYPE_GET_SLOT(type, print)((mp_print_t *)print, o_in, kind); } else { - mp_printf(print, "<%q>", type->name); + mp_printf(print, "<%q>", (qstr)type->name); } } @@ -417,6 +417,36 @@ mp_int_t mp_obj_get_int(mp_const_obj_t arg) { return val; } +#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE +mp_uint_t mp_obj_get_uint(mp_const_obj_t arg) { + if (!mp_obj_is_exact_type(arg, &mp_type_int)) { + mp_obj_t as_int = mp_unary_op(MP_UNARY_OP_INT_MAYBE, (mp_obj_t)arg); + if (as_int == MP_OBJ_NULL) { + mp_raise_TypeError_int_conversion(arg); + } + arg = as_int; + } + return mp_obj_int_get_uint_checked(arg); +} + +long long mp_obj_get_ll(mp_const_obj_t arg) { + if (!mp_obj_is_exact_type(arg, &mp_type_int)) { + mp_obj_t as_int = mp_unary_op(MP_UNARY_OP_INT_MAYBE, (mp_obj_t)arg); + if (as_int == MP_OBJ_NULL) { + mp_raise_TypeError_int_conversion(arg); + } + arg = as_int; + } + if (mp_obj_is_small_int(arg)) { + return MP_OBJ_SMALL_INT_VALUE(arg); + } else { + long long res; + mp_obj_int_to_bytes_impl((mp_obj_t)arg, MP_ENDIANNESS_BIG, sizeof(res), (byte *)&res); + return res; + } +} +#endif + mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg) { if (mp_obj_is_int(arg)) { return mp_obj_int_get_truncated(arg); diff --git a/py/obj.h b/py/obj.h index 38db17cba449b..e85505ff3ae70 100644 --- a/py/obj.h +++ b/py/obj.h @@ -184,13 +184,15 @@ static inline bool mp_obj_is_small_int(mp_const_obj_t o) { #define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1)) #if MICROPY_PY_BUILTINS_FLOAT -#define MP_OBJ_NEW_CONST_FLOAT(f) MP_ROM_PTR((mp_obj_t)((((((uint64_t)f) & ~3) | 2) + 0x80800000) & 0xffffffff)) +#include +// note: MP_OBJ_NEW_CONST_FLOAT should be a MP_ROM_PTR but that macro isn't available yet +#define MP_OBJ_NEW_CONST_FLOAT(f) ((mp_obj_t)((((((uint64_t)f) & ~3) | 2) + 0x80800000) & 0xffffffff)) #define mp_const_float_e MP_OBJ_NEW_CONST_FLOAT(0x402df854) #define mp_const_float_pi MP_OBJ_NEW_CONST_FLOAT(0x40490fdb) +#define mp_const_float_nan MP_OBJ_NEW_CONST_FLOAT(0x7fc00000) #if MICROPY_PY_MATH_CONSTANTS #define mp_const_float_tau MP_OBJ_NEW_CONST_FLOAT(0x40c90fdb) #define mp_const_float_inf MP_OBJ_NEW_CONST_FLOAT(0x7f800000) -#define mp_const_float_nan MP_OBJ_NEW_CONST_FLOAT(0xffc00000) #endif static inline bool mp_obj_is_float(mp_const_obj_t o) { @@ -204,9 +206,17 @@ static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { mp_float_t f; mp_uint_t u; } num = {.u = ((mp_uint_t)o - 0x80800000u) & ~3u}; + // Rather than always truncating toward zero, which creates a strong + // bias, copy the two previous bits to fill in the two missing bits. + // This appears to be a pretty good heuristic. + num.u |= (num.u >> 2) & 3u; return num.f; } static inline mp_obj_t mp_obj_new_float(mp_float_t f) { + if (isnan(f)) { + // prevent creation of bad nanboxed pointers via array.array or struct + return mp_const_float_nan; + } union { mp_float_t f; mp_uint_t u; @@ -257,8 +267,10 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { #error MICROPY_OBJ_REPR_D requires MICROPY_FLOAT_IMPL_DOUBLE #endif +#include #define mp_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))} #define mp_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))} +#define mp_const_float_nan {((mp_obj_t)((uint64_t)0x7ff8000000000000 + 0x8004000000000000))} #if MICROPY_PY_MATH_CONSTANTS #define mp_const_float_tau {((mp_obj_t)((uint64_t)0x401921fb54442d18 + 0x8004000000000000))} #define mp_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))} @@ -276,6 +288,13 @@ static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { return num.f; } static inline mp_obj_t mp_obj_new_float(mp_float_t f) { + if (isnan(f)) { + // prevent creation of bad nanboxed pointers via array.array or struct + struct { + uint64_t r; + } num = mp_const_float_nan; + return num.r; + } union { mp_float_t f; uint64_t r; @@ -371,22 +390,22 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; #define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t obj_name = \ - {{&mp_type_fun_builtin_0}, .fun._0 = fun_name} + {.base = {.type = &mp_type_fun_builtin_0}, .fun = {._0 = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t obj_name = \ - {{&mp_type_fun_builtin_1}, .fun._1 = fun_name} + {.base = {.type = &mp_type_fun_builtin_1}, .fun = {._1 = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t obj_name = \ - {{&mp_type_fun_builtin_2}, .fun._2 = fun_name} + {.base = {.type = &mp_type_fun_builtin_2}, .fun = {._2 = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t obj_name = \ - {{&mp_type_fun_builtin_3}, .fun._3 = fun_name} + {.base = {.type = &mp_type_fun_builtin_3}, .fun = {._3 = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) \ const mp_obj_fun_builtin_var_t obj_name = \ - {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, false), .fun.var = fun_name} + {.base = {.type = &mp_type_fun_builtin_var}, .sig = MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, false), .fun = {.var = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) \ const mp_obj_fun_builtin_var_t obj_name = \ - {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, false), .fun.var = fun_name} + {.base = {.type = &mp_type_fun_builtin_var}, .sig = MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, false), .fun = {.var = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, n_args_min, fun_name) \ const mp_obj_fun_builtin_var_t obj_name = \ {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun.kw = fun_name} @@ -516,9 +535,7 @@ static inline bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) { void mp_map_init(mp_map_t *map, size_t n); void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table); -mp_map_t *mp_map_new(size_t n); void mp_map_deinit(mp_map_t *map); -void mp_map_free(mp_map_t *map); mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind); void mp_map_clear(mp_map_t *map); void mp_map_dump(mp_map_t *map); @@ -555,6 +572,10 @@ typedef mp_obj_t (*mp_fun_var_t)(size_t n, const mp_obj_t *); typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); // Flags for type behaviour (mp_obj_type_t.flags) +// If MP_TYPE_FLAG_IS_SUBCLASSED is set, then subclasses of this class have been created. +// Mutations to this class that would require updating all subclasses must be rejected. +// If MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS is set, then attribute lookups involving this +// class need to additionally check for special accessor methods, such as from descriptors. // If MP_TYPE_FLAG_EQ_NOT_REFLEXIVE is clear then __eq__ is reflexive (A==A returns True). // If MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE is clear then the type can't be equal to an // instance of any different class that also clears this flag. If this flag is set @@ -572,6 +593,8 @@ typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); // If MP_TYPE_FLAG_ITER_IS_STREAM is set then the type implicitly gets a "return self" // getiter, and mp_stream_unbuffered_iter for iternext. // If MP_TYPE_FLAG_INSTANCE_TYPE is set then this is an instance type (i.e. defined in Python). +// If MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE is set then the "subscr" slot allows a stack +// allocated slice to be passed in (no references to it will be retained after the call). #define MP_TYPE_FLAG_NONE (0x0000) #define MP_TYPE_FLAG_IS_SUBCLASSED (0x0001) #define MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002) @@ -585,8 +608,9 @@ typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); #define MP_TYPE_FLAG_ITER_IS_CUSTOM (0x0100) #define MP_TYPE_FLAG_ITER_IS_STREAM (MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_ITER_IS_CUSTOM) #define MP_TYPE_FLAG_INSTANCE_TYPE (0x0200) +#define MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE (0x0400) // CIRCUITPY-CHANGE: check for valid types in json dumps -#define MP_TYPE_FLAG_PRINT_JSON (0x0400) +#define MP_TYPE_FLAG_PRINT_JSON (0x0800) typedef enum { PRINT_STR = 0, @@ -827,7 +851,7 @@ typedef struct _mp_obj_full_type_t { #define MP_DEFINE_CONST_OBJ_TYPE_NARGS(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, N, ...) MP_DEFINE_CONST_OBJ_TYPE_NARGS_##N // This macros is used to define a object type in ROM. -// Invoke as MP_DEFINE_CONST_OBJ_TYPE(_typename, _name, _flags, _make_new [, slot, func]*) +// Invoke as MP_DEFINE_CONST_OBJ_TYPE(_typename, _name, _flags, [, slot, func]*) // It uses the number of arguments to select which MP_DEFINE_CONST_OBJ_TYPE_* // macro to use based on the number of arguments. It works by shifting the // numeric values 12, 11, ... 0 by the number of arguments, such that the @@ -927,7 +951,6 @@ extern const mp_obj_type_t mp_type_StopAsyncIteration; extern const mp_obj_type_t mp_type_StopIteration; extern const mp_obj_type_t mp_type_SyntaxError; extern const mp_obj_type_t mp_type_SystemExit; -extern const mp_obj_type_t mp_type_TimeoutError; extern const mp_obj_type_t mp_type_TypeError; extern const mp_obj_type_t mp_type_UnicodeError; extern const mp_obj_type_t mp_type_ValueError; @@ -991,11 +1014,11 @@ void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); // Object allocation macros for allocating objects that have a finaliser. #if MICROPY_ENABLE_FINALISER #define mp_obj_malloc_with_finaliser(struct_type, obj_type) ((struct_type *)mp_obj_malloc_with_finaliser_helper(sizeof(struct_type), obj_type)) -#define mp_obj_malloc_var_with_finaliser(struct_type, var_type, var_num, obj_type) ((struct_type *)mp_obj_malloc_with_finaliser_helper(sizeof(struct_type) + sizeof(var_type) * (var_num), obj_type)) +#define mp_obj_malloc_var_with_finaliser(struct_type, var_field, var_type, var_num, obj_type) ((struct_type *)mp_obj_malloc_with_finaliser_helper(offsetof(struct_type, var_field) + sizeof(var_type) * (var_num), obj_type)) void *mp_obj_malloc_with_finaliser_helper(size_t num_bytes, const mp_obj_type_t *type); #else #define mp_obj_malloc_with_finaliser(struct_type, obj_type) mp_obj_malloc(struct_type, obj_type) -#define mp_obj_malloc_var_with_finaliser(struct_type, var_type, var_num, obj_type) mp_obj_malloc_var(struct_type, var_type, var_num, obj_type) +#define mp_obj_malloc_var_with_finaliser(struct_type, var_field, var_type, var_num, obj_type) mp_obj_malloc_var(struct_type, var_field, var_type, var_num, obj_type) #endif // These macros are derived from more primitive ones and are used to @@ -1032,7 +1055,6 @@ bool mp_obj_is_dict_or_ordereddict(mp_obj_t o); // type check is done on iter method to allow tuple, namedtuple, attrtuple #define mp_obj_is_tuple_compatible(o) (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o), iter) == mp_obj_tuple_getiter) -mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); static inline mp_obj_t mp_obj_new_bool(mp_int_t x) { return x ? mp_const_true : mp_const_false; } @@ -1114,6 +1136,8 @@ static inline bool mp_obj_is_integer(mp_const_obj_t o) { } mp_int_t mp_obj_get_int(mp_const_obj_t arg); +mp_uint_t mp_obj_get_uint(mp_const_obj_t arg); +long long mp_obj_get_ll(mp_const_obj_t arg); mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg); bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value); #if MICROPY_PY_BUILTINS_FLOAT diff --git a/py/objarray.c b/py/objarray.c index 0be1947167d6e..5b63f693698f9 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -126,6 +126,19 @@ static mp_obj_array_t *array_new(char typecode, size_t n) { #endif #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY +static void array_extend_impl(mp_obj_array_t *array, mp_obj_t arg, char typecode, size_t len) { + mp_obj_t iterable = mp_getiter(arg, NULL); + mp_obj_t item; + size_t i = 0; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (len == 0) { + array_append(MP_OBJ_FROM_PTR(array), item); + } else { + mp_binary_set_val_array(typecode, array->items, i++, item); + } + } +} + static mp_obj_t array_construct(char typecode, mp_obj_t initializer) { // bytearrays can be raw-initialised from anything with the buffer protocol // other arrays can only be raw-initialised from bytes and bytearray objects @@ -158,18 +171,7 @@ static mp_obj_t array_construct(char typecode, mp_obj_t initializer) { } mp_obj_array_t *array = array_new(typecode, len); - - mp_obj_t iterable = mp_getiter(initializer, NULL); - mp_obj_t item; - size_t i = 0; - while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - if (len == 0) { - array_append(MP_OBJ_FROM_PTR(array), item); - } else { - mp_binary_set_val_array(typecode, array->items, i++, item); - } - } - + array_extend_impl(array, initializer, typecode, len); return MP_OBJ_FROM_PTR(array); } #endif @@ -493,9 +495,10 @@ static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) { // allow to extend by anything that has the buffer protocol (extension to CPython) mp_buffer_info_t arg_bufinfo; - // CIRCUITPY-CHANGE: allow appending an iterable - if (mp_get_buffer(arg_in, &arg_bufinfo, MP_BUFFER_READ)) { - size_t sz = mp_binary_get_size('@', self->typecode, NULL); + if (!mp_get_buffer(arg_in, &arg_bufinfo, MP_BUFFER_READ)) { + array_extend_impl(self, arg_in, 0, 0); + return mp_const_none; + } // convert byte count to element count size_t len = arg_bufinfo.len / sz; @@ -561,6 +564,8 @@ static mp_obj_t buffer_finder(size_t n_args, const mp_obj_t *args, int direction } else { return MP_OBJ_NEW_SMALL_INT(-1); } + } else { + self->free -= len; } return MP_OBJ_NEW_SMALL_INT(p - (const byte *)haystack_bufinfo.buf); } @@ -667,7 +672,7 @@ static mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value mp_seq_replace_slice_no_grow(dest_items, o->len, slice.start, slice.stop, src_items + src_offs, src_len, item_sz); // CIRCUITPY-CHANGE - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES // Clear "freed" elements at the end of list // TODO: This is actually only needed for typecode=='O' mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz); diff --git a/py/objboundmeth.c b/py/objboundmeth.c index e3503ff154a65..6df67f7bf9e0c 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -102,7 +102,11 @@ static mp_obj_t bound_meth_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_ } mp_obj_bound_meth_t *lhs = MP_OBJ_TO_PTR(lhs_in); mp_obj_bound_meth_t *rhs = MP_OBJ_TO_PTR(rhs_in); + #if MICROPY_PY_BOUND_METHOD_FULL_EQUALITY_CHECK + return mp_obj_new_bool(mp_obj_equal(lhs->self, rhs->self) && mp_obj_equal(lhs->meth, rhs->meth)); + #else return mp_obj_new_bool(lhs->self == rhs->self && lhs->meth == rhs->meth); + #endif } #if MICROPY_PY_FUNCTION_ATTRS diff --git a/py/objcell.c b/py/objcell.c index 95966c7917cb7..5c030c7405a80 100644 --- a/py/objcell.c +++ b/py/objcell.c @@ -30,7 +30,7 @@ static void cell_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_cell_t *o = MP_OBJ_TO_PTR(o_in); - mp_printf(print, "obj); + mp_printf(print, "obj); if (o->obj == MP_OBJ_NULL) { mp_print_str(print, "(nil)"); } else { diff --git a/py/objcode.c b/py/objcode.c index 9b98a696798d4..09904f10f3683 100644 --- a/py/objcode.c +++ b/py/objcode.c @@ -69,6 +69,7 @@ static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_ return consts; } +#if !MICROPY_PREVIEW_VERSION_2 static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { // const mp_bytecode_prelude_t *prelude = &rc->prelude; uint start = 0; @@ -106,6 +107,68 @@ static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { m_del(byte, buffer, buffer_size); return o; } +#endif + +static mp_obj_t code_colines_iter(mp_obj_t); +static mp_obj_t code_colines_next(mp_obj_t); +typedef struct _mp_obj_colines_iter_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + const mp_raw_code_t *rc; + mp_uint_t bc; + mp_uint_t source_line; + const byte *ci; +} mp_obj_colines_iter_t; + +static mp_obj_t code_colines_iter(mp_obj_t self_in) { + mp_obj_code_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_colines_iter_t *iter = mp_obj_malloc(mp_obj_colines_iter_t, &mp_type_polymorph_iter); + iter->iternext = code_colines_next; + iter->rc = self->rc; + iter->bc = 0; + iter->source_line = 1; + iter->ci = self->rc->prelude.line_info; + return MP_OBJ_FROM_PTR(iter); +} +static MP_DEFINE_CONST_FUN_OBJ_1(code_colines_obj, code_colines_iter); + +static mp_obj_t code_colines_next(mp_obj_t iter_in) { + mp_obj_colines_iter_t *iter = MP_OBJ_TO_PTR(iter_in); + const byte *ci_end = iter->rc->prelude.line_info_top; + + mp_uint_t start = iter->bc; + mp_uint_t line_no = iter->source_line; + bool another = true; + + while (another && iter->ci < ci_end) { + another = false; + mp_code_lineinfo_t decoded = mp_bytecode_decode_lineinfo(&iter->ci); + iter->bc += decoded.bc_increment; + iter->source_line += decoded.line_increment; + + if (decoded.bc_increment == 0) { + line_no = iter->source_line; + another = true; + } else if (decoded.line_increment == 0) { + another = true; + } + } + + if (another) { + mp_uint_t prelude_size = (iter->rc->prelude.opcodes - (const byte *)iter->rc->fun_data); + mp_uint_t bc_end = iter->rc->fun_data_len - prelude_size; + if (iter->bc >= bc_end) { + return MP_OBJ_STOP_ITERATION; + } else { + iter->bc = bc_end; + } + } + + mp_uint_t end = iter->bc; + mp_obj_t next[3] = {MP_OBJ_NEW_SMALL_INT(start), MP_OBJ_NEW_SMALL_INT(end), MP_OBJ_NEW_SMALL_INT(line_no)}; + + return mp_obj_new_tuple(MP_ARRAY_SIZE(next), next); +} static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] != MP_OBJ_NULL) { @@ -137,12 +200,18 @@ static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { case MP_QSTR_co_names: dest[0] = MP_OBJ_FROM_PTR(o->dict_locals); break; + #if !MICROPY_PREVIEW_VERSION_2 case MP_QSTR_co_lnotab: if (!o->lnotab) { o->lnotab = raw_code_lnotab(rc); } dest[0] = o->lnotab; break; + #endif + case MP_QSTR_co_lines: + dest[0] = MP_OBJ_FROM_PTR(&code_colines_obj); + dest[1] = self_in; + break; } } @@ -168,7 +237,9 @@ mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t o->context = context; o->rc = rc; o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly? + #if !MICROPY_PREVIEW_VERSION_2 o->lnotab = MP_OBJ_NULL; + #endif return MP_OBJ_FROM_PTR(o); } diff --git a/py/objcode.h b/py/objcode.h index 8db9a34b6e1c9..7be15e23f5271 100644 --- a/py/objcode.h +++ b/py/objcode.h @@ -72,15 +72,16 @@ static inline const void *mp_code_get_proto_fun(mp_obj_code_t *self) { #include "py/emitglue.h" -#define MP_CODE_QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) +#define MP_CODE_QSTR_MAP(context, idx) ((qstr)(context->constants.qstr_table[idx])) typedef struct _mp_obj_code_t { - // TODO this was 4 words mp_obj_base_t base; const mp_module_context_t *context; const mp_raw_code_t *rc; mp_obj_dict_t *dict_locals; + #if !MICROPY_PREVIEW_VERSION_2 mp_obj_t lnotab; + #endif } mp_obj_code_t; mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc, bool result_required); diff --git a/py/objcomplex.c b/py/objcomplex.c index d7287efdc13dd..88f4444f09e4d 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -49,29 +49,18 @@ typedef struct _mp_obj_complex_t { static void complex_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_complex_t *o = MP_OBJ_TO_PTR(o_in); - #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - char buf[16]; - #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C - const int precision = 6; - #else - const int precision = 7; - #endif - #else - char buf[32]; - const int precision = 16; - #endif - if (o->real == 0) { - mp_format_float(o->imag, buf, sizeof(buf), 'g', precision, '\0'); - mp_printf(print, "%sj", buf); + const char *suffix; + int flags = 0; + if (o->real != 0) { + mp_print_str(print, "("); + mp_print_float(print, o->real, 'g', 0, '\0', -1, MP_FLOAT_REPR_PREC); + flags = PF_FLAG_SHOW_SIGN; + suffix = "j)"; } else { - mp_format_float(o->real, buf, sizeof(buf), 'g', precision, '\0'); - mp_printf(print, "(%s", buf); - if (o->imag >= 0 || isnan(o->imag)) { - mp_print_str(print, "+"); - } - mp_format_float(o->imag, buf, sizeof(buf), 'g', precision, '\0'); - mp_printf(print, "%sj)", buf); + suffix = "j"; } + mp_print_float(print, o->imag, 'g', flags, '\0', -1, MP_FLOAT_REPR_PREC); + mp_print_str(print, suffix); } static mp_obj_t complex_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/py/objdict.c b/py/objdict.c index 79a606f09707b..d459d1ae48647 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -101,7 +101,7 @@ static void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ #endif } if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict && kind != PRINT_JSON) { - mp_printf(print, "%q(", self->base.type->name); + mp_printf(print, "%q(", (qstr)self->base.type->name); } mp_print_str(print, "{"); size_t cur = 0; @@ -616,7 +616,8 @@ static mp_obj_t dict_view_unary_op(mp_unary_op_t op, mp_obj_t o_in) { if (op == MP_UNARY_OP_HASH && o->kind == MP_DICT_VIEW_VALUES) { return MP_OBJ_NEW_SMALL_INT((mp_uint_t)o_in); } - return MP_OBJ_NULL; + // delegate all other ops to dict unary op handler + return dict_unary_op(op, o->dict); } static mp_obj_t dict_view_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { diff --git a/py/objfloat.c b/py/objfloat.c index 3610c2b85862d..ecc0f61a6abc9 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -34,6 +34,11 @@ #if MICROPY_PY_BUILTINS_FLOAT +// Workaround a bug in Windows SDK version 10.0.26100.0, where NAN is no longer constant. +#if defined(_MSC_VER) && !defined(_UCRT_NOISY_NAN) +#define _UCRT_NOISY_NAN +#endif + #include #include "py/formatfloat.h" @@ -51,13 +56,6 @@ #define M_PI (3.14159265358979323846) #endif -// Workaround a bug in recent MSVC where NAN is no longer constant. -// (By redefining back to the previous MSVC definition of NAN) -#if defined(_MSC_VER) && _MSC_VER >= 1942 -#undef NAN -#define NAN (-(float)(((float)(1e+300 * 1e+300)) * 0.0F)) -#endif - typedef struct _mp_obj_float_t { mp_obj_base_t base; mp_float_t value; @@ -116,23 +114,7 @@ mp_int_t mp_float_hash(mp_float_t src) { static void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_float_t o_val = mp_obj_float_get(o_in); - #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - char buf[16]; - #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C - const int precision = 6; - #else - const int precision = 7; - #endif - #else - char buf[32]; - const int precision = 16; - #endif - mp_format_float(o_val, buf, sizeof(buf), 'g', precision, '\0'); - mp_print_str(print, buf); - if (strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) { - // Python floats always have decimal point (unless inf or nan) - mp_print_str(print, ".0"); - } + mp_print_float(print, o_val, 'g', PF_FLAG_ALWAYS_DECIMAL, '\0', -1, MP_FLOAT_REPR_PREC); } static mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -324,6 +306,10 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t lhs_val = MICROPY_FLOAT_CONST(1.0); break; } + if (isnan(rhs_val)) { + lhs_val = rhs_val; + break; + } #endif lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); break; diff --git a/py/objint.c b/py/objint.c index b12f09c9d38a7..cabbd7b8f0e12 100644 --- a/py/objint.c +++ b/py/objint.c @@ -117,9 +117,15 @@ static mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val) { } // 8 * sizeof(uintptr_t) counts the number of bits for a small int // TODO provide a way to configure this properly + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B + if (e <= ((8 * sizeof(uintptr_t) + MP_FLOAT_EXP_BIAS - 4) << MP_FLOAT_EXP_SHIFT_I32)) { + return MP_FP_CLASS_FIT_SMALLINT; + } + #else if (e <= ((8 * sizeof(uintptr_t) + MP_FLOAT_EXP_BIAS - 3) << MP_FLOAT_EXP_SHIFT_I32)) { return MP_FP_CLASS_FIT_SMALLINT; } + #endif #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG if (e <= (((sizeof(long long) * MP_BITS_PER_BYTE) + MP_FLOAT_EXP_BIAS - 2) << MP_FLOAT_EXP_SHIFT_I32)) { return MP_FP_CLASS_FIT_LONGINT; @@ -211,7 +217,7 @@ static const uint8_t log_base2_floor[] = { size_t mp_int_format_size(size_t num_bits, int base, const char *prefix, char comma) { assert(2 <= base && base <= 16); size_t num_digits = num_bits / log_base2_floor[base - 1] + 1; - size_t num_commas = comma ? num_digits / 3 : 0; + size_t num_commas = comma ? (base == 10 ? num_digits / 3 : num_digits / 4): 0; size_t prefix_len = prefix ? strlen(prefix) : 0; return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte } @@ -249,10 +255,11 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co char sign = '\0'; if (num < 0) { - num = -num; + num = -(fmt_uint_t)num; sign = '-'; } + int n_comma = (base == 10) ? 3 : 4; size_t needed_size = mp_int_format_size(sizeof(fmt_int_t) * 8, base, prefix, comma); if (needed_size > *buf_size) { *buf = m_new(char, needed_size); @@ -277,7 +284,7 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co c += '0'; } *(--b) = c; - if (comma && num != 0 && b > str && (last_comma - b) == 3) { + if (comma && num != 0 && b > str && (last_comma - b) == n_comma) { *(--b) = comma; last_comma = b; } diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 0fad693c7adcd..4ff6ee6cff8fe 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -43,6 +43,9 @@ const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; #endif +static void raise_long_long_overflow(void) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("result overflows long long storage")); + // CIRCUITPY-CHANGE: bit_length mp_obj_t mp_obj_int_bit_length_impl(mp_obj_t self_in) { assert(mp_obj_is_type(self_in, &mp_type_int)); @@ -132,7 +135,6 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { // small int if the value fits without truncation case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT((mp_int_t)o->val); - case MP_UNARY_OP_POSITIVE: return o_in; case MP_UNARY_OP_NEGATIVE: @@ -159,6 +161,8 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { long long lhs_val; long long rhs_val; + bool overflow = false; + long long result; if (mp_obj_is_small_int(lhs_in)) { lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs_in); @@ -171,21 +175,41 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs_in); } else if (mp_obj_is_exact_type(rhs_in, &mp_type_int)) { rhs_val = ((mp_obj_int_t *)rhs_in)->val; + #if MICROPY_PY_BUILTINS_FLOAT + } else if (mp_obj_is_float(rhs_in)) { + return mp_obj_float_binary_op(op, (mp_float_t)lhs_val, rhs_in); + #endif + #if MICROPY_PY_BUILTINS_COMPLEX + } else if (mp_obj_is_type(rhs_in, &mp_type_complex)) { + return mp_obj_complex_binary_op(op, (mp_float_t)lhs_val, 0, rhs_in); + #endif } else { // delegate to generic function to check for extra cases return mp_obj_int_binary_op_extra_cases(op, lhs_in, rhs_in); } + #if MICROPY_PY_BUILTINS_FLOAT + if (op == MP_BINARY_OP_TRUE_DIVIDE || op == MP_BINARY_OP_INPLACE_TRUE_DIVIDE) { + if (rhs_val == 0) { + goto zero_division; + } + return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val); + } + #endif + switch (op) { case MP_BINARY_OP_ADD: case MP_BINARY_OP_INPLACE_ADD: - return mp_obj_new_int_from_ll(lhs_val + rhs_val); + overflow = mp_add_ll_overflow(lhs_val, rhs_val, &result); + break; case MP_BINARY_OP_SUBTRACT: case MP_BINARY_OP_INPLACE_SUBTRACT: - return mp_obj_new_int_from_ll(lhs_val - rhs_val); + overflow = mp_sub_ll_overflow(lhs_val, rhs_val, &result); + break; case MP_BINARY_OP_MULTIPLY: case MP_BINARY_OP_INPLACE_MULTIPLY: - return mp_obj_new_int_from_ll(lhs_val * rhs_val); + overflow = mp_mul_ll_overflow(lhs_val, rhs_val, &result); + break; case MP_BINARY_OP_FLOOR_DIVIDE: case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: if (rhs_val == 0) { @@ -211,9 +235,21 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i case MP_BINARY_OP_LSHIFT: case MP_BINARY_OP_INPLACE_LSHIFT: - return mp_obj_new_int_from_ll(lhs_val << (int)rhs_val); + if (rhs_val < 0) { + // negative shift not allowed + mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); + } + overflow = rhs_val >= (sizeof(long long) * MP_BITS_PER_BYTE) + || lhs_val > (LLONG_MAX >> rhs_val) + || lhs_val < (LLONG_MIN >> rhs_val); + result = (unsigned long long)lhs_val << rhs_val; + break; case MP_BINARY_OP_RSHIFT: case MP_BINARY_OP_INPLACE_RSHIFT: + if ((int)rhs_val < 0) { + // negative shift not allowed + mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); + } return mp_obj_new_int_from_ll(lhs_val >> (int)rhs_val); case MP_BINARY_OP_POWER: @@ -225,18 +261,18 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i mp_raise_ValueError(MP_ERROR_TEXT("negative power with no float support")); #endif } - long long ans = 1; - while (rhs_val > 0) { + result = 1; + while (rhs_val > 0 && !overflow) { if (rhs_val & 1) { - ans *= lhs_val; + overflow = mp_mul_ll_overflow(result, lhs_val, &result); } - if (rhs_val == 1) { + if (rhs_val == 1 || overflow) { break; } rhs_val /= 2; - lhs_val *= lhs_val; + overflow = mp_mul_ll_overflow(lhs_val, lhs_val, &lhs_val); } - return mp_obj_new_int_from_ll(ans); + break; } case MP_BINARY_OP_LESS: @@ -254,8 +290,14 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i return MP_OBJ_NULL; // op not supported } + if (overflow) { + raise_long_long_overflow(); + } + + return mp_obj_new_int_from_ll(result); + zero_division: - mp_raise_ZeroDivisionError(); + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); } mp_obj_t mp_obj_new_int(mp_int_t value) { @@ -277,22 +319,12 @@ mp_obj_t mp_obj_new_int_from_ll(long long val) { } mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { - // TODO raise an exception if the unsigned long long won't fit if (val >> (sizeof(unsigned long long) * 8 - 1) != 0) { - mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("ulonglong too large")); + raise_long_long_overflow(); } return mp_obj_new_int_from_ll(val); } -mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) { - // TODO this does not honor the given length of the string, but it all cases it should anyway be null terminated - // TODO check overflow - char *endptr; - mp_obj_t result = mp_obj_new_int_from_ll(strtoll(*str, &endptr, base)); - *str = endptr; - return result; -} - mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) { if (mp_obj_is_small_int(self_in)) { return MP_OBJ_SMALL_INT_VALUE(self_in); diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 111f53009fb1f..1bede811a4aaa 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -364,9 +364,10 @@ static mpz_t *mp_mpz_for_int(mp_obj_t arg, mpz_t *temp) { mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { if (!mp_obj_is_int(base) || !mp_obj_is_int(exponent) || !mp_obj_is_int(modulus)) { mp_raise_TypeError(MP_ERROR_TEXT("pow() with 3 arguments requires integers")); + } else if (modulus == MP_OBJ_NEW_SMALL_INT(0)) { + mp_raise_ValueError(MP_ERROR_TEXT("divide by zero")); } else { - mp_obj_t result = mp_obj_new_int_from_ull(0); // Use the _from_ull version as this forces an mpz int - mp_obj_int_t *res_p = (mp_obj_int_t *)MP_OBJ_TO_PTR(result); + mp_obj_int_t *res_p = mp_obj_int_new_mpz(); mpz_t l_temp, r_temp, m_temp; mpz_t *lhs = mp_mpz_for_int(base, &l_temp); @@ -389,7 +390,7 @@ mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { if (mod == &m_temp) { mpz_deinit(mod); } - return result; + return MP_OBJ_FROM_PTR(res_p); } } #endif diff --git a/py/objlist.c b/py/objlist.c index 3137e9fa53483..beb647917d9bd 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -118,7 +118,7 @@ static mp_obj_t list_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } static mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: allow subclassing mp_obj_list_t *o = native_list(lhs); switch (op) { case MP_BINARY_OP_ADD: { @@ -142,7 +142,7 @@ static mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { if (n < 0) { n = 0; } - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: overflow check (PR#1279) size_t new_len = mp_seq_multiply_len(o->len, n); mp_obj_list_t *s = list_new(new_len); mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items); @@ -171,76 +171,66 @@ static mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { } static mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { - // CIRCUITPY-CHANGE + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); - if (value == MP_OBJ_NULL) { - // delete - #if MICROPY_PY_BUILTINS_SLICE - if (mp_obj_is_type(index, &mp_type_slice)) { - mp_bound_slice_t slice; - if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { - mp_raise_NotImplementedError(NULL); + mp_bound_slice_t slice; + bool fast = mp_seq_get_fast_slice_indexes(self->len, index, &slice); + if (value == MP_OBJ_SENTINEL) { + // load + if (!fast) { + return mp_seq_extract_slice(self->items, &slice); } - - mp_int_t len_adj = slice.start - slice.stop; - assert(len_adj <= 0); - mp_seq_replace_slice_no_grow(self->items, self->len, slice.start, slice.stop, self->items /*NULL*/, 0, sizeof(*self->items)); + mp_obj_list_t *res = list_new(slice.stop - slice.start); + mp_seq_copy(res->items, self->items + slice.start, res->len, mp_obj_t); + return MP_OBJ_FROM_PTR(res); + } + // assign/delete + if (value == MP_OBJ_NULL) { + // delete is equivalent to slice assignment of an empty sequence + value = mp_const_empty_tuple; + } + if (!fast) { + mp_raise_NotImplementedError(NULL); + } + size_t value_len; + mp_obj_t *value_items; + mp_obj_get_array(value, &value_len, &value_items); + mp_int_t len_adj = value_len - (slice.stop - slice.start); + if (len_adj > 0) { + if (self->len + len_adj > self->alloc) { + // TODO: Might optimize memory copies here by checking if block can + // be grown inplace or not + self->items = m_renew(mp_obj_t, self->items, self->alloc, self->len + len_adj); + self->alloc = self->len + len_adj; + } + mp_seq_replace_slice_grow_inplace(self->items, self->len, + slice.start, slice.stop, value_items, value_len, len_adj, sizeof(*self->items)); + } else { + mp_seq_replace_slice_no_grow(self->items, self->len, + slice.start, slice.stop, value_items, value_len, sizeof(*self->items)); // Clear "freed" elements at the end of list mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items)); - self->len += len_adj; - return mp_const_none; + // TODO: apply allocation policy re: alloc_size } - #endif - // CIRCUITPY-CHANGE + self->len += len_adj; + return mp_const_none; + } + #endif + if (value == MP_OBJ_NULL) { + // delete + // CIRCUITPY-CHANGE: handle subclassing mp_obj_t args[2] = {MP_OBJ_FROM_PTR(self), index}; list_pop(2, args); return mp_const_none; } else if (value == MP_OBJ_SENTINEL) { // load - #if MICROPY_PY_BUILTINS_SLICE - if (mp_obj_is_type(index, &mp_type_slice)) { - mp_bound_slice_t slice; - if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { - return mp_seq_extract_slice(self->items, &slice); - } - mp_obj_list_t *res = list_new(slice.stop - slice.start); - mp_seq_copy(res->items, self->items + slice.start, res->len, mp_obj_t); - return MP_OBJ_FROM_PTR(res); - } - #endif + // CIRCUITPY-CHANGE: handle subclassing + mp_obj_list_t *self = native_list(self_in); size_t index_val = mp_get_index(self->base.type, self->len, index, false); return self->items[index_val]; } else { - #if MICROPY_PY_BUILTINS_SLICE - if (mp_obj_is_type(index, &mp_type_slice)) { - size_t value_len; - mp_obj_t *value_items; - mp_obj_get_array(value, &value_len, &value_items); - mp_bound_slice_t slice_out; - if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice_out)) { - mp_raise_NotImplementedError(NULL); - } - mp_int_t len_adj = value_len - (slice_out.stop - slice_out.start); - if (len_adj > 0) { - if (self->len + len_adj > self->alloc) { - // TODO: Might optimize memory copies here by checking if block can - // be grown inplace or not - self->items = m_renew(mp_obj_t, self->items, self->alloc, self->len + len_adj); - self->alloc = self->len + len_adj; - } - mp_seq_replace_slice_grow_inplace(self->items, self->len, - slice_out.start, slice_out.stop, value_items, value_len, len_adj, sizeof(*self->items)); - } else { - mp_seq_replace_slice_no_grow(self->items, self->len, - slice_out.start, slice_out.stop, value_items, value_len, sizeof(*self->items)); - // Clear "freed" elements at the end of list - mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items)); - // TODO: apply allocation policy re: alloc_size - } - self->len += len_adj; - return mp_const_none; - } - #endif mp_obj_list_store(self_in, index, value); return mp_const_none; } @@ -252,7 +242,7 @@ static mp_obj_t list_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); if (self->len >= self->alloc) { self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc * 2); @@ -266,7 +256,7 @@ mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { static mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); if (mp_obj_is_type(arg_in, &mp_type_list)) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); mp_obj_list_t *arg = native_list(arg_in); @@ -285,12 +275,13 @@ static mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { return mp_const_none; // return None, as per CPython } -// CIRCUITPY-CHANGE: used elsewhere so not static; impl is different -inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index) { +// CIRCUITPY-CHANGE: provide version for C use outside this file +inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self_in, size_t index) { if (self->len == 0) { // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_IndexError_varg(MP_ERROR_TEXT("pop from empty %q"), MP_QSTR_list); } + size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); mp_obj_t ret = self->items[index]; self->len -= 1; memmove(self->items + index, self->items + index + 1, (self->len - index) * sizeof(mp_obj_t)); @@ -303,18 +294,28 @@ inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index) { return ret; } -// CIRCUITPY-CHANGE static mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { mp_check_self(mp_obj_is_type(args[0], &mp_type_list)); mp_obj_list_t *self = native_list(args[0]); size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); + // CIRCUITPY-CHANGE: use factored-out pop code return mp_obj_list_pop(self, index); } +// "head" is actually the *exclusive lower bound* of the range to sort. That is, +// the first element to be sorted is `head[1]`, not `head[0]`. Similarly `tail` +// is an *inclusive upper bound* of the range to sort. That is, the final +// element to sort is `tail[0]`, not `tail[-1]`. +// +// The pivot element is always chosen as `tail[0]`. +// +// These unusual choices allows structuring the partitioning +// process as a do/while loop, which generates smaller code than the equivalent +// code with usual C bounds & a while or for loop. static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, mp_obj_t binop_less_result) { mp_cstack_check(); - while (head < tail) { - mp_obj_t *h = head - 1; + while (tail - head > 1) { // So long as at least 2 elements remain + mp_obj_t *h = head; mp_obj_t *t = tail; mp_obj_t v = key_fn == MP_OBJ_NULL ? tail[0] : mp_call_function_1(key_fn, tail[0]); // get pivot using key_fn for (;;) { @@ -325,19 +326,21 @@ static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, mp_obj if (h >= t) { break; } + // A pair of objects must be swapped to the other side of the partition mp_obj_t x = h[0]; h[0] = t[0]; t[0] = x; } + // Place the pivot element in the proper position mp_obj_t x = h[0]; h[0] = tail[0]; tail[0] = x; // do the smaller recursive call first, to keep stack within O(log(N)) - if (t - head < tail - h - 1) { + if (t - head < tail - h) { mp_quicksort(head, t, key_fn, binop_less_result); - head = h + 1; + head = h; } else { - mp_quicksort(h + 1, tail, key_fn, binop_less_result); + mp_quicksort(h, tail, key_fn, binop_less_result); tail = t; } } @@ -362,7 +365,7 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ mp_obj_list_t *self = native_list(pos_args[0]); if (self->len > 1) { - mp_quicksort(self->items, self->items + self->len - 1, + mp_quicksort(self->items - 1, self->items + self->len - 1, args.key.u_obj == mp_const_none ? MP_OBJ_NULL : args.key.u_obj, args.reverse.u_bool ? mp_const_false : mp_const_true); } @@ -370,10 +373,10 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ return mp_const_none; } -// CIRCUITPY-CHANGE: used elsewhere so not static +// CIRCUITPY-CHANGE: rename and make not static for use elsewhere mp_obj_t mp_obj_list_clear(mp_obj_t self_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); self->len = 0; self->items = m_renew(mp_obj_t, self->items, self->alloc, LIST_MIN_ALLOC); @@ -384,27 +387,28 @@ mp_obj_t mp_obj_list_clear(mp_obj_t self_in) { static mp_obj_t list_copy(mp_obj_t self_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); return mp_obj_new_list(self->len, self->items); } static mp_obj_t list_count(mp_obj_t self_in, mp_obj_t value) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); return mp_seq_count_obj(self->items, self->len, value); } static mp_obj_t list_index(size_t n_args, const mp_obj_t *args) { mp_check_self(mp_obj_is_type(args[0], &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(args[0]); return mp_seq_index_obj(self->items, self->len, n_args, args); } -// CIRCUITPY-CHANGE: used elsewhere so not static -inline void mp_obj_list_insert(mp_obj_list_t *self, size_t index, mp_obj_t obj) { +// CIRCUITPY-CHANGE: factor out for C use elsewhere; not meant to emulate Python API +inline void mp_obj_list_insert(mp_obj_list_t *self_in, size_t index, mp_obj_t obj) { + mp_obj_list_t *self = native_list(self_in); mp_obj_list_append(MP_OBJ_FROM_PTR(self), mp_const_none); for (size_t i = self->len - 1; i > index; --i) { @@ -428,7 +432,7 @@ static mp_obj_t list_insert(mp_obj_t self_in, mp_obj_t idx, mp_obj_t obj) { if ((size_t)index > self->len) { index = self->len; } - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: use factored-out insert code mp_obj_list_insert(self, index, obj); return mp_const_none; } diff --git a/py/objmodule.c b/py/objmodule.c index a5c1dee968ea8..60b0368abac58 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -34,11 +34,6 @@ #include "py/runtime.h" #include "py/builtin.h" -// CIRCUITPY-CHANGE -#if CIRCUITPY_WARNINGS -#include "shared-module/warnings/__init__.h" -#endif - static void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); @@ -49,7 +44,7 @@ static void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin module_name = mp_obj_str_get_str(elem->value); } - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ // If we store __file__ to imported modules then try to lookup this // symbol to give more information about the module. elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___file__), MP_MAP_LOOKUP); @@ -157,11 +152,13 @@ static const mp_rom_map_elem_t mp_builtin_module_table[] = { }; MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table); +#if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES static const mp_rom_map_elem_t mp_builtin_extensible_module_table[] = { // built-in modules declared with MP_REGISTER_EXTENSIBLE_MODULE() MICROPY_REGISTERED_EXTENSIBLE_MODULES }; MP_DEFINE_CONST_MAP(mp_builtin_extensible_module_map, mp_builtin_extensible_module_table); +#endif #if MICROPY_MODULE_ATTR_DELEGATION && defined(MICROPY_MODULE_DELEGATIONS) typedef struct _mp_module_delegation_entry_t { @@ -178,13 +175,13 @@ static const mp_module_delegation_entry_t mp_builtin_module_delegation_table[] = // Attempts to find (and initialise) a built-in, otherwise returns // MP_OBJ_NULL. mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible) { - // CIRCUITPY-CHANGE - #if CIRCUITPY_PARALLELDISPLAYBUS && CIRCUITPY_WARNINGS - if (module_name == MP_QSTR_paralleldisplay) { - warnings_warn(&mp_type_FutureWarning, MP_ERROR_TEXT("%q renamed %q"), MP_QSTR_paralleldisplay, MP_QSTR_paralleldisplaybus); - } + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES + const mp_map_t *map = extensible ? &mp_builtin_extensible_module_map : &mp_builtin_module_map; + #else + const mp_map_t *map = &mp_builtin_module_map; #endif - mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)(extensible ? &mp_builtin_extensible_module_map : &mp_builtin_module_map), MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); + mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); + if (!elem) { #if MICROPY_PY_SYS // Special case for sys, which isn't extensible but can always be @@ -194,6 +191,7 @@ mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible) { } #endif + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES if (extensible) { // At this point we've already tried non-extensible built-ins, the // filesystem, and now extensible built-ins. No match, so fail diff --git a/py/objmodule.h b/py/objmodule.h index 8b14cd9fc3d30..78e4da3cac690 100644 --- a/py/objmodule.h +++ b/py/objmodule.h @@ -35,7 +35,10 @@ #endif extern const mp_map_t mp_builtin_module_map; + +#if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES extern const mp_map_t mp_builtin_extensible_module_map; +#endif mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible); diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index b6bbe6e05d8b2..d687dbfda59d6 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -73,7 +73,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(namedtuple_asdict_obj, namedtuple_asdict); void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_namedtuple_t *o = MP_OBJ_TO_PTR(o_in); - mp_printf(print, "%q", o->tuple.base.type->name); + mp_printf(print, "%q", (qstr)o->tuple.base.type->name); const qstr *fields = ((mp_obj_namedtuple_type_t *)o->tuple.base.type)->fields; mp_obj_attrtuple_print_helper(print, fields, &o->tuple); } diff --git a/py/objrange.c b/py/objrange.c index bde2ebaabb4ed..dea9fe5f2963a 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -41,9 +41,9 @@ typedef struct _mp_obj_range_it_t { static mp_obj_t range_it_iternext(mp_obj_t o_in) { mp_obj_range_it_t *o = MP_OBJ_TO_PTR(o_in); if ((o->step > 0 && o->cur < o->stop) || (o->step < 0 && o->cur > o->stop)) { - mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT(o->cur); + mp_int_t cur = o->cur; o->cur += o->step; - return o_out; + return mp_obj_new_int(cur); } else { return MP_OBJ_STOP_ITERATION; } @@ -133,7 +133,7 @@ static mp_obj_t range_unary_op(mp_unary_op_t op, mp_obj_t self_in) { case MP_UNARY_OP_BOOL: return mp_obj_new_bool(len > 0); case MP_UNARY_OP_LEN: - return MP_OBJ_NEW_SMALL_INT(len); + return mp_obj_new_int(len); default: return MP_OBJ_NULL; // op not supported } @@ -165,20 +165,16 @@ static mp_obj_t range_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { #if MICROPY_PY_BUILTINS_SLICE if (mp_obj_is_type(index, &mp_type_slice)) { mp_bound_slice_t slice; - mp_seq_get_fast_slice_indexes(len, index, &slice); + mp_obj_slice_indices(index, len, &slice); mp_obj_range_t *o = mp_obj_malloc(mp_obj_range_t, &mp_type_range); o->start = self->start + slice.start * self->step; o->stop = self->start + slice.stop * self->step; o->step = slice.step * self->step; - if (slice.step < 0) { - // Negative slice steps have inclusive stop, so adjust for exclusive - o->stop -= self->step; - } return MP_OBJ_FROM_PTR(o); } #endif size_t index_val = mp_get_index(self->base.type, len, index, false); - return MP_OBJ_NEW_SMALL_INT(self->start + index_val * self->step); + return mp_obj_new_int(self->start + index_val * self->step); } else { return MP_OBJ_NULL; // op not supported } diff --git a/py/objringio.c b/py/objringio.c index ba1ec25307ea4..0025b26be3b07 100644 --- a/py/objringio.c +++ b/py/objringio.c @@ -39,22 +39,19 @@ typedef struct _micropython_ringio_obj_t { static mp_obj_t micropython_ringio_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - mp_int_t buff_size = -1; mp_buffer_info_t bufinfo = {NULL, 0, 0}; if (!mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) { - buff_size = mp_obj_get_int(args[0]); + bufinfo.len = mp_obj_get_int(args[0]) + 1; + bufinfo.buf = m_new(uint8_t, bufinfo.len); } - micropython_ringio_obj_t *self = mp_obj_malloc(micropython_ringio_obj_t, type); - if (bufinfo.buf != NULL) { - // buffer passed in, use it directly for ringbuffer. - self->ringbuffer.buf = bufinfo.buf; - self->ringbuffer.size = bufinfo.len; - self->ringbuffer.iget = self->ringbuffer.iput = 0; - } else { - // Allocate new buffer, add one extra to buff_size as ringbuf consumes one byte for tracking. - ringbuf_alloc(&(self->ringbuffer), buff_size + 1); + if (bufinfo.len < 2 || bufinfo.len > UINT16_MAX) { + mp_raise_ValueError(NULL); } + micropython_ringio_obj_t *self = mp_obj_malloc(micropython_ringio_obj_t, type); + self->ringbuffer.buf = bufinfo.buf; + self->ringbuffer.size = bufinfo.len; + self->ringbuffer.iget = self->ringbuffer.iput = 0; return MP_OBJ_FROM_PTR(self); } diff --git a/py/objstr.c b/py/objstr.c index 1874cdb01d03d..bd650bbe18ad6 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -33,6 +33,7 @@ #include "py/objlist.h" #include "py/runtime.h" #include "py/cstack.h" +#include "py/objtuple.h" // CIRCUITPY-CHANGE const char nibble_to_hex_upper[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', @@ -46,7 +47,7 @@ static mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ #endif static mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); -static NORETURN void bad_implicit_conversion(mp_obj_t self_in); +static MP_NORETURN void bad_implicit_conversion(mp_obj_t self_in); static mp_obj_t mp_obj_new_str_type_from_vstr(const mp_obj_type_t *type, vstr_t *vstr); @@ -372,8 +373,7 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i mp_obj_t *args = &rhs_in; size_t n_args = 1; mp_obj_t dict = MP_OBJ_NULL; - if (mp_obj_is_type(rhs_in, &mp_type_tuple)) { - // TODO: Support tuple subclasses? + if (mp_obj_is_tuple_compatible(rhs_in)) { mp_obj_tuple_get(rhs_in, &n_args, &args); } else if (mp_obj_is_type(rhs_in, &mp_type_dict)) { dict = rhs_in; @@ -1018,7 +1018,7 @@ static mp_obj_t arg_as_int(mp_obj_t arg) { #endif #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE -static NORETURN void terse_str_format_value_error(void) { +static MP_NORETURN void terse_str_format_value_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("bad format string")); } #else @@ -1205,7 +1205,7 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar int width = -1; int precision = -1; char type = '\0'; - int flags = 0; + unsigned int flags = 0; if (format_spec) { // The format specifier (from http://docs.python.org/2/library/string.html#formatspec) @@ -1250,8 +1250,9 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } } s = str_to_int(s, stop, &width); - if (*s == ',') { - flags |= PF_FLAG_SHOW_COMMA; + if (*s == ',' || *s == '_') { + MP_STATIC_ASSERT((unsigned)'_' << PF_FLAG_SEP_POS >> PF_FLAG_SEP_POS == '_'); + flags |= (unsigned)*s << PF_FLAG_SEP_POS; s++; } if (*s == '.') { @@ -1324,7 +1325,7 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } case '\0': // No explicit format type implies 'd' - case 'n': // I don't think we support locales in uPy so use 'd' + case 'n': // I don't think we support locales in MicroPython so use 'd' case 'd': mp_print_mp_int(&print, arg, 10, 'a', flags, fill, width, 0); continue; @@ -2395,7 +2396,7 @@ bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) { } } -static NORETURN void bad_implicit_conversion(mp_obj_t self_in) { +static MP_NORETURN void bad_implicit_conversion(mp_obj_t self_in) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("can't convert to str implicitly")); #else diff --git a/py/objtuple.c b/py/objtuple.c index e0b31edaf2376..b2ccbbb299082 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -31,9 +31,6 @@ #include "py/objtuple.h" #include "py/runtime.h" -// type check is done on getiter method to allow tuple, namedtuple, attrtuple -#define mp_obj_is_tuple_compatible(o) (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o), iter) == mp_obj_tuple_getiter) - /******************************************************************************/ /* tuple */ diff --git a/py/objtuple.h b/py/objtuple.h index e2d010e6c5a3d..783522a6222b5 100644 --- a/py/objtuple.h +++ b/py/objtuple.h @@ -57,7 +57,7 @@ extern const mp_obj_type_t mp_type_attrtuple; #define MP_DEFINE_ATTRTUPLE(tuple_obj_name, fields, nitems, ...) \ const mp_rom_obj_tuple_t tuple_obj_name = { \ - .base = {&mp_type_attrtuple}, \ + .base = {.type = &mp_type_attrtuple}, \ .len = nitems, \ .items = { __VA_ARGS__, MP_ROM_PTR((void *)fields) } \ } @@ -68,4 +68,7 @@ void mp_obj_attrtuple_print_helper(const mp_print_t *print, const qstr *fields, mp_obj_t mp_obj_new_attrtuple(const qstr *fields, size_t n, const mp_obj_t *items); +// type check is done on getiter method to allow tuple, namedtuple, attrtuple +#define mp_obj_is_tuple_compatible(o) (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o), iter) == mp_obj_tuple_getiter) + #endif // MICROPY_INCLUDED_PY_OBJTUPLE_H diff --git a/py/objtype.c b/py/objtype.c index 074d6f39293db..83bafa14e8839 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -44,6 +44,7 @@ #define ENABLE_SPECIAL_ACCESSORS \ (MICROPY_PY_DESCRIPTORS || MICROPY_PY_DELATTR_SETATTR || MICROPY_PY_BUILTINS_PROPERTY) +static mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); static mp_obj_t mp_obj_is_subclass(mp_obj_t object, mp_obj_t classinfo); static mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); @@ -690,8 +691,8 @@ static void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des // try __getattr__ if (attr != MP_QSTR___getattr__) { #if MICROPY_PY_DESCRIPTORS - // With descriptors enabled, don't delegate lookups of __get__/__set__/__delete__. - if (attr == MP_QSTR___get__ || attr == MP_QSTR___set__ || attr == MP_QSTR___delete__) { + // With descriptors enabled, don't delegate lookups of __get__/__set__/__delete__/__set_name__. + if (attr == MP_QSTR___get__ || attr == MP_QSTR___set__ || attr == MP_QSTR___delete__ || attr == MP_QSTR___set_name__) { return; } #endif @@ -996,7 +997,7 @@ static bool check_for_special_accessors(mp_obj_t key, mp_obj_t value) { #endif #if MICROPY_PY_DESCRIPTORS static const uint8_t to_check[] = { - MP_QSTR___get__, MP_QSTR___set__, MP_QSTR___delete__, + MP_QSTR___get__, MP_QSTR___set__, MP_QSTR___delete__, // not needed for MP_QSTR___set_name__ though }; for (size_t i = 0; i < MP_ARRAY_SIZE(to_check); ++i) { mp_obj_t dest_temp[2]; @@ -1010,10 +1011,52 @@ static bool check_for_special_accessors(mp_obj_t key, mp_obj_t value) { } #endif +#if MICROPY_PY_DESCRIPTORS +// Shared data layout for the __set_name__ call and a linked list of calls to be made. +typedef union _setname_list_t setname_list_t; +union _setname_list_t { + mp_obj_t call[4]; + struct { + mp_obj_t _meth; + mp_obj_t _self; + setname_list_t *next; // can use the "owner" argument position temporarily for the linked list + mp_obj_t _name; + }; +}; + +// Append any `__set_name__` method on `value` to the setname list, with its per-attr args +static setname_list_t *setname_maybe_bind_append(setname_list_t *tail, mp_obj_t name, mp_obj_t value) { + // make certain our type-punning is safe: + MP_STATIC_ASSERT_NONCONSTEXPR(offsetof(setname_list_t, next) == offsetof(setname_list_t, call[2])); + + // tail is a blank list entry + mp_load_method_maybe(value, MP_QSTR___set_name__, tail->call); + if (tail->call[1] != MP_OBJ_NULL) { + // Each time a __set_name__ is found, leave it in-place in the former tail and allocate a new tail + tail->next = m_new_obj(setname_list_t); + tail->next->next = NULL; + tail->call[3] = name; + return tail->next; + } else { + return tail; + } +} + +// Execute the captured `__set_name__` calls, destroying the setname list in the process. +static inline void setname_consume_call_all(setname_list_t *head, mp_obj_t owner) { + setname_list_t *next; + while ((next = head->next) != NULL) { + head->call[2] = owner; + mp_call_method_n_kw(2, 0, head->call); + head = next; + } +} +#endif + static void type_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "", self->name); + mp_printf(print, "", (qstr)self->name); } static mp_obj_t type_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -1159,7 +1202,7 @@ MP_DEFINE_CONST_OBJ_TYPE( attr, type_attr ); -mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { +static mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { // Verify input objects have expected type if (!mp_obj_is_type(bases_tuple, &mp_type_tuple)) { mp_raise_TypeError(NULL); @@ -1252,20 +1295,38 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) } } + #if MICROPY_PY_DESCRIPTORS + // To avoid any dynamic allocations when no __set_name__ exists, + // the head of this list is kept on the stack (marked blank with `next = NULL`). + setname_list_t setname_list = { .next = NULL }; + setname_list_t *setname_tail = &setname_list; + #endif + #if ENABLE_SPECIAL_ACCESSORS - // Check if the class has any special accessor methods - if (!(o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS)) { - for (size_t i = 0; i < locals_ptr->map.alloc; i++) { - if (mp_map_slot_is_filled(&locals_ptr->map, i)) { - const mp_map_elem_t *elem = &locals_ptr->map.table[i]; - if (check_for_special_accessors(elem->key, elem->value)) { - o->flags |= MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; - break; - } + // Check if the class has any special accessor methods, + // and accumulate bound __set_name__ methods that need to be called + for (size_t i = 0; i < locals_ptr->map.alloc; i++) { + #if !MICROPY_PY_DESCRIPTORS + // __set_name__ needs to scan the entire locals map, can't early-terminate + if (o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS) { + break; + } + #endif + + if (mp_map_slot_is_filled(&locals_ptr->map, i)) { + const mp_map_elem_t *elem = &locals_ptr->map.table[i]; + + if (!(o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS) // elidable when the early-termination check is enabled + && check_for_special_accessors(elem->key, elem->value)) { + o->flags |= MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; } + + #if MICROPY_PY_DESCRIPTORS + setname_tail = setname_maybe_bind_append(setname_tail, elem->key, elem->value); + #endif } } - #endif + #endif // ENABLE_SPECIAL_ACCESSORS const mp_obj_type_t *native_base; size_t num_native_bases = instance_count_native_bases(o, &native_base); @@ -1273,8 +1334,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) mp_raise_TypeError(MP_ERROR_TEXT("multiple bases have instance lay-out conflict")); } - mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(o, locals_dict)->map; - mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(MP_QSTR___new__), MP_MAP_LOOKUP); + mp_map_elem_t *elem = mp_map_lookup(&locals_ptr->map, MP_OBJ_NEW_QSTR(MP_QSTR___new__), MP_MAP_LOOKUP); if (elem != NULL) { // __new__ slot exists; check if it is a function if (mp_obj_is_fun(elem->value)) { @@ -1283,6 +1343,10 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) } } + #if MICROPY_PY_DESCRIPTORS + setname_consume_call_all(&setname_list, MP_OBJ_FROM_PTR(o)); + #endif + return MP_OBJ_FROM_PTR(o); } diff --git a/py/parse.c b/py/parse.c index a393b0ee8c153..36150ef468bdf 100644 --- a/py/parse.c +++ b/py/parse.c @@ -341,18 +341,34 @@ static uint8_t peek_rule(parser_t *parser, size_t n) { } #endif -bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { +#if MICROPY_COMP_CONST_FOLDING || MICROPY_EMIT_INLINE_ASM +static bool mp_parse_node_get_number_maybe(mp_parse_node_t pn, mp_obj_t *o) { if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { *o = MP_OBJ_NEW_SMALL_INT(MP_PARSE_NODE_LEAF_SMALL_INT(pn)); return true; } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; *o = mp_parse_node_extract_const_object(pns); - return mp_obj_is_int(*o); + return mp_obj_is_int(*o) + #if MICROPY_COMP_CONST_FLOAT + || mp_obj_is_float(*o) + #endif + ; } else { return false; } } +#endif + +#if MICROPY_EMIT_INLINE_ASM +bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { + return mp_parse_node_get_number_maybe(pn, o) + #if MICROPY_COMP_CONST_FLOAT + && mp_obj_is_int(*o) + #endif + ; +} +#endif #if MICROPY_COMP_CONST_TUPLE || MICROPY_COMP_CONST static bool mp_parse_node_is_const(mp_parse_node_t pn) { @@ -647,12 +663,32 @@ static const mp_rom_map_elem_t mp_constants_table[] = { #if MICROPY_PY_UCTYPES { MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) }, #endif + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH && MICROPY_COMP_CONST_FLOAT + { MP_ROM_QSTR(MP_QSTR_math), MP_ROM_PTR(&mp_module_math) }, + #endif // Extra constants as defined by a port MICROPY_PORT_CONSTANTS }; static MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); #endif +static bool binary_op_maybe(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs, mp_obj_t *res) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t tmp = mp_binary_op(op, lhs, rhs); + nlr_pop(); + #if MICROPY_PY_BUILTINS_COMPLEX + if (mp_obj_is_type(tmp, &mp_type_complex)) { + return false; + } + #endif + *res = tmp; + return true; + } else { + return false; + } +} + static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *num_args) { if (rule_id == RULE_or_test || rule_id == RULE_and_test) { @@ -711,7 +747,7 @@ static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *nu } static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { - // this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4 + // this code does folding of arbitrary numeric expressions, eg 1 + 2 * 3 + 4 // it does not do partial folding, eg 1 + 2 + x -> 3 + x mp_obj_t arg0; @@ -721,7 +757,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { || rule_id == RULE_power) { // folding for binary ops: | ^ & ** mp_parse_node_t pn = peek_result(parser, num_args - 1); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } mp_binary_op_t op; @@ -737,58 +773,45 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { for (ssize_t i = num_args - 2; i >= 0; --i) { pn = peek_result(parser, i); mp_obj_t arg1; - if (!mp_parse_node_get_int_maybe(pn, &arg1)) { + if (!mp_parse_node_get_number_maybe(pn, &arg1)) { return false; } - if (op == MP_BINARY_OP_POWER && mp_obj_int_sign(arg1) < 0) { - // ** can't have negative rhs + if (!binary_op_maybe(op, arg0, arg1, &arg0)) { return false; } - arg0 = mp_binary_op(op, arg0, arg1); } } else if (rule_id == RULE_shift_expr || rule_id == RULE_arith_expr || rule_id == RULE_term) { // folding for binary ops: << >> + - * @ / % // mp_parse_node_t pn = peek_result(parser, num_args - 1); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } for (ssize_t i = num_args - 2; i >= 1; i -= 2) { pn = peek_result(parser, i - 1); mp_obj_t arg1; - if (!mp_parse_node_get_int_maybe(pn, &arg1)) { + if (!mp_parse_node_get_number_maybe(pn, &arg1)) { return false; } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, i)); - if (tok == MP_TOKEN_OP_AT || tok == MP_TOKEN_OP_SLASH) { - // Can't fold @ or / - return false; - } mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS); - int rhs_sign = mp_obj_int_sign(arg1); - if (op <= MP_BINARY_OP_RSHIFT) { - // << and >> can't have negative rhs - if (rhs_sign < 0) { - return false; - } - } else if (op >= MP_BINARY_OP_FLOOR_DIVIDE) { - // % and // can't have zero rhs - if (rhs_sign == 0) { - return false; - } + if (!binary_op_maybe(op, arg0, arg1, &arg0)) { + return false; } - arg0 = mp_binary_op(op, arg0, arg1); } } else if (rule_id == RULE_factor_2) { // folding for unary ops: + - ~ mp_parse_node_t pn = peek_result(parser, 0); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, 1)); mp_unary_op_t op; if (tok == MP_TOKEN_OP_TILDE) { + if (!mp_obj_is_int(arg0)) { + return false; + } op = MP_UNARY_OP_INVERT; } else { assert(tok == MP_TOKEN_OP_PLUS || tok == MP_TOKEN_OP_MINUS); // should be @@ -860,7 +883,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { return false; } // id1.id2 - // look it up in constant table, see if it can be replaced with an integer + // look it up in constant table, see if it can be replaced with an integer or a float mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn1; assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0])); qstr q_base = MP_PARSE_NODE_LEAF_ARG(pn0); @@ -871,7 +894,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { } mp_obj_t dest[2]; mp_load_method_maybe(elem->value, q_attr, dest); - if (!(dest[0] != MP_OBJ_NULL && mp_obj_is_int(dest[0]) && dest[1] == MP_OBJ_NULL)) { + if (!(dest[0] != MP_OBJ_NULL && (mp_obj_is_int(dest[0]) || mp_obj_is_float(dest[0])) && dest[1] == MP_OBJ_NULL)) { return false; } arg0 = dest[0]; diff --git a/py/parsenum.c b/py/parsenum.c index 874216f08d005..91340fd2a18d7 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -28,6 +28,7 @@ #include #include "py/runtime.h" +#include "py/misc.h" #include "py/parsenumbase.h" #include "py/parsenum.h" #include "py/smallint.h" @@ -36,7 +37,7 @@ #include #endif -static NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) { +static MP_NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) { // if lex!=NULL then the parser called us and we need to convert the // exception's type from ValueError to SyntaxError and add traceback info if (lex != NULL) { @@ -46,6 +47,27 @@ static NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) { nlr_raise(exc); } +#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG +// For the common small integer parsing case, we parse directly to mp_int_t and +// check that the value doesn't overflow a smallint (in which case we fail over +// to bigint parsing if supported) +typedef mp_int_t parsed_int_t; + +#define PARSED_INT_MUL_OVERFLOW mp_mul_mp_int_t_overflow +#define PARSED_INT_FITS MP_SMALL_INT_FITS +#else +// In the special case where bigint support is long long, we save code size by +// parsing directly to long long and then return either a bigint or smallint +// from the same result. +// +// To avoid pulling in (slow) signed 64-bit math routines we do the initial +// parsing to an unsigned long long and only convert to signed at the end. +typedef unsigned long long parsed_int_t; + +#define PARSED_INT_MUL_OVERFLOW mp_mul_ull_overflow +#define PARSED_INT_FITS(I) ((I) <= (unsigned long long)LLONG_MAX + 1) +#endif + mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, mp_lexer_t *lex) { const byte *restrict str = (const byte *)str_; const byte *restrict top = str + len; @@ -77,7 +99,7 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m str += mp_parse_num_base((const char *)str, top - str, &base); // string should be an integer number - mp_int_t int_val = 0; + parsed_int_t parsed_val = 0; const byte *restrict str_val_start = str; for (; str < top; str++) { // get next digit as a value @@ -99,25 +121,32 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m break; } - // add next digi and check for overflow - if (mp_small_int_mul_overflow(int_val, base)) { + // add next digit and check for overflow + if (PARSED_INT_MUL_OVERFLOW(parsed_val, base, &parsed_val)) { goto overflow; } - int_val = int_val * base + dig; - if (!MP_SMALL_INT_FITS(int_val)) { + parsed_val += dig; + if (!PARSED_INT_FITS(parsed_val)) { goto overflow; } } - // negate value if needed - if (neg) { - int_val = -int_val; + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG + // The PARSED_INT_FITS check above ensures parsed_val fits in small int representation + ret_val = MP_OBJ_NEW_SMALL_INT(neg ? (-parsed_val) : parsed_val); +have_ret_val: + #else + // The PARSED_INT_FITS check above ensures parsed_val won't overflow signed long long + long long signed_val = -parsed_val; + if (!neg) { + if (signed_val == LLONG_MIN) { + goto overflow; + } + signed_val = -signed_val; } + ret_val = mp_obj_new_int_from_ll(signed_val); // Could be large or small int + #endif - // create the small int - ret_val = MP_OBJ_NEW_SMALL_INT(int_val); - -have_ret_val: // check we parsed something if (str == str_val_start) { goto value_error; @@ -136,6 +165,7 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m return ret_val; overflow: + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG // reparse using long int { const char *s2 = (const char *)str_val_start; @@ -143,6 +173,9 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m str = (const byte *)s2; goto have_ret_val; } + #else + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("result overflows long long storage")); + #endif value_error: { @@ -167,6 +200,8 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m } } +#if MICROPY_PY_BUILTINS_FLOAT + enum { REAL_IMAG_STATE_START = 0, REAL_IMAG_STATE_HAVE_REAL = 1, @@ -179,27 +214,77 @@ typedef enum { PARSE_DEC_IN_EXP, } parse_dec_in_t; -#if MICROPY_PY_BUILTINS_FLOAT // MANTISSA_MAX is used to retain precision while not overflowing mantissa -// SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float -// EXACT_POWER_OF_10 is the largest value of x so that 10^x can be stored exactly in a float -// Note: EXACT_POWER_OF_10 is at least floor(log_5(2^mantissa_length)). Indeed, 10^n = 2^n * 5^n -// so we only have to store the 5^n part in the mantissa (the 2^n part will go into the float's -// exponent). +#define MANTISSA_MAX (sizeof(mp_large_float_uint_t) == 8 ? 0x1999999999999998ULL : 0x19999998U) + +// MAX_EXACT_POWER_OF_5 is the largest value of x so that 5^x can be stored exactly in a float #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT -#define MANTISSA_MAX 0x19999998U -#define SMALL_NORMAL_VAL (1e-37F) -#define SMALL_NORMAL_EXP (-37) -#define EXACT_POWER_OF_10 (9) +#define MAX_EXACT_POWER_OF_5 (10) #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE -#define MANTISSA_MAX 0x1999999999999998ULL -#define SMALL_NORMAL_VAL (1e-307) -#define SMALL_NORMAL_EXP (-307) -#define EXACT_POWER_OF_10 (22) +#define MAX_EXACT_POWER_OF_5 (22) #endif +// Helper to compute `num * (10.0 ** dec_exp)` +mp_large_float_t mp_decimal_exp(mp_large_float_t num, int dec_exp) { + if (dec_exp == 0 || num == (mp_large_float_t)(0.0)) { + return num; + } + + #if MICROPY_FLOAT_FORMAT_IMPL == MICROPY_FLOAT_FORMAT_IMPL_EXACT + + // If the assert below fails, it means you have chosen MICROPY_FLOAT_FORMAT_IMPL_EXACT + // manually on a platform where `larger floats` are not supported, which would + // result in inexact conversions. To fix this issue, change your `mpconfigport.h` + // and select MICROPY_FLOAT_FORMAT_IMPL_APPROX instead + assert(sizeof(mp_large_float_t) > sizeof(mp_float_t)); + + // Perform power using simple multiplications, to avoid + // dependency to higher-precision pow() function + int neg_exp = (dec_exp < 0); + if (neg_exp) { + dec_exp = -dec_exp; + } + mp_large_float_t res = num; + mp_large_float_t expo = (mp_large_float_t)10.0; + while (dec_exp) { + if (dec_exp & 1) { + if (neg_exp) { + res /= expo; + } else { + res *= expo; + } + } + dec_exp >>= 1; + if (dec_exp) { + expo *= expo; + } + } + return res; + + #else + // MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_EXACT + + mp_float_union_t res = {num}; + // Multiply first by (2.0 ** dec_exp) via the exponent + // - this will ensure that the result of `pow()` is always in mp_float_t range + // when the result is expected to be in mp_float_t range (e.g. during format) + // - we don't need to care about p.exp overflow, as (5.0 ** dec_exp) will anyway + // force the final result toward the proper edge if needed (0.0 or inf) + res.p.exp += dec_exp; + // Use positive exponents when they are more precise then negative + if (dec_exp < 0 && dec_exp >= -MAX_EXACT_POWER_OF_5) { + res.f /= MICROPY_FLOAT_C_FUN(pow)(5, -dec_exp); + } else { + res.f *= MICROPY_FLOAT_C_FUN(pow)(5, dec_exp); + } + return (mp_large_float_t)res.f; + + #endif +} + + // Break out inner digit accumulation routine to ease trailing zero deferral. -static mp_float_uint_t accept_digit(mp_float_uint_t p_mantissa, unsigned int dig, int *p_exp_extra, int in) { +static mp_large_float_uint_t accept_digit(mp_large_float_uint_t p_mantissa, unsigned int dig, int *p_exp_extra, int in) { // Core routine to ingest an additional digit. if (p_mantissa < MANTISSA_MAX) { // dec_val won't overflow so keep accumulating @@ -216,6 +301,85 @@ static mp_float_uint_t accept_digit(mp_float_uint_t p_mantissa, unsigned int dig return p_mantissa; } } + +// Helper to parse an unsigned decimal number into a mp_float_t +const char *mp_parse_float_internal(const char *str, size_t len, mp_float_t *res) { + const char *top = str + len; + + parse_dec_in_t in = PARSE_DEC_IN_INTG; + bool exp_neg = false; + mp_large_float_uint_t mantissa = 0; + int exp_val = 0; + int exp_extra = 0; + int trailing_zeros_intg = 0, trailing_zeros_frac = 0; + while (str < top) { + unsigned int dig = *str++; + if ('0' <= dig && dig <= '9') { + dig -= '0'; + if (in == PARSE_DEC_IN_EXP) { + // don't overflow exp_val when adding next digit, instead just truncate + // it and the resulting float will still be correct, either inf or 0.0 + // (use INT_MAX/2 to allow adding exp_extra at the end without overflow) + if (exp_val < (INT_MAX / 2 - 9) / 10) { + exp_val = 10 * exp_val + dig; + } + } else { + if (dig == 0 || mantissa >= MANTISSA_MAX) { + // Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them. + // Also, once we reach MANTISSA_MAX, treat every additional digit as a trailing zero. + if (in == PARSE_DEC_IN_INTG) { + ++trailing_zeros_intg; + } else { + ++trailing_zeros_frac; + } + } else { + // Time to un-defer any trailing zeros. Intg zeros first. + while (trailing_zeros_intg) { + mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_INTG); + --trailing_zeros_intg; + } + while (trailing_zeros_frac) { + mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_FRAC); + --trailing_zeros_frac; + } + mantissa = accept_digit(mantissa, dig, &exp_extra, in); + } + } + } else if (in == PARSE_DEC_IN_INTG && dig == '.') { + in = PARSE_DEC_IN_FRAC; + } else if (in != PARSE_DEC_IN_EXP && ((dig | 0x20) == 'e')) { + in = PARSE_DEC_IN_EXP; + if (str < top) { + if (str[0] == '+') { + str++; + } else if (str[0] == '-') { + str++; + exp_neg = true; + } + } + if (str == top) { + return NULL; + } + } else if (dig == '_') { + continue; + } else { + // unknown character + str--; + break; + } + } + + // work out the exponent + if (exp_neg) { + exp_val = -exp_val; + } + exp_val += exp_extra + trailing_zeros_intg; + + // At this point, we just need to multiply the mantissa by its base 10 exponent. + *res = (mp_float_t)mp_decimal_exp(mantissa, exp_val); + + return str; +} #endif // MICROPY_PY_BUILTINS_FLOAT #if MICROPY_PY_BUILTINS_COMPLEX @@ -253,111 +417,23 @@ parse_start:; const char *str_val_start = str; // determine what the string is - if (str < top && (str[0] | 0x20) == 'i') { - // string starts with 'i', should be 'inf' or 'infinity' (case insensitive) - if (str + 2 < top && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'f') { - // inf - str += 3; - dec_val = (mp_float_t)INFINITY; - if (str + 4 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'i' && (str[3] | 0x20) == 't' && (str[4] | 0x20) == 'y') { - // infinity - str += 5; - } - } - } else if (str < top && (str[0] | 0x20) == 'n') { - // string starts with 'n', should be 'nan' (case insensitive) - if (str + 2 < top && (str[1] | 0x20) == 'a' && (str[2] | 0x20) == 'n') { - // NaN - str += 3; - dec_val = MICROPY_FLOAT_C_FUN(nan)(""); + if (str + 2 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'f') { + // 'inf' or 'infinity' (case insensitive) + str += 3; + dec_val = (mp_float_t)INFINITY; + if (str + 4 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'i' && (str[3] | 0x20) == 't' && (str[4] | 0x20) == 'y') { + // infinity + str += 5; } + } else if (str + 2 < top && (str[0] | 0x20) == 'n' && (str[1] | 0x20) == 'a' && (str[2] | 0x20) == 'n') { + // 'nan' (case insensitive) + str += 3; + dec_val = MICROPY_FLOAT_C_FUN(nan)(""); } else { // string should be a decimal number - parse_dec_in_t in = PARSE_DEC_IN_INTG; - bool exp_neg = false; - mp_float_uint_t mantissa = 0; - int exp_val = 0; - int exp_extra = 0; - int trailing_zeros_intg = 0, trailing_zeros_frac = 0; - while (str < top) { - unsigned int dig = *str++; - if ('0' <= dig && dig <= '9') { - dig -= '0'; - if (in == PARSE_DEC_IN_EXP) { - // don't overflow exp_val when adding next digit, instead just truncate - // it and the resulting float will still be correct, either inf or 0.0 - // (use INT_MAX/2 to allow adding exp_extra at the end without overflow) - if (exp_val < (INT_MAX / 2 - 9) / 10) { - exp_val = 10 * exp_val + dig; - } - } else { - if (dig == 0 || mantissa >= MANTISSA_MAX) { - // Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them. - // Also, once we reach MANTISSA_MAX, treat every additional digit as a trailing zero. - if (in == PARSE_DEC_IN_INTG) { - ++trailing_zeros_intg; - } else { - ++trailing_zeros_frac; - } - } else { - // Time to un-defer any trailing zeros. Intg zeros first. - while (trailing_zeros_intg) { - mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_INTG); - --trailing_zeros_intg; - } - while (trailing_zeros_frac) { - mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_FRAC); - --trailing_zeros_frac; - } - mantissa = accept_digit(mantissa, dig, &exp_extra, in); - } - } - } else if (in == PARSE_DEC_IN_INTG && dig == '.') { - in = PARSE_DEC_IN_FRAC; - } else if (in != PARSE_DEC_IN_EXP && ((dig | 0x20) == 'e')) { - in = PARSE_DEC_IN_EXP; - if (str < top) { - if (str[0] == '+') { - str++; - } else if (str[0] == '-') { - str++; - exp_neg = true; - } - } - if (str == top) { - goto value_error; - } - } else if (dig == '_') { - continue; - } else { - // unknown character - str--; - break; - } - } - - // work out the exponent - if (exp_neg) { - exp_val = -exp_val; - } - - // apply the exponent, making sure it's not a subnormal value - exp_val += exp_extra + trailing_zeros_intg; - dec_val = (mp_float_t)mantissa; - if (exp_val < SMALL_NORMAL_EXP) { - exp_val -= SMALL_NORMAL_EXP; - dec_val *= SMALL_NORMAL_VAL; - } - - // At this point, we need to multiply the mantissa by its base 10 exponent. If possible, - // we would rather manipulate numbers that have an exact representation in IEEE754. It - // turns out small positive powers of 10 do, whereas small negative powers of 10 don't. - // So in that case, we'll yield a division of exact values rather than a multiplication - // of slightly erroneous values. - if (exp_val < 0 && exp_val >= -EXACT_POWER_OF_10) { - dec_val /= MICROPY_FLOAT_C_FUN(pow)(10, -exp_val); - } else { - dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val); + str = mp_parse_float_internal(str, top - str, &dec_val); + if (!str) { + goto value_error; } } diff --git a/py/parsenum.h b/py/parsenum.h index f444632d23021..d532cb194a5d8 100644 --- a/py/parsenum.h +++ b/py/parsenum.h @@ -34,6 +34,11 @@ mp_obj_t mp_parse_num_integer(const char *restrict str, size_t len, int base, mp_lexer_t *lex); +#if MICROPY_PY_BUILTINS_FLOAT +mp_large_float_t mp_decimal_exp(mp_large_float_t num, int dec_exp); +const char *mp_parse_float_internal(const char *str, size_t len, mp_float_t *res); +#endif + #if MICROPY_PY_BUILTINS_COMPLEX mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex); diff --git a/py/persistentcode.c b/py/persistentcode.c index 93f4c33deb4e2..5c3de3174bcd0 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -63,6 +63,10 @@ typedef struct _bytecode_prelude_t { uint code_info_size; } bytecode_prelude_t; +#if MICROPY_EMIT_RV32 +#include "py/asmrv32.h" +#endif + #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE #if MICROPY_PERSISTENT_CODE_LOAD @@ -72,6 +76,8 @@ 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_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA // An mp_obj_list_t that tracks native text/BSS/rodata to prevent the GC from reclaiming them. @@ -86,8 +92,6 @@ static void track_root_pointer(void *ptr) { #endif -#if MICROPY_EMIT_MACHINE_CODE - typedef struct _reloc_info_t { mp_reader_t *reader; mp_module_context_t *context; @@ -422,15 +426,17 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co // Relocate and commit code to executable address space reloc_info_t ri = {reader, context, rodata, bss}; + #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA + if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { + // Track the function data memory so it's not reclaimed by the GC. + track_root_pointer(fun_data); + } + #endif #if defined(MP_PLAT_COMMIT_EXEC) void *opt_ri = (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL; fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri); #else if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { - #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA - // Track the function data memory so it's not reclaimed by the GC. - track_root_pointer(fun_data); - #endif // Do the relocations. mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data); } @@ -480,7 +486,7 @@ void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { || header[3] > MP_SMALL_INT_BITS) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } - if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { + if (arch != MP_NATIVE_ARCH_NONE) { if (!MPY_FEATURE_ARCH_TEST(arch)) { if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) { // On supported ports this can be resolved by enabling feature, eg @@ -492,6 +498,23 @@ void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { } } + size_t arch_flags = 0; + if (MPY_FEATURE_ARCH_FLAGS_TEST(header[2])) { + #if MICROPY_EMIT_RV32 + arch_flags = read_uint(reader); + + if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_RV32IMC)) { + if ((arch_flags & (size_t)asm_rv32_allowed_extensions()) != arch_flags) { + mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); + } + } else + #endif + { + (void)arch_flags; + mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); + } + } + size_t n_qstr = read_uint(reader); size_t n_obj = read_uint(reader); mp_module_context_alloc_tables(cm->context, n_qstr, n_obj); @@ -513,6 +536,7 @@ void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { cm->has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE; cm->n_qstr = n_qstr; cm->n_obj = n_obj; + cm->arch_flags = arch_flags; #endif // Deregister exception handler and close the reader. @@ -682,7 +706,7 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { byte header[4] = { 'C', MPY_VERSION, - cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0, + (cm->arch_flags != 0 ? MPY_FEATURE_ARCH_FLAGS : 0) | (cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0), #if MICROPY_DYNAMIC_COMPILER mp_dynamic_compiler.small_int_bits, #else @@ -691,6 +715,10 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { }; mp_print_bytes(print, header, sizeof(header)); + if (cm->arch_flags) { + mp_print_uint(print, cm->arch_flags); + } + // Number of entries in constant table. mp_print_uint(print, cm->n_qstr); mp_print_uint(print, cm->n_obj); @@ -769,7 +797,7 @@ static void bit_vector_clear(bit_vector_t *self) { static bool bit_vector_is_set(bit_vector_t *self, size_t index) { const size_t bits_size = sizeof(*self->bits) * MP_BITS_PER_BYTE; return index / bits_size < self->alloc - && (self->bits[index / bits_size] & (1 << (index % bits_size))) != 0; + && (self->bits[index / bits_size] & ((uintptr_t)1 << (index % bits_size))) != 0; } static void bit_vector_set(bit_vector_t *self, size_t index) { @@ -780,7 +808,7 @@ static void bit_vector_set(bit_vector_t *self, size_t index) { self->bits = m_renew(uintptr_t, self->bits, self->alloc, new_alloc); self->alloc = new_alloc; } - self->bits[index / bits_size] |= 1 << (index % bits_size); + self->bits[index / bits_size] |= (uintptr_t)1 << (index % bits_size); } typedef struct _mp_opcode_t { diff --git a/py/persistentcode.h b/py/persistentcode.h index 46b474e57f707..85668e608c85b 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -50,7 +50,7 @@ // Macros to encode/decode native architecture to/from the feature byte #define MPY_FEATURE_ENCODE_ARCH(arch) ((arch) << 2) -#define MPY_FEATURE_DECODE_ARCH(feat) ((feat) >> 2) +#define MPY_FEATURE_DECODE_ARCH(feat) (((feat) >> 2) & 0x2F) // Define the host architecture #if MICROPY_EMIT_X86 @@ -90,6 +90,10 @@ #define MPY_FILE_HEADER_INT (MPY_VERSION \ | (MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH)) << 8) +// Architecture-specific flags are present in the .mpy file +#define MPY_FEATURE_ARCH_FLAGS (0x40) +#define MPY_FEATURE_ARCH_FLAGS_TEST(x) (((x) & MPY_FEATURE_ARCH_FLAGS) == MPY_FEATURE_ARCH_FLAGS) + enum { MP_NATIVE_ARCH_NONE = 0, MP_NATIVE_ARCH_X86, @@ -103,6 +107,7 @@ enum { MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN, MP_NATIVE_ARCH_RV32IMC, + MP_NATIVE_ARCH_RV64IMC, MP_NATIVE_ARCH_DEBUG, // this entry should always be last }; diff --git a/py/profile.c b/py/profile.c index 397d0291f9fad..b5a0c54728c97 100644 --- a/py/profile.c +++ b/py/profile.c @@ -80,7 +80,7 @@ static void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t "", frame, MP_CODE_QSTR_MAP(code->context, 0), - frame->lineno, + (int)frame->lineno, MP_CODE_QSTR_MAP(code->context, prelude->qstr_block_name_idx) ); } diff --git a/py/py.cmake b/py/py.cmake index 6c180ae53e6f2..b99d2d24b5db3 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -58,6 +58,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/mpz.c ${MICROPY_PY_DIR}/nativeglue.c ${MICROPY_PY_DIR}/nlr.c + ${MICROPY_PY_DIR}/nlraarch64.c ${MICROPY_PY_DIR}/nlrmips.c ${MICROPY_PY_DIR}/nlrpowerpc.c ${MICROPY_PY_DIR}/nlrrv32.c diff --git a/py/py.mk b/py/py.mk index c05327bf81d55..71b44f84d65e2 100644 --- a/py/py.mk +++ b/py/py.mk @@ -289,7 +289,7 @@ $(HEADER_BUILD)/compressed_translations.generated.h: $(PY_SRC)/maketranslationda PY_CORE_O += $(PY_BUILD)/translations-$(TRANSLATION).o # build a list of registered modules for py/objmodule.c. -$(HEADER_BUILD)/moduledefs.h: $(HEADER_BUILD)/moduledefs.collected +$(HEADER_BUILD)/moduledefs.h: $(HEADER_BUILD)/moduledefs.collected $(PY_SRC)/makemoduledefs.py @$(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makemoduledefs.py $< > $@ diff --git a/py/repl.c b/py/repl.c index c9a20305c79ff..bbfdd2ea6a6e2 100644 --- a/py/repl.c +++ b/py/repl.c @@ -181,8 +181,11 @@ static bool test_qstr(mp_obj_t obj, qstr name) { return dest[0] != MP_OBJ_NULL; } else { // try builtin module - return mp_map_lookup((mp_map_t *)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP) || - mp_map_lookup((mp_map_t *)&mp_builtin_extensible_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP); + return mp_map_lookup((mp_map_t *)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP) + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES + || mp_map_lookup((mp_map_t *)&mp_builtin_extensible_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP) + #endif + ; } } @@ -237,6 +240,10 @@ static void print_completions(const mp_print_t *print, for (qstr q = q_first; q <= q_last; ++q) { size_t d_len; const char *d_str = (const char *)qstr_data(q, &d_len); + // filter out words that begin with underscore unless there's already a partial match + if (s_len == 0 && d_str[0] == '_') { + continue; + } if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) { if (test_qstr(obj, q)) { int gap = (line_len + WORD_SLOT_LEN - 1) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len; diff --git a/py/runtime.c b/py/runtime.c index 9def98380fa45..0b62500cc2a10 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -134,7 +134,7 @@ void mp_init(void) { MP_STATE_VM(mp_module_builtins_override_dict) = NULL; #endif - #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA + #if MICROPY_EMIT_MACHINE_CODE && (MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA) MP_STATE_VM(persistent_code_root_pointers) = MP_OBJ_NULL; #endif @@ -446,7 +446,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs // Operations that can overflow: // + result always fits in mp_int_t, then handled by SMALL_INT check // - result always fits in mp_int_t, then handled by SMALL_INT check - // * checked explicitly + // * checked explicitly for fit in mp_int_t, then handled by SMALL_INT check // / if lhs=MIN and rhs=-1; result always fits in mp_int_t, then handled by SMALL_INT check // % if lhs=MIN and rhs=-1; result always fits in mp_int_t, then handled by SMALL_INT check // << checked explicitly @@ -505,30 +505,16 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs break; case MP_BINARY_OP_MULTIPLY: case MP_BINARY_OP_INPLACE_MULTIPLY: { - - // If long long type exists and is larger than mp_int_t, then - // we can use the following code to perform overflow-checked multiplication. - // Otherwise (eg in x64 case) we must use mp_small_int_mul_overflow. - #if 0 - // compute result using long long precision - long long res = (long long)lhs_val * (long long)rhs_val; - if (res > MP_SMALL_INT_MAX || res < MP_SMALL_INT_MIN) { - // result overflowed SMALL_INT, so return higher precision integer - return mp_obj_new_int_from_ll(res); - } else { - // use standard precision - lhs_val = (mp_int_t)res; - } - #endif - - if (mp_small_int_mul_overflow(lhs_val, rhs_val)) { + mp_int_t int_res; + if (mp_mul_mp_int_t_overflow(lhs_val, rhs_val, &int_res)) { // use higher precision lhs = mp_obj_new_int_from_ll(lhs_val); goto generic_binary_op; } else { // use standard precision - return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val); + lhs_val = int_res; } + break; } case MP_BINARY_OP_FLOOR_DIVIDE: case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: @@ -568,19 +554,19 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs mp_int_t ans = 1; while (rhs_val > 0) { if (rhs_val & 1) { - if (mp_small_int_mul_overflow(ans, lhs_val)) { + if (mp_mul_mp_int_t_overflow(ans, lhs_val, &ans)) { goto power_overflow; } - ans *= lhs_val; } if (rhs_val == 1) { break; } rhs_val /= 2; - if (mp_small_int_mul_overflow(lhs_val, lhs_val)) { + mp_int_t int_res; + if (mp_mul_mp_int_t_overflow(lhs_val, lhs_val, &int_res)) { goto power_overflow; } - lhs_val *= lhs_val; + lhs_val = int_res; } lhs_val = ans; } @@ -976,7 +962,7 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ob // unpacked items are stored in reverse order into the array pointed to by items // CIRCUITPY-CHANGE: noline -void __attribute__((noinline, )) mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) { +MP_NOINLINE void mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) { size_t seq_len; if (mp_obj_is_type(seq_in, &mp_type_tuple) || mp_obj_is_type(seq_in, &mp_type_list)) { mp_obj_t *seq_items; @@ -1301,6 +1287,19 @@ void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { mp_raise_msg_varg(&mp_type_AttributeError, MP_ERROR_TEXT("type object '%q' has no attribute '%q'"), ((mp_obj_type_t *)MP_OBJ_TO_PTR(base))->name, attr); + #if MICROPY_MODULE___ALL__ && MICROPY_ERROR_REPORTING >= MICROPY_ERROR_REPORTING_DETAILED + } else if (mp_obj_is_type(base, &mp_type_module)) { + // report errors in __all__ as done by CPython + mp_obj_t dest_name[2]; + qstr module_name = MP_QSTR_; + mp_load_method_maybe(base, MP_QSTR___name__, dest_name); + if (mp_obj_is_qstr(dest_name[0])) { + module_name = mp_obj_str_get_qstr(dest_name[0]); + } + mp_raise_msg_varg(&mp_type_AttributeError, + MP_ERROR_TEXT("module '%q' has no attribute '%q'"), + module_name, attr); + #endif } else { mp_raise_msg_varg(&mp_type_AttributeError, MP_ERROR_TEXT("'%s' object has no attribute '%q'"), @@ -1619,7 +1618,7 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) { // build args array mp_obj_t args[5]; args[0] = MP_OBJ_NEW_QSTR(name); - args[1] = mp_const_none; // TODO should be globals + args[1] = MP_OBJ_FROM_PTR(mp_globals_get()); // globals of the current context args[2] = mp_const_none; // TODO should be locals args[3] = fromlist; args[4] = level; @@ -1639,20 +1638,17 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) { } // CIRCUITPY-CHANGE: noinline -mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name) { +MP_NOINLINE mp_obj_t mp_import_from(mp_obj_t module, qstr name) { DEBUG_printf("import from %p %s\n", module, qstr_str(name)); mp_obj_t dest[2]; mp_load_method_maybe(module, name, dest); - if (dest[1] != MP_OBJ_NULL) { - // Hopefully we can't import bound method from an object - import_error: - mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("can't import name %q"), name); - } - - if (dest[0] != MP_OBJ_NULL) { + // Importing a bound method from a class instance. + return mp_obj_new_bound_meth(dest[0], dest[1]); + } else if (dest[0] != MP_OBJ_NULL) { + // Importing a function or attribute. return dest[0]; } @@ -1685,13 +1681,36 @@ mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name) goto import_error; #endif + +import_error: + mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("can't import name %q"), name); } void mp_import_all(mp_obj_t module) { DEBUG_printf("import all %p\n", module); - // TODO: Support __all__ mp_map_t *map = &mp_obj_module_get_globals(module)->map; + + #if MICROPY_MODULE___ALL__ + mp_map_elem_t *elem = mp_map_lookup(map, MP_OBJ_NEW_QSTR(MP_QSTR___all__), MP_MAP_LOOKUP); + if (elem != NULL) { + // When __all__ is defined, we must explicitly load all specified + // symbols, possibly invoking the module __getattr__ function + size_t len; + mp_obj_t *items; + mp_obj_get_array(elem->value, &len, &items); + for (size_t i = 0; i < len; i++) { + qstr qname = mp_obj_str_get_qstr(items[i]); + mp_obj_t dest[2]; + mp_load_method(module, qname, dest); + mp_store_name(qname, dest[0]); + } + return; + } + #endif + + // By default, the set of public names includes all names found in the module's + // namespace which do not begin with an underscore character ('_') for (size_t i = 0; i < map->alloc; i++) { if (mp_map_slot_is_filled(map, i)) { // Entry in module global scope may be generated programmatically @@ -1747,7 +1766,7 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i #endif // MICROPY_ENABLE_COMPILER // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void m_malloc_fail(size_t num_bytes) { +MP_COLD MP_NORETURN void m_malloc_fail(size_t num_bytes) { DEBUG_printf("memory allocation failed, allocating %u bytes\n", (uint)num_bytes); #if MICROPY_ENABLE_GC if (gc_is_locked()) { @@ -1761,29 +1780,29 @@ NORETURN MP_COLD void m_malloc_fail(size_t num_bytes) { #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_type(const mp_obj_type_t *exc_type) { +MP_COLD MP_NORETURN void mp_raise_type(const mp_obj_type_t *exc_type) { nlr_raise(mp_obj_new_exception(exc_type)); } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_ValueError_no_msg(void) { +MP_COLD MP_NORETURN void mp_raise_ValueError_no_msg(void) { mp_raise_type(&mp_type_ValueError); } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_TypeError_no_msg(void) { +MP_COLD MP_NORETURN void mp_raise_TypeError_no_msg(void) { mp_raise_type(&mp_type_TypeError); } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_NotImplementedError_no_msg(void) { +MP_COLD MP_NORETURN void mp_raise_NotImplementedError_no_msg(void) { mp_raise_type(&mp_type_NotImplementedError); } #else // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) { if (msg == NULL) { nlr_raise(mp_obj_new_exception(exc_type)); } else { @@ -1792,24 +1811,24 @@ NORETURN MP_COLD void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_t } // CIRCUITPY-CHANGE: new function for use below. -NORETURN MP_COLD void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr) { +MP_COLD MP_NORETURN void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr) { mp_obj_t exception = mp_obj_new_exception_msg_vlist(exc_type, fmt, argptr); nlr_raise(exception); } // CIRCUITPY-CHANGE: MP_COLD and use mp_raise_msg_vlist() -NORETURN MP_COLD void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(exc_type, fmt, argptr); va_end(argptr); } -NORETURN MP_COLD void mp_raise_ValueError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_ValueError, msg); } -NORETURN MP_COLD void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg) { +MP_COLD MP_NORETURN void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg) { if (msg == NULL) { nlr_raise(mp_obj_new_exception(exc_type)); } else { @@ -1818,17 +1837,17 @@ NORETURN MP_COLD void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_AttributeError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_AttributeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_AttributeError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_RuntimeError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_RuntimeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_RuntimeError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_RuntimeError, fmt, argptr); @@ -1836,17 +1855,17 @@ NORETURN MP_COLD void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_ImportError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_ImportError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_ImportError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_IndexError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_IndexError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_IndexError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_IndexError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_IndexError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_IndexError, fmt, argptr); @@ -1854,7 +1873,7 @@ NORETURN MP_COLD void mp_raise_IndexError_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_ValueError, fmt, argptr); @@ -1862,12 +1881,12 @@ NORETURN MP_COLD void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_TypeError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_TypeError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_TypeError, fmt, argptr); @@ -1875,12 +1894,12 @@ NORETURN MP_COLD void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_OSError_msg(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_OSError_msg(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_OSError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { +MP_COLD MP_NORETURN void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(errno_), str, @@ -1889,7 +1908,7 @@ NORETURN MP_COLD void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_OSError, fmt, argptr); @@ -1897,21 +1916,21 @@ NORETURN MP_COLD void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_ConnectionError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_ConnectionError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_ConnectionError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_BrokenPipeError(void) { +MP_COLD MP_NORETURN void mp_raise_BrokenPipeError(void) { mp_raise_type_arg(&mp_type_BrokenPipeError, MP_OBJ_NEW_SMALL_INT(MP_EPIPE)); } -NORETURN MP_COLD void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_NotImplementedError, fmt, argptr); @@ -1919,7 +1938,7 @@ NORETURN MP_COLD void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_OverflowError, fmt, argptr); @@ -1927,11 +1946,11 @@ NORETURN MP_COLD void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...) } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg) { +MP_COLD MP_NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg) { nlr_raise(mp_obj_new_exception_arg1(exc_type, arg)); } -NORETURN void mp_raise_StopIteration(mp_obj_t arg) { +MP_NORETURN void mp_raise_StopIteration(mp_obj_t arg) { if (arg == MP_OBJ_NULL) { mp_raise_type(&mp_type_StopIteration); } else { @@ -1939,7 +1958,7 @@ NORETURN void mp_raise_StopIteration(mp_obj_t arg) { } } -NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { +MP_NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE (void)arg; mp_raise_TypeError(MP_ERROR_TEXT("can't convert to int")); @@ -1950,12 +1969,12 @@ NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_OSError(int errno_) { +MP_COLD MP_NORETURN void mp_raise_OSError(int errno_) { mp_raise_type_arg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_)); } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_OSError_with_filename(int errno_, const char *filename) { +MP_COLD MP_NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename) { vstr_t vstr; vstr_init(&vstr, 32); vstr_printf(&vstr, "can't open %s", filename); @@ -1965,13 +1984,12 @@ NORETURN MP_COLD void mp_raise_OSError_with_filename(int errno_, const char *fil } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_ZeroDivisionError(void) { +MP_COLD MP_NORETURN void mp_raise_ZeroDivisionError(void) { mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("division by zero")); } #if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_recursion_depth(void) { +MP_COLD MP_NORETURN void mp_raise_recursion_depth(void) { mp_raise_RuntimeError(MP_ERROR_TEXT("maximum recursion depth exceeded")); } #endif -#endif diff --git a/py/runtime.h b/py/runtime.h index a2d69638ffc38..8bdd1982b96fc 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -26,17 +26,12 @@ #ifndef MICROPY_INCLUDED_PY_RUNTIME_H #define MICROPY_INCLUDED_PY_RUNTIME_H -#include - #include "py/mpstate.h" #include "py/pystack.h" #include "py/cstack.h" -// CIRCUITPY-CHANGE -#include "supervisor/linker.h" -#include "supervisor/shared/translate/translate.h" - -// For use with mp_call_function_1_from_nlr_jump_callback. +// Initialize an nlr_jump_callback_node_call_function_1_t struct for use with +// nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback); #define MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, f, a) \ nlr_jump_callback_node_call_function_1_t ctx = { \ .func = (void (*)(void *))(f), \ @@ -58,6 +53,12 @@ typedef enum { MP_ARG_KW_ONLY = 0x200, } mp_arg_flag_t; +typedef enum { + MP_HANDLE_PENDING_CALLBACKS_ONLY, + MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS, + MP_HANDLE_PENDING_CALLBACKS_AND_CLEAR_EXCEPTIONS, +} mp_handle_pending_behaviour_t; + typedef union _mp_arg_val_t { bool u_bool; mp_int_t u_int; @@ -106,7 +107,14 @@ void mp_sched_keyboard_interrupt(void); #if MICROPY_ENABLE_VM_ABORT void mp_sched_vm_abort(void); #endif -void mp_handle_pending(bool raise_exc); + +void mp_handle_pending_internal(mp_handle_pending_behaviour_t behavior); + +static inline void mp_handle_pending(bool raise_exc) { + mp_handle_pending_internal(raise_exc ? + MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS : + MP_HANDLE_PENDING_CALLBACKS_AND_CLEAR_EXCEPTIONS); +} #if MICROPY_ENABLE_SCHEDULER void mp_sched_lock(void); @@ -134,7 +142,7 @@ void mp_event_wait_indefinite(void); void mp_event_wait_ms(mp_uint_t timeout_ms); // extra printing method specifically for mp_obj_t's which are integral type -int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec); +int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, unsigned base, int base_char, int flags, char fill, int width, int prec); void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig); static inline void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw) { @@ -142,11 +150,11 @@ static inline void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_mi } void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals); void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals); -NORETURN void mp_arg_error_terse_mismatch(void); -NORETURN void mp_arg_error_unimpl_kw(void); +MP_NORETURN void mp_arg_error_terse_mismatch(void); +MP_NORETURN void mp_arg_error_unimpl_kw(void); // CIRCUITPY-CHANGE: arg validation routines -NORETURN void mp_arg_error_invalid(qstr arg_name); +MP_NORETURN void mp_arg_error_invalid(qstr arg_name); mp_int_t mp_arg_validate_int(mp_int_t i, mp_int_t required_i, qstr arg_name); mp_int_t mp_arg_validate_int_min(mp_int_t i, mp_int_t min, qstr arg_name); mp_int_t mp_arg_validate_int_max(mp_int_t i, mp_int_t j, qstr arg_name); @@ -194,9 +202,16 @@ static inline void mp_thread_init_state(mp_state_thread_t *ts, size_t stack_size ts->gc_lock_depth = 0; // There are no pending jump callbacks or exceptions yet + ts->nlr_top = NULL; ts->nlr_jump_callback_top = NULL; ts->mp_pending_exception = MP_OBJ_NULL; + #if MICROPY_PY_SYS_SETTRACE + ts->prof_trace_callback = MP_OBJ_NULL; + ts->prof_callback_is_executing = false; + ts->current_code_state = NULL; + #endif + // If locals/globals are not given, inherit from main thread if (locals == NULL) { locals = mp_state_ctx.thread.dict_locals; @@ -274,10 +289,10 @@ mp_obj_t mp_import_from(mp_obj_t module, qstr name); void mp_import_all(mp_obj_t module); #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE -NORETURN void mp_raise_type(const mp_obj_type_t *exc_type); -NORETURN void mp_raise_ValueError_no_msg(void); -NORETURN void mp_raise_TypeError_no_msg(void); -NORETURN void mp_raise_NotImplementedError_no_msg(void); +MP_NORETURN void mp_raise_type(const mp_obj_type_t *exc_type); +MP_NORETURN void mp_raise_ValueError_no_msg(void); +MP_NORETURN void mp_raise_TypeError_no_msg(void); +MP_NORETURN void mp_raise_NotImplementedError_no_msg(void); #define mp_raise_msg(exc_type, msg) mp_raise_type(exc_type) #define mp_raise_msg_varg(exc_type, ...) mp_raise_type(exc_type) #define mp_raise_ValueError(msg) mp_raise_ValueError_no_msg() @@ -285,46 +300,46 @@ NORETURN void mp_raise_NotImplementedError_no_msg(void); #define mp_raise_NotImplementedError(msg) mp_raise_NotImplementedError_no_msg() #else #define mp_raise_type(exc_type) mp_raise_msg(exc_type, NULL) -NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); -NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); -NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); -NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); #endif // CIRCUITPY-CHANGE: new mp_raise routines -NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg); -NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); -NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt +MP_NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg); +MP_NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt , ...); -NORETURN void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr); +MP_NORETURN void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr); // Only use this string version in native mpy files. Otherwise, use the compressed string version. -NORETURN void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg); - -NORETURN void mp_raise_AttributeError(mp_rom_error_text_t msg); -NORETURN void mp_raise_BrokenPipeError(void); -NORETURN void mp_raise_ConnectionError(mp_rom_error_text_t msg); -NORETURN void mp_raise_ImportError(mp_rom_error_text_t msg); -NORETURN void mp_raise_IndexError(mp_rom_error_text_t msg); -NORETURN void mp_raise_IndexError_varg(mp_rom_error_text_t msg, ...); -NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); -NORETURN void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_OSError_errno_str(int errno_, mp_obj_t str); -NORETURN void mp_raise_OSError(int errno_); -NORETURN void mp_raise_OSError_msg(mp_rom_error_text_t msg); -NORETURN void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename); -NORETURN void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_recursion_depth(void); -NORETURN void mp_raise_RuntimeError(mp_rom_error_text_t msg); -NORETURN void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_StopIteration(mp_obj_t arg); -NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); -NORETURN void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg); -NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); -NORETURN void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_ZeroDivisionError(void); +MP_NORETURN void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg); + +MP_NORETURN void mp_raise_AttributeError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_BrokenPipeError(void); +MP_NORETURN void mp_raise_ConnectionError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_ImportError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_IndexError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_IndexError_varg(mp_rom_error_text_t msg, ...); +MP_NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_OSError_errno_str(int errno_, mp_obj_t str); +MP_NORETURN void mp_raise_OSError(int errno_); +MP_NORETURN void mp_raise_OSError_msg(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename); +MP_NORETURN void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_recursion_depth(void); +MP_NORETURN void mp_raise_RuntimeError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_StopIteration(mp_obj_t arg); +MP_NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg); +MP_NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_ZeroDivisionError(void); #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG #undef mp_check_self diff --git a/py/runtime_utils.c b/py/runtime_utils.c index 98252c3122048..33da558e30f08 100644 --- a/py/runtime_utils.c +++ b/py/runtime_utils.c @@ -53,3 +53,59 @@ mp_obj_t mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2 return MP_OBJ_NULL; } } + +#if !MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC +bool mp_mul_ll_overflow(long long int x, long long int y, long long int *res) { + bool overflow; + + // Check for multiply overflow; see CERT INT32-C + if (x > 0) { // x is positive + if (y > 0) { // x and y are positive + overflow = (x > (LLONG_MAX / y)); + } else { // x positive, y nonpositive + overflow = (y < (LLONG_MIN / x)); + } // x positive, y nonpositive + } else { // x is nonpositive + if (y > 0) { // x is nonpositive, y is positive + overflow = (x < (LLONG_MIN / y)); + } else { // x and y are nonpositive + overflow = (x != 0 && y < (LLONG_MAX / x)); + } // End if x and y are nonpositive + } // End if x is nonpositive + + if (!overflow) { + *res = x * y; + } + + return overflow; +} + +bool mp_mul_mp_int_t_overflow(mp_int_t x, mp_int_t y, mp_int_t *res) { + // Check for multiply overflow; see CERT INT32-C + if (x > 0) { // x is positive + if (y > 0) { // x and y are positive + if (x > (MP_INT_MAX / y)) { + return true; + } + } else { // x positive, y nonpositive + if (y < (MP_INT_MIN / x)) { + return true; + } + } // x positive, y nonpositive + } else { // x is nonpositive + if (y > 0) { // x is nonpositive, y is positive + if (x < (MP_INT_MIN / y)) { + return true; + } + } else { // x and y are nonpositive + if (x != 0 && y < (MP_INT_MAX / x)) { + return true; + } + } // End if x and y are nonpositive + } // End if x is nonpositive + + // Result doesn't overflow + *res = x * y; + return false; +} +#endif diff --git a/py/scheduler.c b/py/scheduler.c index 92e5af6b31b20..ee9cd96b331a6 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -97,17 +97,21 @@ static inline void mp_sched_run_pending(void) { #if MICROPY_SCHEDULER_STATIC_NODES // Run all pending C callbacks. - while (MP_STATE_VM(sched_head) != NULL) { - mp_sched_node_t *node = MP_STATE_VM(sched_head); - MP_STATE_VM(sched_head) = node->next; - if (MP_STATE_VM(sched_head) == NULL) { - MP_STATE_VM(sched_tail) = NULL; - } - mp_sched_callback_t callback = node->callback; - node->callback = NULL; - MICROPY_END_ATOMIC_SECTION(atomic_state); - callback(node); - atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_sched_node_t *original_tail = MP_STATE_VM(sched_tail); + if (original_tail != NULL) { + mp_sched_node_t *node; + do { + node = MP_STATE_VM(sched_head); + MP_STATE_VM(sched_head) = node->next; + if (MP_STATE_VM(sched_head) == NULL) { + MP_STATE_VM(sched_tail) = NULL; + } + mp_sched_callback_t callback = node->callback; + node->callback = NULL; + MICROPY_END_ATOMIC_SECTION(atomic_state); + callback(node); + atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + } while (node != original_tail); // Don't execute any callbacks scheduled during this run } #endif @@ -218,24 +222,27 @@ MP_REGISTER_ROOT_POINTER(mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]); // Called periodically from the VM or from "waiting" code (e.g. sleep) to // process background tasks and pending exceptions (e.g. KeyboardInterrupt). -void mp_handle_pending(bool raise_exc) { +void mp_handle_pending_internal(mp_handle_pending_behaviour_t behavior) { + bool handle_exceptions = (behavior != MP_HANDLE_PENDING_CALLBACKS_ONLY); + bool raise_exceptions = (behavior == MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS); + // Handle pending VM abort. #if MICROPY_ENABLE_VM_ABORT - if (MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) { + if (handle_exceptions && MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) { MP_STATE_VM(vm_abort) = false; - if (raise_exc && nlr_get_abort() != NULL) { + if (raise_exceptions && nlr_get_abort() != NULL) { nlr_jump_abort(); } } #endif // Handle any pending exception. - if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { + if (handle_exceptions && MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); if (obj != MP_OBJ_NULL) { MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; - if (raise_exc) { + if (raise_exceptions) { MICROPY_END_ATOMIC_SECTION(atomic_state); nlr_raise(obj); } diff --git a/py/showbc.c b/py/showbc.c index 6913d18c1ca82..792fccd013383 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -144,17 +144,9 @@ void mp_bytecode_print(const mp_print_t *print, const mp_raw_code_t *rc, size_t mp_uint_t source_line = 1; mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); for (const byte *ci = code_info; ci < line_info_top;) { - if ((ci[0] & 0x80) == 0) { - // 0b0LLBBBBB encoding - bc += ci[0] & 0x1f; - source_line += ci[0] >> 5; - ci += 1; - } else { - // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) - bc += ci[0] & 0xf; - source_line += ((ci[0] << 4) & 0x700) | ci[1]; - ci += 2; - } + mp_code_lineinfo_t decoded = mp_bytecode_decode_lineinfo(&ci); + bc += decoded.bc_increment; + source_line += decoded.line_increment; mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); } } diff --git a/py/smallint.c b/py/smallint.c index aa542ca7bf29a..eb99b58667a07 100644 --- a/py/smallint.c +++ b/py/smallint.c @@ -26,32 +26,6 @@ #include "py/smallint.h" -bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y) { - // Check for multiply overflow; see CERT INT32-C - if (x > 0) { // x is positive - if (y > 0) { // x and y are positive - if (x > (MP_SMALL_INT_MAX / y)) { - return true; - } - } else { // x positive, y nonpositive - if (y < (MP_SMALL_INT_MIN / x)) { - return true; - } - } // x positive, y nonpositive - } else { // x is nonpositive - if (y > 0) { // x is nonpositive, y is positive - if (x < (MP_SMALL_INT_MIN / y)) { - return true; - } - } else { // x and y are nonpositive - if (x != 0 && y < (MP_SMALL_INT_MAX / x)) { - return true; - } - } // End if x and y are nonpositive - } // End if x is nonpositive - return false; -} - mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor) { // Python specs require that mod has same sign as second operand dividend %= divisor; diff --git a/py/smallint.h b/py/smallint.h index 584e0018d1ba3..ec5b0af3b2867 100644 --- a/py/smallint.h +++ b/py/smallint.h @@ -68,7 +68,6 @@ // The number of bits in a MP_SMALL_INT including the sign bit. #define MP_SMALL_INT_BITS (MP_IMAX_BITS(MP_SMALL_INT_MAX) + 1) -bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y); mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor); mp_int_t mp_small_int_floor_divide(mp_int_t num, mp_int_t denom); diff --git a/py/stream.c b/py/stream.c index fbf7fd878a133..520422cc92fcf 100644 --- a/py/stream.c +++ b/py/stream.c @@ -260,9 +260,14 @@ void mp_stream_write_adaptor(void *self, const char *buf, size_t len) { mp_stream_write(MP_OBJ_FROM_PTR(self), buf, len, MP_STREAM_RW_WRITE); } -static mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) { +static mp_obj_t stream_readinto_write_generic(size_t n_args, const mp_obj_t *args, byte flags) { mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &bufinfo, (flags & MP_STREAM_RW_WRITE) ? MP_BUFFER_READ : MP_BUFFER_WRITE); + + // CPython extension, allow optional maximum length and offset: + // - stream.operation(buf, max_len) + // - stream.operation(buf, off, max_len) + // Similar to https://docs.python.org/3/library/socket.html#socket.socket.recv_into size_t max_len = (size_t)-1; size_t off = 0; if (n_args == 3) { @@ -275,45 +280,31 @@ static mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) { } } bufinfo.len -= off; - return mp_stream_write(args[0], (byte *)bufinfo.buf + off, MIN(bufinfo.len, max_len), MP_STREAM_RW_WRITE); + + // Perform the readinto or write operation. + return mp_stream_write(args[0], (byte *)bufinfo.buf + off, MIN(bufinfo.len, max_len), flags); +} + +static mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) { + return stream_readinto_write_generic(n_args, args, MP_STREAM_RW_WRITE); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj, 2, 4, stream_write_method); -static mp_obj_t stream_write1_method(mp_obj_t self_in, mp_obj_t arg) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); - return mp_stream_write(self_in, bufinfo.buf, bufinfo.len, MP_STREAM_RW_WRITE | MP_STREAM_RW_ONCE); +static mp_obj_t stream_write1_method(size_t n_args, const mp_obj_t *args) { + return stream_readinto_write_generic(n_args, args, MP_STREAM_RW_WRITE | MP_STREAM_RW_ONCE); } -MP_DEFINE_CONST_FUN_OBJ_2(mp_stream_write1_obj, stream_write1_method); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write1_obj, 2, 4, stream_write1_method); static mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); - - // CPython extension: if 2nd arg is provided, that's max len to read, - // instead of full buffer. Similar to - // https://docs.python.org/3/library/socket.html#socket.socket.recv_into - mp_uint_t len = bufinfo.len; - if (n_args > 2) { - len = mp_obj_get_int(args[2]); - if (len > bufinfo.len) { - len = bufinfo.len; - } - } - - int error; - mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); - if (error != 0) { - if (mp_is_nonblocking_error(error)) { - return mp_const_none; - } - mp_raise_OSError(error); - } else { - return MP_OBJ_NEW_SMALL_INT(out_sz); - } + return stream_readinto_write_generic(n_args, args, MP_STREAM_RW_READ); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj, 2, 3, stream_readinto); +static mp_obj_t stream_readinto1(size_t n_args, const mp_obj_t *args) { + return stream_readinto_write_generic(n_args, args, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto1_obj, 2, 3, stream_readinto1); + static mp_obj_t stream_readall(mp_obj_t self_in) { const mp_stream_p_t *stream_p = mp_get_stream(self_in); diff --git a/py/stream.h b/py/stream.h index 5e1cd5d441302..6e60e849b5f48 100644 --- a/py/stream.h +++ b/py/stream.h @@ -87,10 +87,11 @@ typedef struct _mp_stream_p_t { MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read1_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto1_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_unbuffered_readlines_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj); -MP_DECLARE_CONST_FUN_OBJ_2(mp_stream_write1_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write1_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_close_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream___exit___obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj); diff --git a/py/vm.c b/py/vm.c index 6fd1778261971..860aa36e39792 100644 --- a/py/vm.c +++ b/py/vm.c @@ -205,6 +205,22 @@ static mp_obj_t get_active_exception(mp_exc_stack_t *exc_sp, mp_exc_stack_t *exc return MP_OBJ_NULL; } +#if MICROPY_PY_BUILTINS_SLICE +// This function is marked "no inline" so it doesn't increase the C stack usage of the main VM function. +MP_NOINLINE static mp_obj_t *build_slice_stack_allocated(byte op, mp_obj_t *sp, mp_obj_t step) { + mp_obj_t stop = sp[2]; + mp_obj_t start = sp[1]; + mp_obj_slice_t slice = { .base = { .type = &mp_type_slice }, .start = start, .stop = stop, .step = step }; + if (op == MP_BC_LOAD_SUBSCR) { + SET_TOP(mp_obj_subscr(TOP(), MP_OBJ_FROM_PTR(&slice), MP_OBJ_SENTINEL)); + } else { // MP_BC_STORE_SUBSCR + mp_obj_subscr(TOP(), MP_OBJ_FROM_PTR(&slice), sp[-1]); + sp -= 2; + } + return sp; +} +#endif + // fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc) // sp points to bottom of stack which grows up // returns: @@ -871,9 +887,20 @@ unwind_jump:; // 3-argument slice includes step step = POP(); } - mp_obj_t stop = POP(); - mp_obj_t start = TOP(); - SET_TOP(mp_obj_new_slice(start, stop, step)); + if ((*ip == MP_BC_LOAD_SUBSCR || *ip == MP_BC_STORE_SUBSCR) + && (mp_obj_get_type(sp[-2])->flags & MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE)) { + // Fast path optimisation for when the BUILD_SLICE is immediately followed + // by a LOAD/STORE_SUBSCR for an accepting type, to avoid needing to allocate + // the slice on the heap. In some cases (e.g. a[1:3] = x) this can result + // in no allocations at all. We can't do this for instance types because + // the get/set/delattr implementation may keep a reference to the slice. + byte op = *ip++; + sp = build_slice_stack_allocated(op, sp - 2, step); + } else { + mp_obj_t stop = POP(); + mp_obj_t start = TOP(); + SET_TOP(mp_obj_new_slice(start, stop, step)); + } DISPATCH(); } #endif diff --git a/pyproject.toml b/pyproject.toml index 7003a6af9aeae..e88137bb0f7bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,29 @@ -# SPDX-FileCopyrightText: 2024 Scott Shawcroft for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -[tool.setuptools_scm] -# can be empty if no extra settings are needed, presence enables setuptools-scm - -# Codespell settings copied from MicroPython +[tool.codespell] +count = "" +ignore-regex = '\b[A-Z]{3}\b' +# CIRCUITPY-CHANGE: aranges +ignore-words-list = "ans,asend,aranges,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" +quiet-level = 3 +skip = """ +*/build*,\ +./.git,\ +./drivers/cc3100,\ +./lib,\ +./ports/cc3200/FreeRTOS,\ +./ports/cc3200/bootmgr/sl,\ +./ports/cc3200/hal,\ +./ports/cc3200/simplelink,\ +./ports/cc3200/telnet,\ +./ports/esp32/managed_components,\ +./ports/nrf/drivers/bluetooth/s1*,\ +./ports/stm32/usbhost,\ +./tests,\ +ACKNOWLEDGEMENTS,\ +""" +# CIRCUITPY-CHANGES: additions and removals [tool.ruff] -target-version = "py37" - -line-length = 99 -# Exclude third-party code from linting and formatting. Ruff doesn't know how to ignore submodules. -# Exclude the following tests: -# repl_: not real python files -# viper_args: uses f(*) +# Exclude third-party code from linting and formatting extend-exclude = [ "extmod/ulab", "frozen", @@ -28,8 +37,6 @@ extend-exclude = [ "ports/silabs/gecko_sdk", "ports/silabs/tools/slc_cli_linux", "ports/stm/st_driver", - "tests/*/repl_*.py", - "tests/micropython/viper_args.py", "tools/adabot", "tools/bitmap_font", "tools/cc1", @@ -38,11 +45,27 @@ extend-exclude = [ "tools/python-semver", "tools/uf2", ] -# Exclude third-party code, and exclude the following tests: -# basics: needs careful attention before applying automatic formatting -format.exclude = [ "tests/basics/*.py" ] -lint.extend-select = [ "C9", "PLC" ] -lint.ignore = [ +# Include Python source files that don't end with .py +line-length = 99 +target-version = "py38" + +[tool.ruff.lint] +exclude = [ # Ruff finds Python SyntaxError in these files + "tests/cmdline/cmd_compile_only_error.py", + "tests/cmdline/repl_autocomplete.py", + "tests/cmdline/repl_autocomplete_underscore.py", + "tests/cmdline/repl_autoindent.py", + "tests/cmdline/repl_basic.py", + "tests/cmdline/repl_cont.py", + "tests/cmdline/repl_emacs_keys.py", + "tests/cmdline/repl_paste.py", + "tests/cmdline/repl_words_move.py", + "tests/feature_check/repl_emacs_check.py", + "tests/feature_check/repl_words_move_check.py", + "tests/micropython/viper_args.py", +] +extend-select = ["C9", "PLC"] +extend-ignore = [ "E401", "E402", "E722", @@ -51,35 +74,30 @@ lint.ignore = [ "F401", "F403", "F405", - "PLC1901", + "PLC0206", + "PLC0415", # conditional imports are common in MicroPython ] -# manifest.py files are evaluated with some global names pre-defined -lint.per-file-ignores."**/manifest.py" = [ "F821" ] -lint.per-file-ignores."ports/**/boards/**/manifest_*.py" = [ "F821" ] -# Exclude all tests from linting (does not apply to formatting). -lint.per-file-ignores."tests/**/*.py" = [ "ALL" ] -lint.mccabe.max-complexity = 40 +mccabe.max-complexity = 40 -[tool.codespell] -count = "" -ignore-regex = '\b[A-Z]{3}\b' -ignore-words-list = "ans,asend,aranges,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" -quiet-level = 3 -skip = """ -*/build*,\ -./.git,\ -./drivers/cc3100,\ -./lib,\ -./ports/cc3200/FreeRTOS,\ -./ports/cc3200/bootmgr/sl,\ -./ports/cc3200/hal,\ -./ports/cc3200/simplelink,\ -./ports/cc3200/telnet,\ -./ports/esp32/managed_components,\ -./ports/nrf/drivers/bluetooth/s1*,\ -./ports/stm32/usbhost,\ -./tests,\ -ACKNOWLEDGEMENTS,\ -""" +[tool.ruff.lint.per-file-ignores] +# Exclude all tests from linting. +"tests/**/*.py" = ["ALL"] +"ports/cc3200/tools/uniflash.py" = ["E711"] +# manifest.py files are evaluated with some global names pre-defined +"**/manifest.py" = ["F821"] +"ports/**/boards/**/manifest_*.py" = ["F821"] +# Uses assignment expressions. +"tests/cpydiff/syntax_assign_expr.py" = ["F821"] -# Ruff settings copied from MicroPython +[tool.ruff.format] +# Exclude third-party code, and exclude the following tests: +# basics: needs careful attention before applying automatic formatting +# repl_: not real python files +# viper_args: uses f(*) +exclude = [ + "tests/basics/*.py", + "tests/*/repl_*.py", + "tests/cmdline/cmd_compile_only_error.py", + "tests/micropython/test_normalize_newlines.py", + "tests/micropython/viper_args.py", +] diff --git a/shared-bindings/_bleio/__init__.c b/shared-bindings/_bleio/__init__.c index 3bf8c1a2c9c56..1ec9fd96e766b 100644 --- a/shared-bindings/_bleio/__init__.c +++ b/shared-bindings/_bleio/__init__.c @@ -56,7 +56,7 @@ //| //| MP_DEFINE_BLEIO_EXCEPTION(BluetoothError, Exception) -NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t fmt, ...) { +MP_NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_bleio_BluetoothError, fmt, argptr); @@ -72,7 +72,7 @@ NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t fmt, ...) { //| //| MP_DEFINE_BLEIO_EXCEPTION(RoleError, bleio_BluetoothError) -NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg) { +MP_NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_bleio_RoleError, msg); } @@ -83,7 +83,7 @@ NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg) { //| //| MP_DEFINE_BLEIO_EXCEPTION(SecurityError, bleio_BluetoothError) -NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t fmt, ...) { +MP_NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_bleio_SecurityError, fmt, argptr); diff --git a/shared-bindings/_bleio/__init__.h b/shared-bindings/_bleio/__init__.h index f7428d2fb2138..0c5a5fec06b29 100644 --- a/shared-bindings/_bleio/__init__.h +++ b/shared-bindings/_bleio/__init__.h @@ -45,9 +45,9 @@ void common_hal_bleio_init(void); extern mp_obj_t bleio_set_adapter(mp_obj_t adapter_obj); -NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t msg, ...); -NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg); -NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t msg, ...); +MP_NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t msg, ...); +MP_NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t msg, ...); bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void); void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); diff --git a/shared-bindings/adafruit_pixelbuf/PixelBuf.c b/shared-bindings/adafruit_pixelbuf/PixelBuf.c index cdffbfa627aa1..4144f086b98d6 100644 --- a/shared-bindings/adafruit_pixelbuf/PixelBuf.c +++ b/shared-bindings/adafruit_pixelbuf/PixelBuf.c @@ -24,7 +24,7 @@ #include "extmod/ulab/code/ndarray.h" #endif -static NORETURN void invalid_byteorder(void) { +static MP_NORETURN void invalid_byteorder(void) { mp_arg_error_invalid(MP_QSTR_byteorder); } diff --git a/shared-bindings/alarm/__init__.h b/shared-bindings/alarm/__init__.h index af92b381137c8..37bfd9c51b7cd 100644 --- a/shared-bindings/alarm/__init__.h +++ b/shared-bindings/alarm/__init__.h @@ -24,7 +24,7 @@ extern mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const // it will exit idle as if deep sleep was exited extern void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms, size_t n_dios, digitalio_digitalinout_obj_t **preserve_dios); -extern NORETURN void common_hal_alarm_enter_deep_sleep(void); +extern MP_NORETURN void common_hal_alarm_enter_deep_sleep(void); // May be used to re-initialize peripherals like GPIO, if the VM reset returned // them to a default state diff --git a/shared-bindings/math/__init__.c b/shared-bindings/math/__init__.c index 54fe53280ca8e..49ca2958d8c96 100644 --- a/shared-bindings/math/__init__.c +++ b/shared-bindings/math/__init__.c @@ -26,7 +26,7 @@ //| """ //| -static NORETURN void math_error(void) { +static MP_NORETURN void math_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("math domain error")); } diff --git a/shared-bindings/memorymonitor/__init__.c b/shared-bindings/memorymonitor/__init__.c index 64d0140ff7930..e364344b363bd 100644 --- a/shared-bindings/memorymonitor/__init__.c +++ b/shared-bindings/memorymonitor/__init__.c @@ -25,7 +25,7 @@ //| MP_DEFINE_MEMORYMONITOR_EXCEPTION(AllocationError, Exception) -NORETURN void mp_raise_memorymonitor_AllocationError(mp_rom_error_text_t fmt, ...) { +MP_NORETURN void mp_raise_memorymonitor_AllocationError(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_memorymonitor_AllocationError, fmt, argptr); diff --git a/shared-bindings/memorymonitor/__init__.h b/shared-bindings/memorymonitor/__init__.h index 28480bea7b52b..5f141b924d09d 100644 --- a/shared-bindings/memorymonitor/__init__.h +++ b/shared-bindings/memorymonitor/__init__.h @@ -23,4 +23,4 @@ void memorymonitor_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_pr extern const mp_obj_type_t mp_type_memorymonitor_AllocationError; -NORETURN void mp_raise_memorymonitor_AllocationError(mp_rom_error_text_t msg, ...); +MP_NORETURN void mp_raise_memorymonitor_AllocationError(mp_rom_error_text_t msg, ...); diff --git a/shared-bindings/microcontroller/Pin.c b/shared-bindings/microcontroller/Pin.c index e74d54b0770a7..1fcde50e060b6 100644 --- a/shared-bindings/microcontroller/Pin.c +++ b/shared-bindings/microcontroller/Pin.c @@ -175,14 +175,14 @@ void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, } } -NORETURN void raise_ValueError_invalid_pin(void) { +MP_NORETURN void raise_ValueError_invalid_pin(void) { mp_arg_error_invalid(MP_QSTR_pin); } -NORETURN void raise_ValueError_invalid_pins(void) { +MP_NORETURN void raise_ValueError_invalid_pins(void) { mp_arg_error_invalid(MP_QSTR_pins); } -NORETURN void raise_ValueError_invalid_pin_name(qstr pin_name) { +MP_NORETURN void raise_ValueError_invalid_pin_name(qstr pin_name) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q pin"), pin_name); } diff --git a/shared-bindings/microcontroller/Pin.h b/shared-bindings/microcontroller/Pin.h index 1245b5f23e4a7..8ddca71bfbbe3 100644 --- a/shared-bindings/microcontroller/Pin.h +++ b/shared-bindings/microcontroller/Pin.h @@ -21,9 +21,9 @@ void validate_no_duplicate_pins(mp_obj_t seq, qstr arg_name); void validate_no_duplicate_pins_2(mp_obj_t seq1, mp_obj_t seq2, qstr arg_name1, qstr arg_name2); void validate_list_is_free_pins(qstr what, const mcu_pin_obj_t **pins_out, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out); void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out); -NORETURN void raise_ValueError_invalid_pin(void); -NORETURN void raise_ValueError_invalid_pins(void); -NORETURN void raise_ValueError_invalid_pin_name(qstr pin_name); +MP_NORETURN void raise_ValueError_invalid_pin(void); +MP_NORETURN void raise_ValueError_invalid_pins(void); +MP_NORETURN void raise_ValueError_invalid_pin_name(qstr pin_name); void assert_pin_free(const mcu_pin_obj_t *pin); diff --git a/shared-bindings/microcontroller/__init__.h b/shared-bindings/microcontroller/__init__.h index 2a4a973278162..5c5902ff965f5 100644 --- a/shared-bindings/microcontroller/__init__.h +++ b/shared-bindings/microcontroller/__init__.h @@ -20,7 +20,7 @@ extern void common_hal_mcu_disable_interrupts(void); extern void common_hal_mcu_enable_interrupts(void); extern void common_hal_mcu_on_next_reset(mcu_runmode_t runmode); -NORETURN extern void common_hal_mcu_reset(void); +MP_NORETURN extern void common_hal_mcu_reset(void); extern const mp_obj_dict_t mcu_pin_globals; diff --git a/shared-bindings/paralleldisplaybus/__init__.c b/shared-bindings/paralleldisplaybus/__init__.c index 24d968c9e3111..19a381570bada 100644 --- a/shared-bindings/paralleldisplaybus/__init__.c +++ b/shared-bindings/paralleldisplaybus/__init__.c @@ -29,6 +29,3 @@ const mp_obj_module_t paralleldisplaybus_module = { }; MP_REGISTER_MODULE(MP_QSTR_paralleldisplaybus, paralleldisplaybus_module); - -// Remove in CircuitPython 10 -MP_REGISTER_MODULE(MP_QSTR_paralleldisplay, paralleldisplaybus_module); diff --git a/shared-bindings/socketpool/SocketPool.c b/shared-bindings/socketpool/SocketPool.c index 06b63b80ea74d..ee35429926f69 100644 --- a/shared-bindings/socketpool/SocketPool.c +++ b/shared-bindings/socketpool/SocketPool.c @@ -189,7 +189,7 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &socketpool_socketpool_locals_dict ); -MP_WEAK NORETURN +MP_WEAK MP_NORETURN void common_hal_socketpool_socketpool_raise_gaierror_noname(void) { vstr_t vstr; mp_print_t print; diff --git a/shared-bindings/socketpool/SocketPool.h b/shared-bindings/socketpool/SocketPool.h index 36035ed00e11a..60d58372b9131 100644 --- a/shared-bindings/socketpool/SocketPool.h +++ b/shared-bindings/socketpool/SocketPool.h @@ -25,6 +25,6 @@ bool socketpool_socket(socketpool_socketpool_obj_t *self, socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, int proto, socketpool_socket_obj_t *sock); -NORETURN void common_hal_socketpool_socketpool_raise_gaierror_noname(void); +MP_NORETURN void common_hal_socketpool_socketpool_raise_gaierror_noname(void); mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *self, const char *host, int port, int family, int type, int proto, int flags); diff --git a/shared-bindings/storage/__init__.h b/shared-bindings/storage/__init__.h index ed3e8d44e4aed..6df604262956a 100644 --- a/shared-bindings/storage/__init__.h +++ b/shared-bindings/storage/__init__.h @@ -16,7 +16,7 @@ void common_hal_storage_umount_path(const char *path); void common_hal_storage_umount_object(mp_obj_t vfs_obj); void common_hal_storage_remount(const char *path, bool readonly, bool disable_concurrent_write_protection); mp_obj_t common_hal_storage_getmount(const char *path); -NORETURN void common_hal_storage_erase_filesystem(bool extended); +MP_NORETURN void common_hal_storage_erase_filesystem(bool extended); bool common_hal_storage_disable_usb_drive(void); bool common_hal_storage_enable_usb_drive(void); diff --git a/shared-bindings/usb/core/__init__.c b/shared-bindings/usb/core/__init__.c index 545e182ea99a3..89cec5bbe1de5 100644 --- a/shared-bindings/usb/core/__init__.c +++ b/shared-bindings/usb/core/__init__.c @@ -30,7 +30,7 @@ //| //| MP_DEFINE_USB_CORE_EXCEPTION(USBError, OSError) -NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...) { +MP_NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...) { mp_obj_t exception; if (fmt == NULL) { exception = mp_obj_new_exception(&mp_type_usb_core_USBError); @@ -50,7 +50,7 @@ NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...) { //| //| MP_DEFINE_USB_CORE_EXCEPTION(USBTimeoutError, usb_core_USBError) -NORETURN void mp_raise_usb_core_USBTimeoutError(void) { +MP_NORETURN void mp_raise_usb_core_USBTimeoutError(void) { mp_raise_type(&mp_type_usb_core_USBTimeoutError); } diff --git a/shared-bindings/usb/core/__init__.h b/shared-bindings/usb/core/__init__.h index 0dc08fae53257..adb8430a6c382 100644 --- a/shared-bindings/usb/core/__init__.h +++ b/shared-bindings/usb/core/__init__.h @@ -25,8 +25,8 @@ void usb_core_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_k extern const mp_obj_type_t mp_type_usb_core_USBError; extern const mp_obj_type_t mp_type_usb_core_USBTimeoutError; -NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_usb_core_USBTimeoutError(void); +MP_NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_usb_core_USBTimeoutError(void); // Find is all Python object oriented so we don't need a separate common-hal API // for it. It uses the device common-hal instead. diff --git a/shared-bindings/util.h b/shared-bindings/util.h index d53e5c6e8da13..11f0a00db71ea 100644 --- a/shared-bindings/util.h +++ b/shared-bindings/util.h @@ -9,7 +9,7 @@ #include "py/mpprint.h" #include "py/runtime.h" -NORETURN void raise_deinited_error(void); +MP_NORETURN void raise_deinited_error(void); void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_arg_t *properties, size_t n_properties); void properties_construct_helper(mp_obj_t self_in, const mp_arg_t *args, const mp_arg_val_t *vals, size_t n_properties); bool path_exists(const char *path); diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 8911fa2f454d8..fa1de8bb484aa 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -44,7 +44,7 @@ static void mbedtls_debug(void *ctx, int level, const char *file, int line, cons #define DEBUG_PRINT(...) do {} while (0) #endif -static NORETURN void mbedtls_raise_error(int err) { +static MP_NORETURN void mbedtls_raise_error(int err) { // _mbedtls_ssl_send and _mbedtls_ssl_recv (below) turn positive error codes from the // underlying socket into negative codes to pass them through mbedtls. Here we turn them // positive again so they get interpreted as the OSError they really are. The diff --git a/shared-module/struct/__init__.c b/shared-module/struct/__init__.c index 715b504302115..4b6b2b589efc8 100644 --- a/shared-module/struct/__init__.c +++ b/shared-module/struct/__init__.c @@ -14,7 +14,8 @@ #include "shared-bindings/struct/__init__.h" static void struct_validate_format(char fmt) { - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES + if (fmt == 'S' || fmt == 'O') { mp_raise_RuntimeError(MP_ERROR_TEXT("'S' and 'O' are not supported format types")); } diff --git a/shared/libc/abort_.c b/shared/libc/abort_.c index 3051eae81e0ce..54eab67d3fb49 100644 --- a/shared/libc/abort_.c +++ b/shared/libc/abort_.c @@ -1,7 +1,7 @@ #include -NORETURN void abort_(void); +MP_NORETURN void abort_(void); -NORETURN void abort_(void) { +MP_NORETURN void abort_(void) { mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("abort() called")); } diff --git a/shared/memzip/make-memzip.py b/shared/memzip/make-memzip.py index 92a5d6168bb1d..e406c55a43ca7 100755 --- a/shared/memzip/make-memzip.py +++ b/shared/memzip/make-memzip.py @@ -7,8 +7,6 @@ # This is somewhat like frozen modules in python, but allows arbitrary files # to be used. -from __future__ import print_function - import argparse import os import subprocess diff --git a/shared/netutils/dhcpserver.c b/shared/netutils/dhcpserver.c deleted file mode 100644 index 6d9cdb97d1fc6..0000000000000 --- a/shared/netutils/dhcpserver.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2018-2019 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -// For DHCP specs see: -// https://www.ietf.org/rfc/rfc2131.txt -// https://tools.ietf.org/html/rfc2132 -- DHCP Options and BOOTP Vendor Extensions - -#include -#include -#include "py/mperrno.h" -#include "py/mphal.h" -#include "lwip/opt.h" - -// CIRCUITPY-CHANGE: comment -// Used in CIRCUITPY without MICROPY_PY_LWIP - -#if LWIP_UDP - -#include "shared/netutils/dhcpserver.h" -#include "lwip/udp.h" - -#define DHCPDISCOVER (1) -#define DHCPOFFER (2) -#define DHCPREQUEST (3) -#define DHCPDECLINE (4) -#define DHCPACK (5) -#define DHCPNACK (6) -#define DHCPRELEASE (7) -#define DHCPINFORM (8) - -#define DHCP_OPT_PAD (0) -#define DHCP_OPT_SUBNET_MASK (1) -#define DHCP_OPT_ROUTER (3) -#define DHCP_OPT_DNS (6) -#define DHCP_OPT_HOST_NAME (12) -#define DHCP_OPT_REQUESTED_IP (50) -#define DHCP_OPT_IP_LEASE_TIME (51) -#define DHCP_OPT_MSG_TYPE (53) -#define DHCP_OPT_SERVER_ID (54) -#define DHCP_OPT_PARAM_REQUEST_LIST (55) -#define DHCP_OPT_MAX_MSG_SIZE (57) -#define DHCP_OPT_VENDOR_CLASS_ID (60) -#define DHCP_OPT_CLIENT_ID (61) -#define DHCP_OPT_END (255) - -#define PORT_DHCP_SERVER (67) -#define PORT_DHCP_CLIENT (68) - -#define DEFAULT_DNS MAKE_IP4(192, 168, 4, 1) -#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds - -#define MAC_LEN (6) -#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) - -typedef struct { - uint8_t op; // message opcode - uint8_t htype; // hardware address type - uint8_t hlen; // hardware address length - uint8_t hops; - uint32_t xid; // transaction id, chosen by client - uint16_t secs; // client seconds elapsed - uint16_t flags; - uint8_t ciaddr[4]; // client IP address - uint8_t yiaddr[4]; // your IP address - uint8_t siaddr[4]; // next server IP address - uint8_t giaddr[4]; // relay agent IP address - uint8_t chaddr[16]; // client hardware address - uint8_t sname[64]; // server host name - uint8_t file[128]; // boot file name - uint8_t options[312]; // optional parameters, variable, starts with magic -} dhcp_msg_t; - -static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) { - // family is AF_INET - // type is SOCK_DGRAM - - *udp = udp_new(); - if (*udp == NULL) { - return -MP_ENOMEM; - } - - // Register callback - udp_recv(*udp, cb_udp_recv, (void *)cb_data); - - return 0; // success -} - -static void dhcp_socket_free(struct udp_pcb **udp) { - if (*udp != NULL) { - udp_remove(*udp); - *udp = NULL; - } -} - -static int dhcp_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) { - ip_addr_t addr; - IP_ADDR4(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); - // TODO convert lwIP errors to errno - return udp_bind(*udp, &addr, port); -} - -static int dhcp_socket_sendto(struct udp_pcb **udp, struct netif *netif, const void *buf, size_t len, uint32_t ip, uint16_t port) { - if (len > 0xffff) { - len = 0xffff; - } - - struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); - if (p == NULL) { - return -MP_ENOMEM; - } - - memcpy(p->payload, buf, len); - - ip_addr_t dest; - IP_ADDR4(&dest, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); - err_t err; - if (netif != NULL) { - err = udp_sendto_if(*udp, p, &dest, port, netif); - } else { - err = udp_sendto(*udp, p, &dest, port); - } - - pbuf_free(p); - - if (err != ERR_OK) { - return err; - } - - return len; -} - -static uint8_t *opt_find(uint8_t *opt, uint8_t cmd) { - for (int i = 0; i < 308 && opt[i] != DHCP_OPT_END;) { - if (opt[i] == cmd) { - return &opt[i]; - } - i += 2 + opt[i + 1]; - } - return NULL; -} - -static void opt_write_n(uint8_t **opt, uint8_t cmd, size_t n, const void *data) { - uint8_t *o = *opt; - *o++ = cmd; - *o++ = n; - memcpy(o, data, n); - *opt = o + n; -} - -static void opt_write_u8(uint8_t **opt, uint8_t cmd, uint8_t val) { - uint8_t *o = *opt; - *o++ = cmd; - *o++ = 1; - *o++ = val; - *opt = o; -} - -static void opt_write_u32(uint8_t **opt, uint8_t cmd, uint32_t val) { - uint8_t *o = *opt; - *o++ = cmd; - *o++ = 4; - *o++ = val >> 24; - *o++ = val >> 16; - *o++ = val >> 8; - *o++ = val; - *opt = o; -} - -static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) { - dhcp_server_t *d = arg; - (void)upcb; - (void)src_addr; - (void)src_port; - - // This is around 548 bytes - dhcp_msg_t dhcp_msg; - - #define DHCP_MIN_SIZE (240 + 3) - if (p->tot_len < DHCP_MIN_SIZE) { - goto ignore_request; - } - - size_t len = pbuf_copy_partial(p, &dhcp_msg, sizeof(dhcp_msg), 0); - if (len < DHCP_MIN_SIZE) { - goto ignore_request; - } - - dhcp_msg.op = DHCPOFFER; - memcpy(&dhcp_msg.yiaddr, &ip_2_ip4(&d->ip)->addr, 4); - - uint8_t *opt = (uint8_t *)&dhcp_msg.options; - opt += 4; // assume magic cookie: 99, 130, 83, 99 - - switch (opt[2]) { - case DHCPDISCOVER: { - int yi = DHCPS_MAX_IP; - for (int i = 0; i < DHCPS_MAX_IP; ++i) { - if (memcmp(d->lease[i].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { - // MAC match, use this IP address - yi = i; - break; - } - if (yi == DHCPS_MAX_IP) { - // Look for a free IP address - if (memcmp(d->lease[i].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { - // IP available - yi = i; - } - uint32_t expiry = d->lease[i].expiry << 16 | 0xffff; - if ((int32_t)(expiry - mp_hal_ticks_ms()) < 0) { - // IP expired, reuse it - memset(d->lease[i].mac, 0, MAC_LEN); - yi = i; - } - } - } - if (yi == DHCPS_MAX_IP) { - // No more IP addresses left - goto ignore_request; - } - dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; - opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPOFFER); - break; - } - - case DHCPREQUEST: { - uint8_t *o = opt_find(opt, DHCP_OPT_REQUESTED_IP); - if (o == NULL) { - // Should be NACK - goto ignore_request; - } - if (memcmp(o + 2, &ip_2_ip4(&d->ip)->addr, 3) != 0) { - // Should be NACK - goto ignore_request; - } - uint8_t yi = o[5] - DHCPS_BASE_IP; - if (yi >= DHCPS_MAX_IP) { - // Should be NACK - goto ignore_request; - } - if (memcmp(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { - // MAC match, ok to use this IP address - } else if (memcmp(d->lease[yi].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { - // IP unused, ok to use this IP address - memcpy(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN); - } else { - // IP already in use - // Should be NACK - goto ignore_request; - } - d->lease[yi].expiry = (mp_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16; - dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; - opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK); - // CIRCUITPY-CHANGE: use LWIP_DEBUGF instead of printf - LWIP_DEBUGF(DHCP_DEBUG, ("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n", - dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5], - dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3])); - break; - } - - default: - goto ignore_request; - } - - opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &ip_2_ip4(&d->ip)->addr); - opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &ip_2_ip4(&d->nm)->addr); - opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &ip_2_ip4(&d->ip)->addr); // aka gateway; can have multiple addresses - opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have multiple addresses - opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S); - *opt++ = DHCP_OPT_END; - struct netif *netif = ip_current_input_netif(); - dhcp_socket_sendto(&d->udp, netif, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, 0xffffffff, PORT_DHCP_CLIENT); - -ignore_request: - pbuf_free(p); -} - -void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { - ip_addr_copy(d->ip, *ip); - ip_addr_copy(d->nm, *nm); - memset(d->lease, 0, sizeof(d->lease)); - if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) { - return; - } - dhcp_socket_bind(&d->udp, 0, PORT_DHCP_SERVER); -} - -void dhcp_server_deinit(dhcp_server_t *d) { - dhcp_socket_free(&d->udp); -} - -#endif // MICROPY_PY_LWIP diff --git a/shared/netutils/netutils.c b/shared/netutils/netutils.c deleted file mode 100644 index cd1422f7c8058..0000000000000 --- a/shared/netutils/netutils.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * Copyright (c) 2015 Daniel Campora - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include -#include -#include - -#include "py/runtime.h" -#include "shared/netutils/netutils.h" - -// Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. -mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian) { - char ip_str[16]; - mp_uint_t ip_len; - if (endian == NETUTILS_LITTLE) { - ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[3], ip[2], ip[1], ip[0]); - } else { - ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); - } - return mp_obj_new_str(ip_str, ip_len); -} - -// Takes an array with a raw IP address, and a port, and returns a net-address -// tuple such as ('192.168.0.1', 8080). -mp_obj_t netutils_format_inet_addr(uint8_t *ip, mp_uint_t port, netutils_endian_t endian) { - mp_obj_t tuple[2] = { - tuple[0] = netutils_format_ipv4_addr(ip, endian), - tuple[1] = mp_obj_new_int(port), - }; - return mp_obj_new_tuple(2, tuple); -} - -void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian) { - size_t addr_len; - const char *addr_str = mp_obj_str_get_data(addr_in, &addr_len); - if (addr_len == 0) { - // special case of no address given - memset(out_ip, 0, NETUTILS_IPV4ADDR_BUFSIZE); - return; - } - const char *s = addr_str; - const char *s_top; - // Scan for the end of valid address characters - for (s_top = addr_str; s_top < addr_str + addr_len; s_top++) { - if (!(*s_top == '.' || (*s_top >= '0' && *s_top <= '9'))) { - break; - } - } - for (mp_uint_t i = 3; ; i--) { - mp_uint_t val = 0; - for (; s < s_top && *s != '.'; s++) { - val = val * 10 + *s - '0'; - } - if (endian == NETUTILS_LITTLE) { - out_ip[i] = val; - } else { - out_ip[NETUTILS_IPV4ADDR_BUFSIZE - 1 - i] = val; - } - if (i == 0 && s == s_top) { - return; - } else if (i > 0 && s < s_top && *s == '.') { - s++; - } else { - mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments")); - } - } -} - -// Takes an address of the form ('192.168.0.1', 8080), returns the port and -// puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). -mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian) { - mp_obj_t *addr_items; - mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); - netutils_parse_ipv4_addr(addr_items[0], out_ip, endian); - return mp_obj_get_int(addr_items[1]); -} diff --git a/shared/netutils/netutils.h b/shared/netutils/netutils.h deleted file mode 100644 index f82960ba800bb..0000000000000 --- a/shared/netutils/netutils.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2013, 2014 Damien P. George - * Copyright (c) 2015 Daniel Campora - * - * 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_LIB_NETUTILS_NETUTILS_H -#define MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H - -#include "py/obj.h" - -#define NETUTILS_IPV4ADDR_BUFSIZE 4 - -#define NETUTILS_TRACE_IS_TX (0x0001) -#define NETUTILS_TRACE_PAYLOAD (0x0002) -#define NETUTILS_TRACE_NEWLINE (0x0004) - -typedef enum _netutils_endian_t { - NETUTILS_LITTLE, - NETUTILS_BIG, -} netutils_endian_t; - -// Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. -mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian); - -// Takes an array with a raw IP address, and a port, and returns a net-address -// tuple such as ('192.168.0.1', 8080). -mp_obj_t netutils_format_inet_addr(uint8_t *ip, mp_uint_t port, netutils_endian_t endian); - -void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); - -// Takes an address of the form ('192.168.0.1', 8080), returns the port and -// puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). -mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); - -void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags); - -#endif // MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H diff --git a/shared/netutils/trace.c b/shared/netutils/trace.c deleted file mode 100644 index a6dfb42c28f00..0000000000000 --- a/shared/netutils/trace.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include "py/mphal.h" -#include "shared/netutils/netutils.h" - -static uint32_t get_be16(const uint8_t *buf) { - return buf[0] << 8 | buf[1]; -} - -static uint32_t get_be32(const uint8_t *buf) { - return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; -} - -static void dump_hex_bytes(const mp_print_t *print, size_t len, const uint8_t *buf) { - for (size_t i = 0; i < len; ++i) { - mp_printf(print, " %02x", buf[i]); - } -} - -static const char *ethertype_str(uint16_t type) { - // A value between 0x0000 - 0x05dc (inclusive) indicates a length, not type - switch (type) { - case 0x0800: - return "IPv4"; - case 0x0806: - return "ARP"; - case 0x86dd: - return "IPv6"; - default: - return NULL; - } -} - -void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags) { - mp_printf(print, "[% 8d] ETH%cX len=%u", mp_hal_ticks_ms(), flags & NETUTILS_TRACE_IS_TX ? 'T' : 'R', len); - mp_printf(print, " dst=%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - mp_printf(print, " src=%02x:%02x:%02x:%02x:%02x:%02x", buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]); - - const char *ethertype = ethertype_str(buf[12] << 8 | buf[13]); - if (ethertype) { - mp_printf(print, " type=%s", ethertype); - } else { - mp_printf(print, " type=0x%04x", buf[12] << 8 | buf[13]); - } - if (len > 14) { - len -= 14; - buf += 14; - if (buf[-2] == 0x08 && buf[-1] == 0x00 && buf[0] == 0x45) { - // IPv4 packet - len = get_be16(buf + 2); - mp_printf(print, " srcip=%u.%u.%u.%u dstip=%u.%u.%u.%u", - buf[12], buf[13], buf[14], buf[15], - buf[16], buf[17], buf[18], buf[19]); - uint8_t prot = buf[9]; - buf += 20; - len -= 20; - if (prot == 6) { - // TCP packet - uint16_t srcport = get_be16(buf); - uint16_t dstport = get_be16(buf + 2); - uint32_t seqnum = get_be32(buf + 4); - uint32_t acknum = get_be32(buf + 8); - uint16_t dataoff_flags = get_be16(buf + 12); - uint16_t winsz = get_be16(buf + 14); - mp_printf(print, " TCP srcport=%u dstport=%u seqnum=%u acknum=%u dataoff=%u flags=%x winsz=%u", - srcport, dstport, (unsigned)seqnum, (unsigned)acknum, dataoff_flags >> 12, dataoff_flags & 0x1ff, winsz); - buf += 20; - len -= 20; - if (dataoff_flags >> 12 > 5) { - mp_printf(print, " opts="); - size_t opts_len = ((dataoff_flags >> 12) - 5) * 4; - dump_hex_bytes(print, opts_len, buf); - buf += opts_len; - len -= opts_len; - } - } else if (prot == 17) { - // UDP packet - uint16_t srcport = get_be16(buf); - uint16_t dstport = get_be16(buf + 2); - mp_printf(print, " UDP srcport=%u dstport=%u", srcport, dstport); - len = get_be16(buf + 4); - buf += 8; - if ((srcport == 67 && dstport == 68) || (srcport == 68 && dstport == 67)) { - // DHCP - if (srcport == 67) { - mp_printf(print, " DHCPS"); - } else { - mp_printf(print, " DHCPC"); - } - dump_hex_bytes(print, 12 + 16 + 16 + 64, buf); - size_t n = 12 + 16 + 16 + 64 + 128; - len -= n; - buf += n; - mp_printf(print, " opts:"); - switch (buf[6]) { - case 1: - mp_printf(print, " DISCOVER"); - break; - case 2: - mp_printf(print, " OFFER"); - break; - case 3: - mp_printf(print, " REQUEST"); - break; - case 4: - mp_printf(print, " DECLINE"); - break; - case 5: - mp_printf(print, " ACK"); - break; - case 6: - mp_printf(print, " NACK"); - break; - case 7: - mp_printf(print, " RELEASE"); - break; - case 8: - mp_printf(print, " INFORM"); - break; - } - } - } else { - // Non-UDP packet - mp_printf(print, " prot=%u", prot); - } - } else if (buf[-2] == 0x86 && buf[-1] == 0xdd && (buf[0] >> 4) == 6) { - // IPv6 packet - uint32_t h = get_be32(buf); - uint16_t l = get_be16(buf + 4); - mp_printf(print, " tclass=%u flow=%u len=%u nexthdr=%u hoplimit=%u", (unsigned)((h >> 20) & 0xff), (unsigned)(h & 0xfffff), l, buf[6], buf[7]); - mp_printf(print, " srcip="); - dump_hex_bytes(print, 16, buf + 8); - mp_printf(print, " dstip="); - dump_hex_bytes(print, 16, buf + 24); - buf += 40; - len -= 40; - } - if (flags & NETUTILS_TRACE_PAYLOAD) { - mp_printf(print, " data="); - dump_hex_bytes(print, len, buf); - } - } - if (flags & NETUTILS_TRACE_NEWLINE) { - mp_printf(print, "\n"); - } -} diff --git a/shared/runtime/mpirq.c b/shared/runtime/mpirq.c index 6111b9b10cfe4..4d848ae7e9102 100644 --- a/shared/runtime/mpirq.c +++ b/shared/runtime/mpirq.c @@ -65,9 +65,19 @@ void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t p self->ishard = false; } -void mp_irq_handler(mp_irq_obj_t *self) { - if (self->handler != mp_const_none) { - if (self->ishard) { +int mp_irq_dispatch(mp_obj_t handler, mp_obj_t parent, bool ishard) { + int result = 0; + if (handler != mp_const_none) { + if (ishard) { + #if MICROPY_STACK_CHECK && MICROPY_STACK_SIZE_HARD_IRQ > 0 + // This callback executes in an ISR context so the stack-limit + // check must be changed to use the ISR stack for the duration + // of this function. + char *orig_stack_top = MP_STATE_THREAD(stack_top); + size_t orig_stack_limit = MP_STATE_THREAD(stack_limit); + mp_cstack_init_with_sp_here(MICROPY_STACK_SIZE_HARD_IRQ); + #endif + // When executing code within a handler we must lock the scheduler to // prevent any scheduled callbacks from running, and lock the GC to // prevent any memory allocations. @@ -75,22 +85,36 @@ void mp_irq_handler(mp_irq_obj_t *self) { gc_lock(); nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { - mp_call_function_1(self->handler, self->parent); + mp_call_function_1(handler, parent); nlr_pop(); } else { - // Uncaught exception; disable the callback so that it doesn't run again - self->methods->trigger(self->parent, 0); - self->handler = mp_const_none; mp_printf(MICROPY_ERROR_PRINTER, "Uncaught exception in IRQ callback handler\n"); mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); + result = -1; } gc_unlock(); mp_sched_unlock(); + + #if MICROPY_STACK_CHECK && MICROPY_STACK_SIZE_HARD_IRQ > 0 + // Restore original stack-limit checking values. + MP_STATE_THREAD(stack_top) = orig_stack_top; + MP_STATE_THREAD(stack_limit) = orig_stack_limit; + #endif } else { // Schedule call to user function - mp_sched_schedule(self->handler, self->parent); + mp_sched_schedule(handler, parent); } } + return result; +} + + +void mp_irq_handler(mp_irq_obj_t *self) { + if (mp_irq_dispatch(self->handler, self->parent, self->ishard) < 0) { + // Uncaught exception; disable the callback so that it doesn't run again + self->methods->trigger(self->parent, 0); + self->handler = mp_const_none; + } } /******************************************************************************/ diff --git a/shared/runtime/mpirq.h b/shared/runtime/mpirq.h index dd423c0101137..c65741e0e49d1 100644 --- a/shared/runtime/mpirq.h +++ b/shared/runtime/mpirq.h @@ -77,6 +77,7 @@ extern const mp_obj_type_t mp_irq_type; mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent); void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent); +int mp_irq_dispatch(mp_obj_t handler, mp_obj_t parent, bool ishard); void mp_irq_handler(mp_irq_obj_t *self); #endif // MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 06680ff2dd125..a44fdf724d1f9 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -38,12 +38,9 @@ #include "py/gc.h" #include "py/frozenmod.h" #include "py/mphal.h" -#if MICROPY_HW_ENABLE_USB -#include "irq.h" -#include "usb.h" -#endif #include "shared/readline/readline.h" #include "shared/runtime/pyexec.h" +#include "extmod/modplatform.h" #include "genhdr/mpversion.h" // CIRCUITPY-CHANGE: atexit support @@ -484,7 +481,9 @@ static int pyexec_friendly_repl_process_char(int c) { // CIRCUITPY-CHANGE: print CircuitPython-style banner. mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); mp_hal_stdout_tx_str("\r\n"); - // mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); + #if MICROPY_PY_BUILTINS_HELP + mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); + #endif goto input_restart; } else if (ret == CHAR_CTRL_C) { // break @@ -570,10 +569,20 @@ MP_REGISTER_ROOT_POINTER(vstr_t * repl_line); #else // MICROPY_REPL_EVENT_DRIVEN +#if !MICROPY_HAL_HAS_STDIO_MODE_SWITCH +// If the port doesn't need any stdio mode switching calls then provide trivial ones. +static inline void mp_hal_stdio_mode_raw(void) { +} +static inline void mp_hal_stdio_mode_orig(void) { +} +#endif + int pyexec_raw_repl(void) { vstr_t line; vstr_init(&line, 32); + mp_hal_stdio_mode_raw(); + raw_repl_reset: mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n"); @@ -587,6 +596,7 @@ int pyexec_raw_repl(void) { if (vstr_len(&line) == 2 && vstr_str(&line)[0] == CHAR_CTRL_E) { int ret = do_reader_stdin(vstr_str(&line)[1]); if (ret & PYEXEC_FORCED_EXIT) { + mp_hal_stdio_mode_orig(); return ret; } vstr_reset(&line); @@ -599,6 +609,7 @@ int pyexec_raw_repl(void) { mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; + mp_hal_stdio_mode_orig(); return 0; } else if (c == CHAR_CTRL_C) { // clear line @@ -619,14 +630,18 @@ int pyexec_raw_repl(void) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); + mp_hal_stdio_mode_orig(); return PYEXEC_FORCED_EXIT; } + // Switch to original terminal mode to execute code, eg to support keyboard interrupt (SIGINT). + mp_hal_stdio_mode_orig(); // CIRCUITPY-CHANGE: add last arg, handle reload int ret = parse_compile_execute(&line, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR, NULL); if (ret & (PYEXEC_FORCED_EXIT | PYEXEC_RELOAD)) { return ret; } + mp_hal_stdio_mode_raw(); } } @@ -634,6 +649,8 @@ int pyexec_friendly_repl(void) { vstr_t line; vstr_init(&line, 32); + mp_hal_stdio_mode_raw(); + friendly_repl_reset: // CIRCUITPY-CHANGE: CircuitPython-style banner. mp_hal_stdout_tx_str("\r\n"); @@ -661,20 +678,6 @@ int pyexec_friendly_repl(void) { for (;;) { input_restart: - - #if MICROPY_HW_ENABLE_USB - if (usb_vcp_is_enabled()) { - // If the user gets to here and interrupts are disabled then - // they'll never see the prompt, traceback etc. The USB REPL needs - // interrupts to be enabled or no transfers occur. So we try to - // do the user a favor and re-enable interrupts. - if (query_irq() == IRQ_STATE_DISABLED) { - enable_irq(IRQ_STATE_ENABLED); - mp_hal_stdout_tx_str("MPY: enabling IRQs\r\n"); - } - } - #endif - // If the GC is locked at this point there is no way out except a reset, // so force the GC to be unlocked to help the user debug what went wrong. if (MP_STATE_THREAD(gc_lock_depth) != 0) { @@ -705,6 +708,7 @@ int pyexec_friendly_repl(void) { mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_RAW_REPL; + mp_hal_stdio_mode_orig(); return 0; } else if (ret == CHAR_CTRL_B) { // reset friendly REPL @@ -718,6 +722,7 @@ int pyexec_friendly_repl(void) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); + mp_hal_stdio_mode_orig(); return PYEXEC_FORCED_EXIT; } else if (ret == CHAR_CTRL_E) { // paste mode @@ -762,6 +767,8 @@ int pyexec_friendly_repl(void) { } } + // Switch to original terminal mode to execute code, eg to support keyboard interrupt (SIGINT). + mp_hal_stdio_mode_orig(); // CIRCUITPY-CHANGE: add last arg ret = parse_compile_execute(&line, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR, NULL); if (ret & (PYEXEC_FORCED_EXIT | PYEXEC_RELOAD)) { diff --git a/shared/runtime/pyexec.h b/shared/runtime/pyexec.h index 55a3003791cb2..9d1ef2a3cb029 100644 --- a/shared/runtime/pyexec.h +++ b/shared/runtime/pyexec.h @@ -51,6 +51,17 @@ extern pyexec_mode_kind_t pyexec_mode_kind; #define PYEXEC_DEEP_SLEEP (0x400) #define PYEXEC_RELOAD (0x800) +#if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING +#define PYEXEC_NORMAL_EXIT (0) +#define PYEXEC_UNHANDLED_EXCEPTION (1) +#define PYEXEC_KEYBOARD_INTERRUPT (128 + 2) // same as SIG INT exit code +#define PYEXEC_ABORT (128 + 9) // same as SIG KILL exit code +#else +#define PYEXEC_NORMAL_EXIT (1) +#define PYEXEC_UNHANDLED_EXCEPTION (0) +#define PYEXEC_ABORT PYEXEC_FORCED_EXIT +#endif + int pyexec_raw_repl(void); int pyexec_friendly_repl(void); // CIRCUITPY-CHANGE: result out argument diff --git a/shared/timeutils/timeutils.c b/shared/timeutils/timeutils.c index 4282a0178dd6e..0c6916e06ddd6 100644 --- a/shared/timeutils/timeutils.c +++ b/shared/timeutils/timeutils.c @@ -29,12 +29,27 @@ #include "shared/timeutils/timeutils.h" -// LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately -// after Feb 29. We calculate seconds as a signed integer relative to that. +// To maintain reasonable compatibility with CPython on embedded systems, +// and avoid breaking anytime soon, timeutils functions are required to +// work properly between 1970 and 2099 on all ports. // -// Our timebase is relative to 2000-01-01. - -#define LEAPOCH ((31 + 29) * 86400) +// During that period of time, leap years occur every 4 years without +// exception, so we can keep the code short for 32 bit machines. + +// The last leap day before the required period is Feb 29, 1968. +// This is the number of days to add to get to that date. +#define PREV_LEAP_DAY ((mp_uint_t)(365 + 366 - (31 + 29))) +#define PREV_LEAP_YEAR 1968 + +// On ports where either MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND or +// MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE is enabled, we include extra +// code to support leap years outside of the 'easy' period. +// Computation is then made based on 1600 (a mod-400 year). +// This is the number of days between 1600 and 1968. +#define QC_BASE_DAY 134409 +#define QC_LEAP_YEAR 1600 +// This is the number of leap days between 1600 and 1970 +#define QC_LEAP_DAYS 89 #define DAYS_PER_400Y (365 * 400 + 97) #define DAYS_PER_100Y (365 * 100 + 24) @@ -42,8 +57,20 @@ static const uint16_t days_since_jan1[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; +// type used internally to count small integers relative to epoch +// (using uint when possible produces smaller code on some platforms) +#if MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE +typedef mp_int_t relint_t; +#else +typedef mp_uint_t relint_t; +#endif + bool timeutils_is_leap_year(mp_uint_t year) { + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; + #else + return year % 4 == 0; + #endif } // month is one based @@ -65,67 +92,67 @@ mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) { return yday; } -void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, timeutils_struct_time_t *tm) { - // The following algorithm was adapted from musl's __secs_to_tm and adapted - // for differences in MicroPython's timebase. - - mp_int_t seconds = t - LEAPOCH; +void timeutils_seconds_since_1970_to_struct_time(timeutils_timestamp_t seconds, timeutils_struct_time_t *tm) { + // The following algorithm was inspired from musl's __secs_to_tm + // and simplified to reduce code footprint in the simple case - mp_int_t days = seconds / 86400; + relint_t days = seconds / 86400; seconds %= 86400; + #if MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE if (seconds < 0) { seconds += 86400; days -= 1; } + #endif tm->tm_hour = seconds / 3600; tm->tm_min = seconds / 60 % 60; tm->tm_sec = seconds % 60; - mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2) + relint_t wday = (days + 3) % 7; // Jan 1, 1970 was a Thursday (3) + #if MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE if (wday < 0) { wday += 7; } + #endif tm->tm_wday = wday; - mp_int_t qc_cycles = days / DAYS_PER_400Y; + days += PREV_LEAP_DAY; + + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE + // rebase day to the oldest supported date (=> always positive) + mp_uint_t base_year = QC_LEAP_YEAR; + days += QC_BASE_DAY; + mp_uint_t qc_cycles = days / DAYS_PER_400Y; days %= DAYS_PER_400Y; - if (days < 0) { - days += DAYS_PER_400Y; - qc_cycles--; - } - mp_int_t c_cycles = days / DAYS_PER_100Y; + mp_uint_t c_cycles = days / DAYS_PER_100Y; if (c_cycles == 4) { c_cycles--; } days -= (c_cycles * DAYS_PER_100Y); - - mp_int_t q_cycles = days / DAYS_PER_4Y; + #else + mp_uint_t base_year = PREV_LEAP_YEAR; + mp_uint_t qc_cycles = 0; + mp_uint_t c_cycles = 0; + #endif + + mp_uint_t q_cycles = days / DAYS_PER_4Y; + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE if (q_cycles == 25) { q_cycles--; } + #endif days -= q_cycles * DAYS_PER_4Y; - mp_int_t years = days / 365; + relint_t years = days / 365; if (years == 4) { years--; } days -= (years * 365); - /* We will compute tm_yday at the very end - mp_int_t leap = !years && (q_cycles || !c_cycles); - - tm->tm_yday = days + 31 + 28 + leap; - if (tm->tm_yday >= 365 + leap) { - tm->tm_yday -= 365 + leap; - } - - tm->tm_yday++; // Make one based - */ - - tm->tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; + tm->tm_year = base_year + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; // Note: days_in_month[0] corresponds to March - static const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; + static const uint8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; mp_int_t month; for (month = 0; days_in_month[month] <= days; month++) { @@ -144,21 +171,28 @@ void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, timeutils_struct_t } // returns the number of seconds, as an integer, since 2000-01-01 -mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, +timeutils_timestamp_t timeutils_seconds_since_1970(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { - return - second - + minute * 60 - + hour * 3600 - + (timeutils_year_day(year, month, date) - 1 - + ((year - 2000 + 3) / 4) // add a day each 4 years starting with 2001 - - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001 - + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001 - ) * 86400 - + (year - 2000) * 31536000; + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE + mp_uint_t ref_year = QC_LEAP_YEAR; + #else + mp_uint_t ref_year = PREV_LEAP_YEAR; + #endif + timeutils_timestamp_t res; + res = ((relint_t)year - 1970) * 365; + res += (year - (ref_year + 1)) / 4; // add a day each 4 years + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE + res -= (year - (ref_year + 1)) / 100; // subtract a day each 100 years + res += (year - (ref_year + 1)) / 400; // add a day each 400 years + res -= QC_LEAP_DAYS; + #endif + res += timeutils_year_day(year, month, date) - 1; + res *= 86400; + res += hour * 3600 + minute * 60 + second; + return res; } -mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, +timeutils_timestamp_t timeutils_mktime_1970(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { // Normalize the tuple. This allows things like: @@ -211,12 +245,16 @@ mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, year++; } } - return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds); + return timeutils_seconds_since_1970(year, month, mday, hours, minutes, seconds); } // Calculate the weekday from the date. // The result is zero based with 0 = Monday. // by Michael Keith and Tom Craver, 1990. int timeutils_calc_weekday(int y, int m, int d) { - return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) + 6) % 7; + return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE + - y / 100 + y / 400 + #endif + ) + 6) % 7; } diff --git a/shared/timeutils/timeutils.h b/shared/timeutils/timeutils.h index b41903d3b2777..35356b462aafc 100644 --- a/shared/timeutils/timeutils.h +++ b/shared/timeutils/timeutils.h @@ -27,12 +27,23 @@ #ifndef MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H #define MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H -// CIRCUITPY-CHANGE: MICROPY_EPOCH_IS_1970 -#include "mpconfigport.h" +#include "py/obj.h" +#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#include // required for trunc() +#endif + +// `timeutils_timestamp_t` is the type used internally by timeutils to +// represent timestamps, and is always referenced to 1970. +// It may not match the platform-specific `mp_timestamp_t`. +#if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE +typedef long long timeutils_timestamp_t; +#else +typedef mp_uint_t timeutils_timestamp_t; +#endif // The number of seconds between 1970/1/1 and 2000/1/1 is calculated using: // time.mktime((2000,1,1,0,0,0,0,0,0)) - time.mktime((1970,1,1,0,0,0,0,0,0)) -#define TIMEUTILS_SECONDS_1970_TO_2000 (946684800ULL) +#define TIMEUTILS_SECONDS_1970_TO_2000 (946684800LL) typedef struct _timeutils_struct_time_t { uint16_t tm_year; // i.e. 2014 @@ -48,66 +59,116 @@ typedef struct _timeutils_struct_time_t { bool timeutils_is_leap_year(mp_uint_t year); mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month); mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date); +int timeutils_calc_weekday(int y, int m, int d); -void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, +void timeutils_seconds_since_1970_to_struct_time(timeutils_timestamp_t t, timeutils_struct_time_t *tm); // Year is absolute, month/date are 1-based, hour/minute/second are 0-based. -mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, +timeutils_timestamp_t timeutils_seconds_since_1970(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second); // Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. -mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, +timeutils_timestamp_t timeutils_mktime_1970(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds); +static inline mp_timestamp_t timeutils_obj_get_timestamp(mp_obj_t o_in) { + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE + mp_float_t val = mp_obj_get_float(o_in); + return (mp_timestamp_t)MICROPY_FLOAT_C_FUN(trunc)(val); + #elif MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_UINT + return mp_obj_get_uint(o_in); + #else + return mp_obj_get_ll(o_in); + #endif +} + +static inline mp_obj_t timeutils_obj_from_timestamp(mp_timestamp_t t) { + #if MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_UINT + return mp_obj_new_int_from_uint(t); + #else + return mp_obj_new_int_from_ll(t); + #endif +} + +static inline void timeutils_seconds_since_2000_to_struct_time(mp_timestamp_t t, timeutils_struct_time_t *tm) { + timeutils_seconds_since_1970_to_struct_time((timeutils_timestamp_t)(t + TIMEUTILS_SECONDS_1970_TO_2000), tm); +} + +// Year is absolute, month/date are 1-based, hour/minute/second are 0-based. +static inline mp_timestamp_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, + mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return (mp_timestamp_t)timeutils_seconds_since_1970(year, month, date, hour, minute, second) - TIMEUTILS_SECONDS_1970_TO_2000; +} + +// Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. +static inline mp_timestamp_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, + mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + return (mp_timestamp_t)timeutils_mktime_1970(year, month, mday, hours, minutes, seconds) - TIMEUTILS_SECONDS_1970_TO_2000; +} + + // Select the Epoch used by the port. #if MICROPY_EPOCH_IS_1970 -static inline void timeutils_seconds_since_epoch_to_struct_time(uint64_t t, timeutils_struct_time_t *tm) { - // TODO this will give incorrect results for dates before 2000/1/1 - timeutils_seconds_since_2000_to_struct_time((mp_uint_t)(t - TIMEUTILS_SECONDS_1970_TO_2000), tm); +static inline void timeutils_seconds_since_epoch_to_struct_time(mp_timestamp_t t, timeutils_struct_time_t *tm) { + timeutils_seconds_since_1970_to_struct_time(t, tm); +} + +// Year is absolute, month/date are 1-based, hour/minute/second are 0-based. +static inline mp_timestamp_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, mp_uint_t date, + mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return timeutils_seconds_since_1970(year, month, date, hour, minute, second); } // Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. -static inline uint64_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { - return timeutils_mktime_2000(year, month, mday, hours, minutes, seconds) + TIMEUTILS_SECONDS_1970_TO_2000; +static inline mp_timestamp_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, + mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + return timeutils_mktime_1970(year, month, mday, hours, minutes, seconds); } -// Year is absolute, month/date are 1-based, hour/minute/second are 0-based. -static inline uint64_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, - mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { - // TODO this will give incorrect results for dates before 2000/1/1 - return timeutils_seconds_since_2000(year, month, date, hour, minute, second) + TIMEUTILS_SECONDS_1970_TO_2000; +static inline mp_timestamp_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(int64_t ns) { + return (mp_timestamp_t)(ns / 1000000000LL); } -static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { - return (mp_uint_t)(ns / 1000000000ULL); +static inline int64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_timestamp_t s) { + return (int64_t)s * 1000000000LL; } -static inline uint64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(uint64_t ns) { +static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(int64_t ns) { return ns; } #else // Epoch is 2000 -#define timeutils_seconds_since_epoch_to_struct_time timeutils_seconds_since_2000_to_struct_time -#define timeutils_seconds_since_epoch timeutils_seconds_since_2000 -#define timeutils_mktime timeutils_mktime_2000 +static inline void timeutils_seconds_since_epoch_to_struct_time(mp_timestamp_t t, timeutils_struct_time_t *tm) { + timeutils_seconds_since_2000_to_struct_time(t, tm); +} -static inline uint64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_uint_t s) { - return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL; +// Year is absolute, month/date are 1-based, hour/minute/second are 0-based. +static inline mp_timestamp_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, mp_uint_t date, + mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return timeutils_seconds_since_2000(year, month, date, hour, minute, second); } -static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { - return ns / 1000000000ULL - TIMEUTILS_SECONDS_1970_TO_2000; +// Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. +static inline mp_timestamp_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, + mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + return timeutils_mktime_2000(year, month, mday, hours, minutes, seconds); +} + +static inline mp_timestamp_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(int64_t ns) { + return (mp_timestamp_t)(ns / 1000000000LL - TIMEUTILS_SECONDS_1970_TO_2000); +} + +static inline int64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_timestamp_t s) { + return ((int64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000LL; } static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(int64_t ns) { - return ns + TIMEUTILS_SECONDS_1970_TO_2000 * 1000000000ULL; + return ns + TIMEUTILS_SECONDS_1970_TO_2000 * 1000000000LL; } #endif -int timeutils_calc_weekday(int y, int m, int d); - #endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H diff --git a/supervisor/port.h b/supervisor/port.h index 192307a5f5714..4a6ff4c8d1d15 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -24,13 +24,13 @@ extern uint32_t _ebss; safe_mode_t port_init(void); // Reset the microcontroller completely. -void reset_cpu(void) NORETURN; +void reset_cpu(void) MP_NORETURN; // Reset the microcontroller state. void reset_port(void); // Reset to the bootloader -void reset_to_bootloader(void) NORETURN; +void reset_to_bootloader(void) MP_NORETURN; // Get stack limit address uint32_t *port_stack_get_limit(void); diff --git a/supervisor/shared/safe_mode.h b/supervisor/shared/safe_mode.h index 8f4037cbe20c3..87f65d867ea62 100644 --- a/supervisor/shared/safe_mode.h +++ b/supervisor/shared/safe_mode.h @@ -38,6 +38,6 @@ void set_safe_mode(safe_mode_t safe_mode); safe_mode_t wait_for_safe_mode_reset(void); void safe_mode_on_next_reset(safe_mode_t reason); -void reset_into_safe_mode(safe_mode_t reason) NORETURN; +void reset_into_safe_mode(safe_mode_t reason) MP_NORETURN; void print_safe_mode_message(safe_mode_t reason); diff --git a/tests/README.md b/tests/README.md index 21e14eee5e128..534e7e0a05972 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,6 +1,7 @@ # MicroPython Test Suite -This directory contains tests for most parts of MicroPython. +This directory contains tests for most parts of MicroPython. To run it you will need +CPython 3.8.2 or newer, which is used to validate MicroPython's behaviour. To run all stable tests, run the "run-tests.py" script in this directory. By default that will run the test suite against the unix port of MicroPython. @@ -67,16 +68,14 @@ for a full list of command line options. ### Benchmarking a target -To run tests on a firmware target using `pyboard.py`, run the command line like +To run tests on a firmware target using a serial port, run the command line like this: ``` -./run-perfbench.py -p -d /dev/ttyACM0 168 100 +./run-perfbench.py -t /dev/ttyACM0 168 100 ``` -* `-p` indicates running on a remote target via pyboard.py, not the host. -* `-d PORTNAME` is the serial port, `/dev/ttyACM0` is the default if not - provided. +* `-t PORTNAME` is the serial port to use (and it supports shorthand like `a0`). * `168` is value `N`, the approximate CPU frequency in MHz (in this case Pyboard V1.1 is 168MHz). It's possible to choose other values as well: lower values like `10` will run much the tests much quicker, higher values like `1000` will @@ -136,11 +135,11 @@ Usually you want to know if something is faster or slower than a reference. To do this, copy the output of each `run-perfbench.py` run to a text file. This can be done multiple ways, but one way on Linux/macOS is with the `tee` -utility: `./run-perfbench.py -p 168 100 | tee pyb-run1.txt` +utility: `./run-perfbench.py -t a0 168 100 | tee pyb-run1.txt` Once you have two files with output from two different runs (maybe with different code or configuration), compare the runtimes with `./run-perfbench.py --t pybv-run1.txt pybv-run2.txt` or compare scores with `./run-perfbench.py -s +-m pybv-run1.txt pybv-run2.txt` or compare scores with `./run-perfbench.py -s pybv-run1.txt pybv-run2.txt`: ``` @@ -204,6 +203,18 @@ internal_bench/bytebuf: 1 tests performed (3 individual testcases) ``` +## Serial reliability and performance test + +Serial port reliability and performance can be tested using the `serial_test.py` script. +Pass the name of the port to test against, for example: + + $ ./serial_test.py -t /dev/ttyACM0 + +If no port is specified then `/dev/ttyACM0` is used as the default. + +The test will send data out to the target, and receive data from the target, in various +chunk sizes. The throughput of the serial connection will be reported for each sub-test. + ## Test key/certificates SSL/TLS tests in `multi_net` and `net_inet` use self-signed key/cert pairs diff --git a/tests/basics/annotate_var.py.exp b/tests/basics/annotate_var.py.exp deleted file mode 100644 index 9b6536e966b0e..0000000000000 --- a/tests/basics/annotate_var.py.exp +++ /dev/null @@ -1,5 +0,0 @@ -False -1 -(1, 2) -NameError -1 diff --git a/tests/basics/array_add.py b/tests/basics/array_add.py index 76ce59f761e06..e78615541c3cb 100644 --- a/tests/basics/array_add.py +++ b/tests/basics/array_add.py @@ -14,3 +14,9 @@ a1.extend(array.array('I', [5])) print(a1) + +a1.extend([6, 7]) +print(a1) + +a1.extend(i for i in (8, 9)) +print(a1) diff --git a/tests/basics/assign_expr.py.exp b/tests/basics/assign_expr.py.exp deleted file mode 100644 index 47da56b80d47b..0000000000000 --- a/tests/basics/assign_expr.py.exp +++ /dev/null @@ -1,16 +0,0 @@ -4 -True -2 -4 5 -5 -1 5 5 -5 -2 1 -1 0 -any True -8 -123 -any True -8 -[(1, 0), (2, 2), (3, 6), (4, 12)] -4 diff --git a/tests/basics/assign_expr_scope.py.exp b/tests/basics/assign_expr_scope.py.exp deleted file mode 100644 index 5c780b382215f..0000000000000 --- a/tests/basics/assign_expr_scope.py.exp +++ /dev/null @@ -1,23 +0,0 @@ -scope0 -1 -None -scope1 -[1] -1 -None -scope2 -[0, 1] -1 -1 -scope3 -[0, 1] -None -None -scope4 -[0, 1] -1 -1 -scope5 -[0, 1] -1 -None diff --git a/tests/basics/async_await.py.exp b/tests/basics/async_await.py.exp deleted file mode 100644 index b51c388a9339e..0000000000000 --- a/tests/basics/async_await.py.exp +++ /dev/null @@ -1,32 +0,0 @@ -4 -3 -2 -1 -0 -0 -1 -0 -0 -2 -1 -0 -0 -1 -0 -0 -3 -2 -1 -0 -0 -1 -0 -0 -2 -1 -0 -0 -1 -0 -0 -finished diff --git a/tests/basics/async_await2.py.exp b/tests/basics/async_await2.py.exp deleted file mode 100644 index fc9ff0aa535fc..0000000000000 --- a/tests/basics/async_await2.py.exp +++ /dev/null @@ -1,5 +0,0 @@ -wait value: 1 -return from send: message from wait(1) -wait got back: message from main -x = 100 -got StopIteration diff --git a/tests/basics/async_def.py.exp b/tests/basics/async_def.py.exp deleted file mode 100644 index f555ace99ab20..0000000000000 --- a/tests/basics/async_def.py.exp +++ /dev/null @@ -1,3 +0,0 @@ -decorator -foo -StopIteration diff --git a/tests/basics/async_for.py.exp b/tests/basics/async_for.py.exp deleted file mode 100644 index 6f59979c065de..0000000000000 --- a/tests/basics/async_for.py.exp +++ /dev/null @@ -1,51 +0,0 @@ -== start == -init -aiter -init -anext -a -anext -b -anext -c -anext -== finish == -== start == -init -aiter -init -anext -d -anext -e -anext -f -anext -AsyncIteratorWrapper-def -== finish == -init -== start == -aiter -init -anext -g -anext -h -anext -i -anext -AsyncIteratorWrapper-ghi -== finish == -init -== start == -aiter -init -anext -j -anext -k -anext -l -anext -AsyncIteratorWrapper-jkl -== finish == diff --git a/tests/basics/async_for2.py b/tests/basics/async_for2.py index bbdb02c49b209..82232d52fc2a1 100644 --- a/tests/basics/async_for2.py +++ b/tests/basics/async_for2.py @@ -1,7 +1,7 @@ # test waiting within "async for" __anext__ function # CIRCUITPY-CHANGE -# uPy allows normal generators to be awaitables. +# MicroPython allows normal generators to be awaitables. # CircuitPython does not. # In CircuitPython you need to have an __await__ method on an awaitable like in CPython; # and like in CPython, generators do not have __await__. diff --git a/tests/basics/async_for2.py.exp b/tests/basics/async_for2.py.exp deleted file mode 100644 index 52bbe90c85376..0000000000000 --- a/tests/basics/async_for2.py.exp +++ /dev/null @@ -1,32 +0,0 @@ -init -aiter -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -x 0 -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -x 1 -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -x 2 -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -x 3 -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -finished diff --git a/tests/basics/async_syntaxerror.py.exp b/tests/basics/async_syntaxerror.py.exp deleted file mode 100644 index 5275689b41383..0000000000000 --- a/tests/basics/async_syntaxerror.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -SyntaxError -SyntaxError diff --git a/tests/basics/async_with.py.exp b/tests/basics/async_with.py.exp deleted file mode 100644 index 6bbf84cb4b52c..0000000000000 --- a/tests/basics/async_with.py.exp +++ /dev/null @@ -1,11 +0,0 @@ -enter -body -exit None None -finished -enter -1 -exit error -ValueError -enter -exit -BaseException diff --git a/tests/basics/async_with2.py b/tests/basics/async_with2.py index 5bac38236fe4a..b18fa6bdee106 100644 --- a/tests/basics/async_with2.py +++ b/tests/basics/async_with2.py @@ -1,7 +1,7 @@ # test waiting within async with enter/exit functions # CIRCUITPY-CHANGE -# uPy allows normal generators to be awaitables. +# MicroPython allows normal generators to be awaitables. # CircuitPython does not. # In CircuitPython you need to have an __await__ method on an awaitable like in CPython; # and like in CPython, generators do not have __await__. diff --git a/tests/basics/async_with2.py.exp b/tests/basics/async_with2.py.exp deleted file mode 100644 index 76b173b4c24a7..0000000000000 --- a/tests/basics/async_with2.py.exp +++ /dev/null @@ -1,17 +0,0 @@ -enter -f start: 10 -coro yielded: 11 -coro yielded: 12 -f returned: 13 -body start -f start: 30 -coro yielded: 31 -coro yielded: 32 -body f returned: 33 -body end -exit None None -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -finished diff --git a/tests/basics/async_with_break.py.exp b/tests/basics/async_with_break.py.exp deleted file mode 100644 index d077a88fad0e4..0000000000000 --- a/tests/basics/async_with_break.py.exp +++ /dev/null @@ -1,15 +0,0 @@ -enter -body -exit None None -finished -enter -body -exit None None -finally -finished -enter -body -exit None None -finally inner -finally outer -finished diff --git a/tests/basics/async_with_return.py.exp b/tests/basics/async_with_return.py.exp deleted file mode 100644 index d077a88fad0e4..0000000000000 --- a/tests/basics/async_with_return.py.exp +++ /dev/null @@ -1,15 +0,0 @@ -enter -body -exit None None -finished -enter -body -exit None None -finally -finished -enter -body -exit None None -finally inner -finally outer -finished diff --git a/tests/basics/attrtuple2.py b/tests/basics/attrtuple2.py new file mode 100644 index 0000000000000..081d24b6ae92c --- /dev/null +++ b/tests/basics/attrtuple2.py @@ -0,0 +1,25 @@ +# test os.uname() attrtuple, if available +try: + import os +except ImportError: + print("SKIP") + raise SystemExit + +try: + u = os.uname() +except AttributeError: + print("SKIP") + raise SystemExit + +# test printing of attrtuple +print(str(u).find("machine=") > 0) + +# test read attr +print(isinstance(u.machine, str)) + +# test str modulo operator for attrtuple +impl_str = ("%s " * len(u)) % u +test_str = "" +for val in u: + test_str += val + " " +print(impl_str == test_str) diff --git a/tests/basics/boundmeth1.py b/tests/basics/boundmeth1.py index fe1b454688d64..9f08d9bbb259b 100644 --- a/tests/basics/boundmeth1.py +++ b/tests/basics/boundmeth1.py @@ -1,6 +1,6 @@ # tests basics of bound methods -# uPy and CPython differ when printing a bound method, so just print the type +# MicroPython and CPython differ when printing a bound method, so just print the type print(type(repr([].append))) 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 diff --git a/tests/basics/builtin_pow3_intbig.py b/tests/basics/builtin_pow3_intbig.py index bedc8b36b7edd..41d2acbc0cc75 100644 --- a/tests/basics/builtin_pow3_intbig.py +++ b/tests/basics/builtin_pow3_intbig.py @@ -20,3 +20,8 @@ print(hex(pow(y, x-1, x))) # Should be 1, since x is prime print(hex(pow(y, y-1, x))) # Should be a 'big value' print(hex(pow(y, y-1, y))) # Should be a 'big value' + +try: + print(pow(1, 2, 0)) +except ValueError: + print("ValueError") diff --git a/tests/basics/builtin_range.py b/tests/basics/builtin_range.py index 66226bad16a6d..9608a8163107e 100644 --- a/tests/basics/builtin_range.py +++ b/tests/basics/builtin_range.py @@ -32,13 +32,29 @@ print(range(1, 4)[0:]) print(range(1, 4)[1:]) print(range(1, 4)[:-1]) +print(range(4, 1)[:]) +print(range(4, 1)[0:]) +print(range(4, 1)[1:]) +print(range(4, 1)[:-1]) print(range(7, -2, -4)[:]) print(range(1, 100, 5)[5:15:3]) print(range(1, 100, 5)[15:5:-3]) print(range(100, 1, -5)[5:15:3]) print(range(100, 1, -5)[15:5:-3]) +print(range(1, 100, 5)[5:15:-3]) +print(range(1, 100, 5)[15:5:3]) +print(range(100, 1, -5)[5:15:-3]) +print(range(100, 1, -5)[15:5:3]) +print(range(1, 100, 5)[5:15:3]) +print(range(1, 100, 5)[15:5:-3]) +print(range(1, 100, -5)[5:15:3]) +print(range(1, 100, -5)[15:5:-3]) +print(range(1, 100, 5)[5:15:-3]) +print(range(1, 100, 5)[15:5:3]) +print(range(1, 100, -5)[5:15:-3]) +print(range(1, 100, -5)[15:5:3]) -# for this case uPy gives a different stop value but the listed elements are still correct +# for this case MicroPython gives a different stop value but the listed elements are still correct print(list(range(7, -2, -4)[2:-2:])) # zero step diff --git a/tests/basics/builtin_range_maxsize.py b/tests/basics/builtin_range_maxsize.py new file mode 100644 index 0000000000000..b0f3a5e5129f1 --- /dev/null +++ b/tests/basics/builtin_range_maxsize.py @@ -0,0 +1,38 @@ +try: + from sys import maxsize +except ImportError: + print("SKIP") + raise SystemExit + +# Test the range builtin at extreme values. (https://github.com/micropython/micropython/issues/17684) +# +# This is written using asserts instead of prints because the value of `maxsize` differs. +# +# Numbers & counts right up against the max of mp_int_t also cause overflows, such as +# objrange.c:115:14: runtime error: signed integer overflow: 9223372036854775807 + 1 cannot be represented in type 'long int' +# so just avoid them for the purposes of this test. + +r = range(-maxsize + 1, -1) +assert r.start == -maxsize + 1 +assert r.stop == -1 +assert r[0] == -maxsize + 1 +assert r[1] == -maxsize + 2 +assert r[-1] == -2 +assert r[-2] == -3 + +ir = iter(r) +assert next(ir) == -maxsize + 1 +assert next(ir) == -maxsize + 2 + +r = range(0, maxsize - 1) +assert len(r) == maxsize - 1 +assert r.stop == maxsize - 1 + +r = range(maxsize, 0, -1) +assert len(r) == maxsize +assert r.start == maxsize +assert r[0] == maxsize +assert r[1] == maxsize - 1 +ir = iter(r) +assert next(ir) == maxsize +assert next(ir) == maxsize - 1 diff --git a/tests/basics/builtin_setattr.py b/tests/basics/builtin_setattr.py index e4acb14cac354..2c9a452c32774 100644 --- a/tests/basics/builtin_setattr.py +++ b/tests/basics/builtin_setattr.py @@ -21,5 +21,5 @@ def __init__(self): try: setattr(int, 'to_bytes', 1) except (AttributeError, TypeError): - # uPy raises AttributeError, CPython raises TypeError + # MicroPython raises AttributeError, CPython raises TypeError print('AttributeError/TypeError') diff --git a/tests/basics/builtin_slice.py b/tests/basics/builtin_slice.py index 3ff3c3dd04d43..7f548ee335839 100644 --- a/tests/basics/builtin_slice.py +++ b/tests/basics/builtin_slice.py @@ -1,11 +1,44 @@ # test builtin slice +# ensures that slices passed to user types are heap-allocated and can be +# safely stored as well as not overriden by subsequent slices. + # print slice class A: def __getitem__(self, idx): - print(idx) + print("get", idx) + print("abc"[1:]) + print("get", idx) + return idx + + def __setitem__(self, idx, value): + print("set", idx) + print("abc"[1:]) + print("set", idx) + self.saved_idx = idx + return idx + + def __delitem__(self, idx): + print("del", idx) + print("abc"[1:]) + print("del", idx) return idx -s = A()[1:2:3] + + +a = A() +s = a[1:2:3] +a[4:5:6] = s +del a[7:8:9] + +print(a.saved_idx) + +# nested slicing +print(A()[1 : A()[A()[2:3:4] : 5]]) + +# tuple slicing +a[1:2, 4:5, 7:8] +a[1, 4:5, 7:8, 2] +a[1:2, a[3:4], 5:6] # check type print(type(s) is slice) diff --git a/tests/basics/bytes_format_modulo.py.exp b/tests/basics/bytes_format_modulo.py.exp deleted file mode 100644 index 782b7f91fcbfe..0000000000000 --- a/tests/basics/bytes_format_modulo.py.exp +++ /dev/null @@ -1,5 +0,0 @@ -b'%' -b'=1=' -b'=1=2=' -b'=str=' -b"=b'str'=" diff --git a/tests/basics/class_bind_self.py b/tests/basics/class_bind_self.py index 813f876446ef2..8bece902ca254 100644 --- a/tests/basics/class_bind_self.py +++ b/tests/basics/class_bind_self.py @@ -52,7 +52,7 @@ def f4(self, arg): # generator print(C.f8(13)) print(C.f9(14)) -# not working in uPy +# not working in MicroPython #class C(list): # # this acts like a method and binds self # f1 = list.extend diff --git a/tests/basics/class_descriptor.py b/tests/basics/class_descriptor.py index 83d31674301d5..feaed2fbb2a43 100644 --- a/tests/basics/class_descriptor.py +++ b/tests/basics/class_descriptor.py @@ -1,22 +1,28 @@ class Descriptor: def __get__(self, obj, cls): - print('get') + print("get") print(type(obj) is Main) print(cls is Main) - return 'result' + return "result" def __set__(self, obj, val): - print('set') + print("set") print(type(obj) is Main) print(val) def __delete__(self, obj): - print('delete') + print("delete") print(type(obj) is Main) + def __set_name__(self, owner, name): + print("set_name", name) + print(owner.__name__ == "Main") + + class Main: Forward = Descriptor() + m = Main() try: m.__class__ @@ -26,15 +32,15 @@ class Main: raise SystemExit r = m.Forward -if 'Descriptor' in repr(r.__class__): +if "Descriptor" in repr(r.__class__): # Target doesn't support descriptors. - print('SKIP') + print("SKIP") raise SystemExit # Test assignment and deletion. print(r) -m.Forward = 'a' +m.Forward = "a" del m.Forward # Test that lookup of descriptors like __get__ are not passed into __getattr__. diff --git a/tests/basics/class_inplace_op2.py.exp b/tests/basics/class_inplace_op2.py.exp deleted file mode 100644 index 8c323b5178ef4..0000000000000 --- a/tests/basics/class_inplace_op2.py.exp +++ /dev/null @@ -1,12 +0,0 @@ -__imul__ -__imatmul__ -__ifloordiv__ -__itruediv__ -__imod__ -__ipow__ -__ior__ -__ixor__ -__iand__ -__ilshift__ -__irshift__ -TypeError diff --git a/tests/basics/class_ordereddict.py.exp b/tests/basics/class_ordereddict.py.exp deleted file mode 100644 index b723e327515c7..0000000000000 --- a/tests/basics/class_ordereddict.py.exp +++ /dev/null @@ -1 +0,0 @@ -['a', 'b', 'c', 'd', 'e'] diff --git a/tests/basics/class_setname_hazard.py b/tests/basics/class_setname_hazard.py new file mode 100644 index 0000000000000..77c0409346282 --- /dev/null +++ b/tests/basics/class_setname_hazard.py @@ -0,0 +1,182 @@ +# Test that __set_name__ can access and mutate its owner argument. + + +def skip_if_no_descriptors(): + class Descriptor: + def __get__(self, obj, cls): + return + + class TestClass: + Forward = Descriptor() + + a = TestClass() + try: + a.__class__ + except AttributeError: + # Target doesn't support __class__. + print("SKIP") + raise SystemExit + + b = a.Forward + if "Descriptor" in repr(b.__class__): + # Target doesn't support descriptors. + print("SKIP") + raise SystemExit + + +skip_if_no_descriptors() + + +# Test basic accesses and mutations. + + +class GetSibling: + def __set_name__(self, owner, name): + print(getattr(owner, name + "_sib")) + + +class GetSiblingTest: + desc = GetSibling() + desc_sib = 111 + + +t110 = GetSiblingTest() + + +class SetSibling: + def __set_name__(self, owner, name): + setattr(owner, name + "_sib", 121) + + +class SetSiblingTest: + desc = SetSibling() + + +t120 = SetSiblingTest() + +print(t120.desc_sib) + + +class DelSibling: + def __set_name__(self, owner, name): + delattr(owner, name + "_sib") + + +class DelSiblingTest: + desc = DelSibling() + desc_sib = 131 + + +t130 = DelSiblingTest() + +try: + print(t130.desc_sib) +except AttributeError: + print("AttributeError") + + +class GetSelf: + x = 211 + + def __set_name__(self, owner, name): + print(getattr(owner, name).x) + + +class GetSelfTest: + desc = GetSelf() + + +t210 = GetSelfTest() + + +class SetSelf: + def __set_name__(self, owner, name): + setattr(owner, name, 221) + + +class SetSelfTest: + desc = SetSelf() + + +t220 = SetSelfTest() + +print(t220.desc) + + +class DelSelf: + def __set_name__(self, owner, name): + delattr(owner, name) + + +class DelSelfTest: + desc = DelSelf() + + +t230 = DelSelfTest() + +try: + print(t230.desc) +except AttributeError: + print("AttributeError") + + +# Test exception behavior. + + +class Raise: + def __set_name__(self, owner, name): + raise Exception() + + +try: + + class RaiseTest: + desc = Raise() +except Exception as e: # CPython raises RuntimeError, MicroPython propagates the original exception + print("Exception") + + +# Ensure removed/overwritten class members still get __set_name__ called. + + +class SetSpecific: + def __init__(self, sib_name, sib_replace): + self.sib_name = sib_name + self.sib_replace = sib_replace + + def __set_name__(self, owner, name): + setattr(owner, self.sib_name, self.sib_replace) + + +class SetReplaceTest: + a = SetSpecific("b", 312) # one of these is changed first + b = SetSpecific("a", 311) + + +t310 = SetReplaceTest() +print(t310.a) +print(t310.b) + + +class DelSpecific: + def __init__(self, sib_name): + self.sib_name = sib_name + + def __set_name__(self, owner, name): + delattr(owner, self.sib_name) + + +class DelReplaceTest: + a = DelSpecific("b") # one of these is removed first + b = DelSpecific("a") + + +t320 = DelReplaceTest() +try: + print(t320.a) +except AttributeError: + print("AttributeError") +try: + print(t320.b) +except AttributeError: + print("AttributeError") diff --git a/tests/basics/class_setname_hazard_rand.py b/tests/basics/class_setname_hazard_rand.py new file mode 100644 index 0000000000000..4c9934c3bf068 --- /dev/null +++ b/tests/basics/class_setname_hazard_rand.py @@ -0,0 +1,111 @@ +# Test to make sure there's no sequence hazard even when a __set_name__ implementation +# mutates and reorders the namespace of its owner class. +# VERY hard bug to prove out except via a stochastic test. + + +try: + from random import choice + import re +except ImportError: + print("SKIP") + raise SystemExit + + +def skip_if_no_descriptors(): + class Descriptor: + def __get__(self, obj, cls): + return + + class TestClass: + Forward = Descriptor() + + a = TestClass() + try: + a.__class__ + except AttributeError: + # Target doesn't support __class__. + print("SKIP") + raise SystemExit + + b = a.Forward + if "Descriptor" in repr(b.__class__): + # Target doesn't support descriptors. + print("SKIP") + raise SystemExit + + +skip_if_no_descriptors() + +letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +# Would be r"[A-Z]{5}", but not all ports support the {n} quantifier. +junk_re = re.compile(r"[A-Z][A-Z][A-Z][A-Z][A-Z]") + + +def junk_fill(obj, n=10): # Add randomly-generated attributes to an object. + for i in range(n): + name = "".join(choice(letters) for j in range(5)) + setattr(obj, name, object()) + + +def junk_clear(obj): # Remove attributes added by junk_fill. + to_del = [name for name in dir(obj) if junk_re.match(name)] + for name in to_del: + delattr(obj, name) + + +def junk_sequencer(): + global runs + try: + while True: + owner, name = yield + runs[name] = runs.get(name, 0) + 1 + junk_fill(owner) + finally: + junk_clear(owner) + + +class JunkMaker: + def __set_name__(self, owner, name): + global seq + seq.send((owner, name)) + + +runs = {} +seq = junk_sequencer() +next(seq) + + +class Main: + a = JunkMaker() + b = JunkMaker() + c = JunkMaker() + d = JunkMaker() + e = JunkMaker() + f = JunkMaker() + g = JunkMaker() + h = JunkMaker() + i = JunkMaker() + j = JunkMaker() + k = JunkMaker() + l = JunkMaker() + m = JunkMaker() + n = JunkMaker() + o = JunkMaker() + p = JunkMaker() + q = JunkMaker() + r = JunkMaker() + s = JunkMaker() + t = JunkMaker() + u = JunkMaker() + v = JunkMaker() + w = JunkMaker() + x = JunkMaker() + y = JunkMaker() + z = JunkMaker() + + +seq.close() + +for k in letters.lower(): + print(k, runs.get(k, 0)) diff --git a/tests/basics/del_attr.py b/tests/basics/del_attr.py index 8487b97e311d0..90a8a5a6f833d 100644 --- a/tests/basics/del_attr.py +++ b/tests/basics/del_attr.py @@ -35,5 +35,5 @@ def f(): try: del int.to_bytes except (AttributeError, TypeError): - # uPy raises AttributeError, CPython raises TypeError + # MicroPython raises AttributeError, CPython raises TypeError print('AttributeError/TypeError') diff --git a/tests/basics/del_global.py b/tests/basics/del_global.py index d740357b0332d..e1aa074b97a43 100644 --- a/tests/basics/del_global.py +++ b/tests/basics/del_global.py @@ -14,7 +14,7 @@ def do_del(): try: do_del() except: # NameError: - # FIXME uPy returns KeyError for this + # FIXME MicroPython returns KeyError for this print("NameError") # delete globals using a list diff --git a/tests/basics/del_name.py b/tests/basics/del_name.py index c92be54d3b655..8393b2df92a72 100644 --- a/tests/basics/del_name.py +++ b/tests/basics/del_name.py @@ -10,7 +10,7 @@ try: del x except: # NameError: - # FIXME uPy returns KeyError for this + # FIXME MicroPython returns KeyError for this print("NameError") class C: diff --git a/tests/basics/dict_views.py b/tests/basics/dict_views.py index a82f47b6be26f..45f3238f1c624 100644 --- a/tests/basics/dict_views.py +++ b/tests/basics/dict_views.py @@ -6,6 +6,11 @@ # print a view with more than one item print({1:1, 2:1}.values()) +# `bool` and `len` unary ops +for d in ({}, {1: 2}, {1: 2, 3: 4}): + for op in (bool, len): + print(op(d.keys()), op(d.values()), op(d.items())) + # unsupported binary op on a dict values view try: {1:1}.values() + 1 diff --git a/tests/basics/exception_chain.py b/tests/basics/exception_chain.py index 579756c85fa20..96c06c847ab37 100644 --- a/tests/basics/exception_chain.py +++ b/tests/basics/exception_chain.py @@ -1,5 +1,12 @@ # CIRCUITPY-CHANGE: exception chaining is supported +import sys + +# The unix minimal build doesn't enable MICROPY_WARNINGS (required for this test). +if getattr(sys.implementation, "_build", None) == "minimal": + print("SKIP") + raise SystemExit + try: Exception().__cause__ except AttributeError: diff --git a/tests/basics/frozenset_set.py b/tests/basics/frozenset_set.py index 3bf456acfd70f..2b3c683260989 100644 --- a/tests/basics/frozenset_set.py +++ b/tests/basics/frozenset_set.py @@ -8,5 +8,5 @@ # "Instances of set are compared to instances of frozenset based on their # members. For example:" print(set('abc') == frozenset('abc')) -# This doesn't work in uPy +# This doesn't work in MicroPython #print(set('abc') in set([frozenset('abc')])) diff --git a/tests/basics/fun_code_colines.py b/tests/basics/fun_code_colines.py new file mode 100644 index 0000000000000..a8867770eddf1 --- /dev/null +++ b/tests/basics/fun_code_colines.py @@ -0,0 +1,81 @@ +# Check that we have sensical bytecode offsets in function.__code__.co_lines + +def f1(x, y, obj, obj2, obj3): + a = x + y # line 4: bc+4 line+4 + b = x - y # line 5: bc+4 line+1 + # line 6 + # line 7 + # line 8 + # line 9 + # line 10 + # line 11 + # line 12 + # line 13 + # line 14 + # line 15 + # line 16 + # line 17 + # line 18 + # line 19 + c = a * b # line 20: bc+4 line+15 + obj.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.fun() # line 21: bc+31 line+1; bc+27 line+0 + # line 22 + # line 23 + # line 24: bc+0 line+3 + # line 25 + # line 26 + # line 27: bc+0 line+3 + # line 28 + # line 29 + obj2.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.fun() # line 30: bc+31 line+3; bc+27 line+0 + # line 31 + # line 32 + # line 33: bc+0 line+3 + # line 34 + # line 35 + # line 36 + # line 37 + # line 38 + # line 39 + # line 40 + # line 41 + # line 42 + # line 43 + # line 44 + # line 45 + # line 46 + # line 47 + # line 48 + # line 49 + # line 50 + # line 51 + # line 52 + # line 53 + # line 54 + # line 55 + # line 56 + # line 57 + # line 58 + # line 59 + return obj3.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.fun() # line 60: bc+31 line+27; bc+27 line+0 + +def f2(x, y): + a = x + y # line 63 + b = x - y # line 64 + return a * b # line 65 + +try: + f1.__code__.co_lines +except AttributeError: + print("SKIP") + raise SystemExit + +print("f1") +for start, end, line_no in f1.__code__.co_lines(): + print("line {} start: {}".format(line_no, start)) + print("line {} end: {}".format(line_no, end)) + +print("f2") +for start, end, line_no in f2.__code__.co_lines(): + print("line {} start: {}".format(line_no, start)) + print("line {} end: {}".format(line_no, end)) diff --git a/tests/basics/fun_code_colines.py.exp b/tests/basics/fun_code_colines.py.exp new file mode 100644 index 0000000000000..19bd4ef6e2a5a --- /dev/null +++ b/tests/basics/fun_code_colines.py.exp @@ -0,0 +1,20 @@ +f1 +line 4 start: 0 +line 4 end: 4 +line 5 start: 4 +line 5 end: 8 +line 20 start: 8 +line 20 end: 12 +line 21 start: 12 +line 21 end: 70 +line 30 start: 70 +line 30 end: 128 +line 60 start: 128 +line 60 end: 186 +f2 +line 63 start: 0 +line 63 end: 4 +line 64 start: 4 +line 64 end: 8 +line 65 start: 8 +line 65 end: 12 diff --git a/tests/basics/fun_code_full.py b/tests/basics/fun_code_full.py new file mode 100644 index 0000000000000..e1c867939a2a7 --- /dev/null +++ b/tests/basics/fun_code_full.py @@ -0,0 +1,40 @@ +# Test function.__code__ attributes not available with MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC + +try: + (lambda: 0).__code__.co_code +except AttributeError: + print("SKIP") + raise SystemExit + +def f(x, y): + a = x + y + b = x - y + return a * b + +code = f.__code__ + +print(type(code.co_code)) # both bytes (but mpy and cpy have different instruction sets) +print(code.co_consts) # (not necessarily the same set, but in this function they are) +print(code.co_filename.rsplit('/')[-1]) # same terminal filename but might be different paths on other ports +print(type(code.co_firstlineno)) # both ints (but mpy points to first line inside, cpy points to declaration) +print(code.co_name) +print(iter(code.co_names) is not None) # both iterable (but mpy returns dict with names as keys, cpy only the names; and not necessarily the same set) + +co_lines = code.co_lines() + +l = list(co_lines) +first_start = l[0][0] +last_end = l[-1][1] +print(first_start) # co_lines should start at the start of the bytecode +print(len(code.co_code) - last_end) # and end at the end of the bytecode + +prev_end = 0 +for start, end, line_no in l: + if end != prev_end: + print("non-contiguous") + break # the offset ranges should be contiguous + prev_end = end +else: + print("contiguous") + + diff --git a/tests/basics/fun_code_full.py.exp b/tests/basics/fun_code_full.py.exp new file mode 100644 index 0000000000000..830effadfc695 --- /dev/null +++ b/tests/basics/fun_code_full.py.exp @@ -0,0 +1,9 @@ + +(None,) +fun_code_full.py + +f +True +0 +0 +non-contiguous diff --git a/tests/basics/fun_code_lnotab.py b/tests/basics/fun_code_lnotab.py new file mode 100644 index 0000000000000..9223e5730f0ea --- /dev/null +++ b/tests/basics/fun_code_lnotab.py @@ -0,0 +1,34 @@ +# Test deprecation of co_lnotab + +try: + (lambda: 0).__code__.co_code +except AttributeError: + print("SKIP") + raise SystemExit + + +import unittest +import sys + + +mpy_is_v2 = getattr(sys.implementation, '_v2', False) + + +def f(): + pass + + +class Test(unittest.TestCase): + + @unittest.skipIf(mpy_is_v2, "Removed in MicroPython v2 and later.") + def test_co_lnotab_exists(self): + self.assertIsInstance(f.__code__.co_lnotab, bytes) + + @unittest.skipUnless(mpy_is_v2, "Not removed before MicroPython v2.") + def test_co_lnotab_removed(self): + with self.assertRaises(AttributeError): + f.__code__.co_lnotab + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/basics/fun_name.py b/tests/basics/fun_name.py index b2642280a2a65..8aac35eeafa25 100644 --- a/tests/basics/fun_name.py +++ b/tests/basics/fun_name.py @@ -16,7 +16,7 @@ def Fun(self): print('SKIP') raise SystemExit -# __name__ of a bound native method is not implemented in uPy +# __name__ of a bound native method is not implemented in MicroPython # the test here is to make sure it doesn't crash try: str((1).to_bytes.__name__) diff --git a/tests/basics/gc1.py b/tests/basics/gc1.py index 332bf9744c072..dff9538b14b5c 100644 --- a/tests/basics/gc1.py +++ b/tests/basics/gc1.py @@ -15,13 +15,13 @@ gc.collect() if hasattr(gc, 'mem_free'): - # uPy has these extra functions + # MicroPython has these extra functions # just test they execute and return an int assert type(gc.mem_free()) is int assert type(gc.mem_alloc()) is int if hasattr(gc, 'threshold'): - # uPy has this extra function + # MicroPython has this extra function # check execution and returns assert(gc.threshold(1) is None) assert(gc.threshold() == 0) diff --git a/tests/basics/import_instance_method.py b/tests/basics/import_instance_method.py new file mode 100644 index 0000000000000..d25b70ac5f0bf --- /dev/null +++ b/tests/basics/import_instance_method.py @@ -0,0 +1,38 @@ +# Test importing a method from a class instance. +# This is not a common thing to do, but ensures MicroPython has the same semantics as CPython. + +import sys + +if not hasattr(sys, "modules"): + print("SKIP") + raise SystemExit + + +class A: + def __init__(self, value): + self.value = value + + def meth(self): + return self.value + + def meth_with_arg(self, a): + return [self.value, a] + + +# Register a class instance as the module "mod". +sys.modules["mod"] = A(1) + +# Try importing it as a module. +import mod + +print(mod.meth()) +print(mod.meth_with_arg(2)) + +# Change the module. +sys.modules["mod"] = A(3) + +# Try importing it using "from ... import". +from mod import meth, meth_with_arg + +print(meth()) +print(meth_with_arg(4)) diff --git a/tests/basics/int_64_basics.py b/tests/basics/int_64_basics.py new file mode 100644 index 0000000000000..ef76793317ec8 --- /dev/null +++ b/tests/basics/int_64_basics.py @@ -0,0 +1,161 @@ +# test support for 64-bit long integers +# (some ports don't support arbitrary precision but do support these) + +# this test is adapted from int_big1.py with numbers kept within 64-bit signed range + +# to test arbitrary precision integers + +x = 1000000000000000000 +xn = -1000000000000000000 +y = 2000000000000000000 + +# printing +print(x) +print(y) +print('%#X' % (x - x)) # print prefix +print('{:#,}'.format(x)) # print with commas + +# construction +print(int(x)) + +# addition +print(x + 1) +print(x + y) +print(x + xn == 0) +print(bool(x + xn)) + +# subtraction +print(x - 1) +print(x - y) +print(y - x) +print(x - x == 0) +print(bool(x - x)) + +# multiplication +print(x * 2) +print(1090511627776 * 1048500) + +# integer division +print(x // 2) +print(y // x) + +# bit inversion +print(~x) +print(~(-x)) + +# left shift +print("left shift positive") +x = 0x40000000 +for i in range(32): + x = x << 1 + print(x) + +# right shift +print("right shift positive") +x = 0x2000000000000000 # TODO: why can't second-tip bit be set? +for i in range(64): + x = x >> 1 + print(x) + +# left shift of a negative number +print("left shift negative") +for i in range(8): + print(-10000000000000000 << i) + print(-10000000000000001 << i) + print(-10000000000000002 << i) + print(-10000000000000003 << i) + print(-10000000000000004 << i) + + +# right shift of a negative number +print("right shift negative") +for i in range(8): + print(-1000000000000000000 >> i) + print(-1000000000000000001 >> i) + print(-1000000000000000002 >> i) + print(-1000000000000000003 >> i) + print(-1000000000000000004 >> i) + +# conversion from string +print(int("1234567890123456789")) +print(int("-1234567890123456789")) +print(int("1234567890abcdef", 16)) +print(int("1234567890ABCDEF", 16)) +print(int("-1234567890ABCDEF", 16)) +print(int("ijklmnopqrsz", 36)) + +# numbers close to 64-bit limits +print(int("-9111222333444555666")) +print(int("9111222333444555666")) + +# numbers with preceding 0s +print(int("-00000000000000000000009111222333444555666")) +print(int("0000000000000000000000009111222333444555666")) + +# invalid characters in string +try: + print(int("1234567890abcdef")) +except ValueError: + print('ValueError'); +try: + print(int("123456789\x01")) +except ValueError: + print('ValueError'); + +# test parsing ints just on threshold of small to big +# for 32 bit archs +x = 1073741823 # small +x = -1073741823 # small +x = 1073741824 # big +x = -1073741824 # big +# for 64 bit archs +x = 4611686018427387903 # small +x = -4611686018427387903 # small +x = 4611686018427387904 # big +x = -4611686018427387904 # big + +# sys.maxsize is a constant bigint, so test it's compatible with dynamic ones +import sys +if hasattr(sys, "maxsize"): + print(sys.maxsize - 1 + 1 == sys.maxsize) +else: + print(True) # No maxsize property in this config + +# test extraction of big int value via mp_obj_get_int_maybe +x = 1 << 62 +print('a' * (x + 4 - x)) + +# test overflow check in mp_obj_get_int_maybe +x = 1 << 32 +r = None +try: + r = range(0, x) +except OverflowError: + # 32-bit target, correctly handled the overflow of x + print("ok") +if r is not None: + if len(r) == x: + # 64-bit target, everything is just a small-int + print("ok") + else: + # 32-bit target that did not handle the overflow of x + print("unhandled overflow") + +# negative shifts are invalid +try: + print((1 << 48) >> -4) +except ValueError as e: + print(e) + +try: + print((1 << 48) << -6) +except ValueError as e: + print(e) + +# Test that the most extreme 64 bit integer values all parse with int() +print(int("-9223372036854775807")) +print(int("-9223372036854775808")) +print(int("9223372036854775807")) + +# Test that the most negative 64 bit integer can be formed via arithmetic +print(-9223372036854775807-1) diff --git a/tests/basics/int_big_to_small.py b/tests/basics/int_big_to_small.py index 64280d0c635f5..c92d263fc2837 100644 --- a/tests/basics/int_big_to_small.py +++ b/tests/basics/int_big_to_small.py @@ -5,6 +5,17 @@ print("SKIP") raise SystemExit +# Skip this test on "REPR B" where (1<<29 + 1) is not a small int. +k = 1 << 29 +micropython.heap_lock() +try: + k = k + 1 +except MemoryError: + print("SKIP") + raise SystemExit +finally: + micropython.heap_unlock() + # All less than small int max. for d in (0, 27, 1<<29, -1861, -(1<<29)): i = 1<<70 diff --git a/tests/basics/int_big_to_small_int29.py b/tests/basics/int_big_to_small_int29.py new file mode 100644 index 0000000000000..438a74d2bc6a4 --- /dev/null +++ b/tests/basics/int_big_to_small_int29.py @@ -0,0 +1,22 @@ +try: + import micropython + micropython.heap_lock +except: + print("SKIP") + raise SystemExit + +# All less than small int max. +for d in (0, 27, 1<<28, -1861, -(1<<28)): + i = 1<<70 + print(i) + j = (1<<70) + d + print(j) + # k should now be a small int. + k = j - i + print(k) + + # Now verify that working with k doesn't allocate (i.e. it's a small int). + micropython.heap_lock() + print(k + 20) + print(k // 20) + micropython.heap_unlock() diff --git a/tests/basics/int_big_to_small_int29.py.exp b/tests/basics/int_big_to_small_int29.py.exp new file mode 100644 index 0000000000000..0920101924aa8 --- /dev/null +++ b/tests/basics/int_big_to_small_int29.py.exp @@ -0,0 +1,25 @@ +1180591620717411303424 +1180591620717411303424 +0 +20 +0 +1180591620717411303424 +1180591620717411303451 +27 +47 +1 +1180591620717411303424 +1180591620717679738880 +268435456 +268435476 +13421772 +1180591620717411303424 +1180591620717411301563 +-1861 +-1841 +-94 +1180591620717411303424 +1180591620717142867968 +-268435456 +-268435436 +-13421773 diff --git a/tests/basics/io_buffered_writer.py b/tests/basics/io_buffered_writer.py index 11df0324e2467..31d3eb489c452 100644 --- a/tests/basics/io_buffered_writer.py +++ b/tests/basics/io_buffered_writer.py @@ -1,10 +1,11 @@ -import io +try: + import io try: io.BytesIO io.BufferedWriter -except AttributeError: - print("SKIP") +except (AttributeError, ImportError): + print('SKIP') raise SystemExit bts = io.BytesIO() @@ -28,3 +29,27 @@ # hashing a BufferedWriter print(type(hash(buf))) + +# Test failing flush() +class MyIO(io.IOBase): + def __init__(self): + self.count = 0 + + def write(self, buf): + self.count += 1 + if self.count < 3: + return None + print("writing", buf) + return len(buf) + + +buf = io.BufferedWriter(MyIO(), 8) + +buf.write(b"foobar") + +for _ in range(4): + try: + buf.flush() + print("flushed") + except OSError: + print("OSError") diff --git a/tests/basics/io_buffered_writer.py.exp b/tests/basics/io_buffered_writer.py.exp index 2209348f5af45..d61eb148b453c 100644 --- a/tests/basics/io_buffered_writer.py.exp +++ b/tests/basics/io_buffered_writer.py.exp @@ -4,3 +4,8 @@ b'foobarfoobar' b'foobarfoobar' b'foo' +OSError +OSError +writing bytearray(b'foobar') +flushed +flushed diff --git a/tests/basics/io_bytesio_cow.py b/tests/basics/io_bytesio_cow.py index 2edb7136a9691..543c12ad42ab2 100644 --- a/tests/basics/io_bytesio_cow.py +++ b/tests/basics/io_bytesio_cow.py @@ -1,6 +1,12 @@ # Make sure that write operations on io.BytesIO don't # change original object it was constructed from. -import io + +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + b = b"foobar" a = io.BytesIO(b) diff --git a/tests/basics/io_bytesio_ext.py b/tests/basics/io_bytesio_ext.py index 4d4c60c1363b7..92e715178116c 100644 --- a/tests/basics/io_bytesio_ext.py +++ b/tests/basics/io_bytesio_ext.py @@ -1,5 +1,11 @@ # Extended stream operations on io.BytesIO -import io + +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.BytesIO(b"foobar") a.seek(10) print(a.read(10)) diff --git a/tests/basics/io_bytesio_ext2.py b/tests/basics/io_bytesio_ext2.py index 414ac90a3b083..f60a6a9a6041e 100644 --- a/tests/basics/io_bytesio_ext2.py +++ b/tests/basics/io_bytesio_ext2.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.BytesIO(b"foobar") try: a.seek(-10) diff --git a/tests/basics/io_iobase.py b/tests/basics/io_iobase.py index b2ee5cd63a619..31f2616310e79 100644 --- a/tests/basics/io_iobase.py +++ b/tests/basics/io_iobase.py @@ -1,15 +1,17 @@ import io try: + import io + io.IOBase -except AttributeError: +except (AttributeError, ImportError): print("SKIP") raise SystemExit class MyIO(io.IOBase): def write(self, buf): - # CPython and uPy pass in different types for buf (str vs bytearray) - print("write", len(buf)) + # CPython and MicroPython pass in different types for buf (str vs bytearray) + print('write', len(buf)) return len(buf) -print("test", file=MyIO()) +print('test', file=MyIO()) diff --git a/tests/basics/io_stringio1.py b/tests/basics/io_stringio1.py index 7d355930f5a29..889e3ce697377 100644 --- a/tests/basics/io_stringio1.py +++ b/tests/basics/io_stringio1.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.StringIO() print('io.StringIO' in repr(a)) print(a.getvalue()) diff --git a/tests/basics/io_stringio_base.py b/tests/basics/io_stringio_base.py index 0f65fb3fabc3b..c8890dab73ab7 100644 --- a/tests/basics/io_stringio_base.py +++ b/tests/basics/io_stringio_base.py @@ -1,7 +1,11 @@ # Checks that an instance type inheriting from a native base that uses # MP_TYPE_FLAG_ITER_IS_STREAM will still have a getiter. -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit a = io.StringIO() a.write("hello\nworld\nmicro\npython\n") diff --git a/tests/basics/io_stringio_with.py b/tests/basics/io_stringio_with.py index a3aa6ec84e066..0155ad5382dcd 100644 --- a/tests/basics/io_stringio_with.py +++ b/tests/basics/io_stringio_with.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + # test __enter__/__exit__ with io.StringIO() as b: b.write("foo") diff --git a/tests/basics/io_write_ext.py b/tests/basics/io_write_ext.py index 79f7450954e4c..5af1de7a6c3aa 100644 --- a/tests/basics/io_write_ext.py +++ b/tests/basics/io_write_ext.py @@ -1,11 +1,12 @@ # This tests extended (MicroPython-specific) form of write: # write(buf, len) and write(buf, offset, len) -import io try: + import io + io.BytesIO -except AttributeError: - print("SKIP") +except (AttributeError, ImportError): + print('SKIP') raise SystemExit buf = io.BytesIO() diff --git a/tests/basics/list_pop.py b/tests/basics/list_pop.py index 87ed456f85174..58bcdd91e414c 100644 --- a/tests/basics/list_pop.py +++ b/tests/basics/list_pop.py @@ -10,7 +10,7 @@ else: raise AssertionError("No IndexError raised") -# popping such that list storage shrinks (tests implementation detail of uPy) +# popping such that list storage shrinks (tests implementation detail of MicroPython) l = list(range(20)) for i in range(len(l)): l.pop() diff --git a/tests/basics/module2.py b/tests/basics/module2.py index a135601579cd7..d1dc18930f00d 100644 --- a/tests/basics/module2.py +++ b/tests/basics/module2.py @@ -1,4 +1,4 @@ -# uPy behaviour only: builtin modules are read-only +# MicroPython behaviour only: builtin modules are read-only import sys try: sys.x = 1 diff --git a/tests/basics/namedtuple1.py b/tests/basics/namedtuple1.py index 362c60583ec32..b9689b58423b8 100644 --- a/tests/basics/namedtuple1.py +++ b/tests/basics/namedtuple1.py @@ -21,6 +21,9 @@ print(isinstance(t, tuple)) + # a NamedTuple can be used as a tuple + print("(%d, %d)" % t) + # Check tuple can compare equal to namedtuple with same elements print(t == (t[0], t[1]), (t[0], t[1]) == t) diff --git a/tests/basics/parser.py b/tests/basics/parser.py index 626b67ad7a941..6ae5f05ba458e 100644 --- a/tests/basics/parser.py +++ b/tests/basics/parser.py @@ -7,7 +7,7 @@ raise SystemExit # completely empty string -# uPy and CPy differ for this case +# MPy and CPy differ for this case #try: # compile("", "stdin", "single") #except SyntaxError: diff --git a/tests/basics/python34.py b/tests/basics/python34.py index 922234d22db93..3071b55eff8bf 100644 --- a/tests/basics/python34.py +++ b/tests/basics/python34.py @@ -28,7 +28,7 @@ def test_syntax(code): test_syntax("del ()") # can't delete empty tuple (in 3.6 we can) # from basics/sys1.py -# uPy prints version 3.4 +# MicroPython prints version 3.4 import sys print(sys.version[:3]) print(sys.version_info[0], sys.version_info[1]) diff --git a/tests/basics/python36.py.exp b/tests/basics/python36.py.exp deleted file mode 100644 index 4b65daafa18fe..0000000000000 --- a/tests/basics/python36.py.exp +++ /dev/null @@ -1,5 +0,0 @@ -100000 -165 -65535 -123 -83 diff --git a/tests/basics/self_type_check.py b/tests/basics/self_type_check.py index 3bd5b3eed6058..5f6f6f2ca6585 100644 --- a/tests/basics/self_type_check.py +++ b/tests/basics/self_type_check.py @@ -3,6 +3,14 @@ import skip_if skip_if.board_in("gemma_m0", "trinket_m0") +import sys + +# Minimal builds usually don't enable MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG, +# which is required for this test. +if getattr(sys.implementation, "_build", None) == "minimal": + print("SKIP") + raise SystemExit + list.append try: diff --git a/tests/basics/slice_optimise.py b/tests/basics/slice_optimise.py new file mode 100644 index 0000000000000..f663e16b8c2f9 --- /dev/null +++ b/tests/basics/slice_optimise.py @@ -0,0 +1,23 @@ +# Test that the slice-on-stack optimisation does not break various uses of slice +# (see MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE type option). +# +# Note: this test has a corresponding .py.exp file because hashing slice objects +# was not allowed in CPython until 3.12. + +try: + from collections import OrderedDict +except ImportError: + print("SKIP") + raise SystemExit + +# Attempt to index with a slice, error should contain the slice (failed key). +try: + dict()[:] +except KeyError as e: + print("KeyError", e.args) + +# Put a slice and another object into an OrderedDict, and retrieve them. +x = OrderedDict() +x[:"a"] = 1 +x["b"] = 2 +print(list(x.keys()), list(x.values())) diff --git a/tests/basics/slice_optimise.py.exp b/tests/basics/slice_optimise.py.exp new file mode 100644 index 0000000000000..3fa59aae15ae6 --- /dev/null +++ b/tests/basics/slice_optimise.py.exp @@ -0,0 +1,2 @@ +KeyError (slice(None, None, None),) +[slice(None, 'a', None), 'b'] [1, 2] diff --git a/tests/basics/special_methods2.py.exp b/tests/basics/special_methods2.py.exp deleted file mode 100644 index a9ae75be55c95..0000000000000 --- a/tests/basics/special_methods2.py.exp +++ /dev/null @@ -1,19 +0,0 @@ -__pos__ called -__pos__ called -__neg__ called -__invert__ called -__mul__ called -__matmul__ called -__truediv__ called -__floordiv__ called -__iadd__ called -__isub__ called -__mod__ called -__pow__ called -__or__ called -__and__ called -__xor__ called -__lshift__ called -__rshift__ called -['a', 'b', 'c'] -False diff --git a/tests/basics/string_format.py b/tests/basics/string_format.py index e8600f5836139..11e7836a73ece 100644 --- a/tests/basics/string_format.py +++ b/tests/basics/string_format.py @@ -22,7 +22,17 @@ def test(fmt, *args): test("{:4x}", 123) test("{:4X}", 123) +test("{:4,d}", 1) +test("{:4_d}", 1) +test("{:4_o}", 1) +test("{:4_b}", 1) +test("{:4_x}", 1) + test("{:4,d}", 12345678) +test("{:4_d}", 12345678) +test("{:4_o}", 12345678) +test("{:4_b}", 12345678) +test("{:4_x}", 12345678) test("{:#4b}", 10) test("{:#4o}", 123) diff --git a/tests/basics/string_format_sep.py b/tests/basics/string_format_sep.py new file mode 100644 index 0000000000000..de131fdaf3383 --- /dev/null +++ b/tests/basics/string_format_sep.py @@ -0,0 +1,9 @@ +try: + "%d" % 1 +except TypeError: + print("SKIP") + raise SystemExit + +for v in (0, 0x10, 0x1000, -0x10, -0x1000): + for sz in range(1, 12): print(("{:0%d,d}" % sz).format(v)) + for sz in range(1, 12): print(("{:0%d_x}" % sz).format(v)) diff --git a/tests/basics/string_fstring_debug.py.exp b/tests/basics/string_fstring_debug.py.exp deleted file mode 100644 index f0309e1c98aa9..0000000000000 --- a/tests/basics/string_fstring_debug.py.exp +++ /dev/null @@ -1,9 +0,0 @@ -x=1 -x=00000001 -a x=1 b 2 c -a x=00000001 b 2 c -a f() + g("foo") + h()=15 b -a f() + g("foo") + h()=0000000f b -a 1,=(1,) b -a x,y,=(1, 2) b -a x,1=(1, 1) b diff --git a/tests/basics/subclass_native_init.py b/tests/basics/subclass_native_init.py index 64167fa037e0c..102befd551fad 100644 --- a/tests/basics/subclass_native_init.py +++ b/tests/basics/subclass_native_init.py @@ -1,5 +1,9 @@ # test subclassing a native type and overriding __init__ +if not hasattr(object, "__init__"): + print("SKIP") + raise SystemExit + # overriding list.__init__() class L(list): def __init__(self, a, b): diff --git a/tests/basics/syntaxerror.py b/tests/basics/syntaxerror.py index c0702cb24525e..7b6ef9b7550ea 100644 --- a/tests/basics/syntaxerror.py +++ b/tests/basics/syntaxerror.py @@ -86,7 +86,7 @@ def test_syntax(code): test_syntax("nonlocal a") test_syntax("await 1") -# error on uPy, warning on CPy +# error on MPy, warning on CPy #test_syntax("def f():\n a = 1\n global a") # default except must be last @@ -98,7 +98,7 @@ def test_syntax(code): # non-keyword after keyword test_syntax("f(a=1, 2)") -# doesn't error on uPy but should +# doesn't error on MPy but should #test_syntax("f(1, i for i in i)") # all elements of dict/set must be pairs or singles diff --git a/tests/basics/sys1.py b/tests/basics/sys1.py index 7f261aa96459d..02946abc220be 100644 --- a/tests/basics/sys1.py +++ b/tests/basics/sys1.py @@ -31,6 +31,12 @@ # Effectively skip subtests print(str) +if hasattr(sys.implementation, '_thread'): + print(sys.implementation._thread in ("GIL", "unsafe")) +else: + # Effectively skip subtests + print(True) + try: print(sys.intern('micropython') == 'micropython') has_intern = True diff --git a/tests/basics/sys_tracebacklimit.py b/tests/basics/sys_tracebacklimit.py index 3ae372b8d524e..6f575fde65404 100644 --- a/tests/basics/sys_tracebacklimit.py +++ b/tests/basics/sys_tracebacklimit.py @@ -30,7 +30,7 @@ def print_exc(e): if l.startswith(" File "): l = l.split('"') print(l[0], l[2]) - # uPy and CPy tracebacks differ in that CPy prints a source line for + # MPy and CPy tracebacks differ in that CPy prints a source line for # each traceback entry. In this case, we know that offending line # has 4-space indent, so filter it out. elif not l.startswith(" "): diff --git a/tests/basics/sys_tracebacklimit.py.native.exp b/tests/basics/sys_tracebacklimit.py.native.exp new file mode 100644 index 0000000000000..f9d30c058564b --- /dev/null +++ b/tests/basics/sys_tracebacklimit.py.native.exp @@ -0,0 +1,22 @@ +ValueError: value + +limit 4 +ValueError: value + +limit 3 +ValueError: value + +limit 2 +ValueError: value + +limit 1 +ValueError: value + +limit 0 +ValueError: value + +limit -1 +ValueError: value + +True +False diff --git a/tests/basics/try_finally_continue.py.exp b/tests/basics/try_finally_continue.py.exp deleted file mode 100644 index 2901997b1aa18..0000000000000 --- a/tests/basics/try_finally_continue.py.exp +++ /dev/null @@ -1,9 +0,0 @@ -4 0 -continue -4 1 -continue -4 2 -continue -4 3 -continue -None diff --git a/tests/basics/tuple1.py b/tests/basics/tuple1.py index 72bb3f01bff4a..70ae071898e5b 100644 --- a/tests/basics/tuple1.py +++ b/tests/basics/tuple1.py @@ -17,7 +17,7 @@ x += (10, 11, 12) print(x) -# construction of tuple from large iterator (tests implementation detail of uPy) +# construction of tuple from large iterator (tests implementation detail of MicroPython) print(tuple(range(20))) # unsupported unary operation diff --git a/tests/cmdline/cmd_compile_only.py b/tests/cmdline/cmd_compile_only.py new file mode 100644 index 0000000000000..89964c1b5bdf2 --- /dev/null +++ b/tests/cmdline/cmd_compile_only.py @@ -0,0 +1,13 @@ +# cmdline: -X compile-only +# test compile-only functionality +print("This should not be printed") +x = 1 + 2 + + +def hello(): + return "world" + + +class TestClass: + def __init__(self): + self.value = 42 diff --git a/tests/cmdline/cmd_compile_only.py.exp b/tests/cmdline/cmd_compile_only.py.exp new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/cmdline/cmd_compile_only.py.exp @@ -0,0 +1 @@ + diff --git a/tests/cmdline/cmd_compile_only_error.py b/tests/cmdline/cmd_compile_only_error.py new file mode 100644 index 0000000000000..326937a5c07ef --- /dev/null +++ b/tests/cmdline/cmd_compile_only_error.py @@ -0,0 +1,6 @@ +# cmdline: -X compile-only +# test compile-only with syntax error +print("This should not be printed") +def broken_syntax( + # Missing closing parenthesis + return "error" diff --git a/tests/cmdline/cmd_compile_only_error.py.exp b/tests/cmdline/cmd_compile_only_error.py.exp new file mode 100644 index 0000000000000..3911f71d6244d --- /dev/null +++ b/tests/cmdline/cmd_compile_only_error.py.exp @@ -0,0 +1 @@ +CRASH \ No newline at end of file diff --git a/tests/cmdline/cmd_file_variable.py b/tests/cmdline/cmd_file_variable.py new file mode 100644 index 0000000000000..6cac6744d904e --- /dev/null +++ b/tests/cmdline/cmd_file_variable.py @@ -0,0 +1,5 @@ +# Test that __file__ is set correctly for script execution +try: + print("__file__ =", __file__) +except NameError: + print("__file__ not defined") diff --git a/tests/cmdline/cmd_file_variable.py.exp b/tests/cmdline/cmd_file_variable.py.exp new file mode 100644 index 0000000000000..6807569f662b5 --- /dev/null +++ b/tests/cmdline/cmd_file_variable.py.exp @@ -0,0 +1 @@ +__file__ = cmdline/cmd_file_variable.py diff --git a/tests/cmdline/cmd_module_atexit.py b/tests/cmdline/cmd_module_atexit.py new file mode 100644 index 0000000000000..100bc112777e1 --- /dev/null +++ b/tests/cmdline/cmd_module_atexit.py @@ -0,0 +1,16 @@ +# cmdline: -m cmdline.cmd_module_atexit +# +# Test running as a module and using sys.atexit. + +import sys + +if not hasattr(sys, "atexit"): + print("SKIP") + raise SystemExit + +# Verify we ran as a module. +print(sys.argv) + +sys.atexit(lambda: print("done")) + +print("start") diff --git a/tests/cmdline/cmd_module_atexit.py.exp b/tests/cmdline/cmd_module_atexit.py.exp new file mode 100644 index 0000000000000..2a0f756b1e799 --- /dev/null +++ b/tests/cmdline/cmd_module_atexit.py.exp @@ -0,0 +1,3 @@ +['cmdline.cmd_module_atexit', 'cmdline/cmd_module_atexit.py'] +start +done diff --git a/tests/cmdline/cmd_module_atexit_exc.py b/tests/cmdline/cmd_module_atexit_exc.py new file mode 100644 index 0000000000000..88940a7741fb9 --- /dev/null +++ b/tests/cmdline/cmd_module_atexit_exc.py @@ -0,0 +1,19 @@ +# cmdline: -m cmdline.cmd_module_atexit_exc +# +# Test running as a module and using sys.atexit, with script completion via sys.exit. + +import sys + +if not hasattr(sys, "atexit"): + print("SKIP") + raise SystemExit + +# Verify we ran as a module. +print(sys.argv) + +sys.atexit(lambda: print("done")) + +print("start") + +# This will raise SystemExit to finish the script, and atexit should still be run. +sys.exit(0) diff --git a/tests/cmdline/cmd_module_atexit_exc.py.exp b/tests/cmdline/cmd_module_atexit_exc.py.exp new file mode 100644 index 0000000000000..6320d9d2d3011 --- /dev/null +++ b/tests/cmdline/cmd_module_atexit_exc.py.exp @@ -0,0 +1,3 @@ +['cmdline.cmd_module_atexit_exc', 'cmdline/cmd_module_atexit_exc.py'] +start +done diff --git a/tests/cmdline/cmd_sys_exit_0.py b/tests/cmdline/cmd_sys_exit_0.py new file mode 100644 index 0000000000000..1294b739e8ff1 --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_0.py @@ -0,0 +1,5 @@ +# cmdline: +# test sys.exit(0) - success exit code +import sys + +sys.exit(0) diff --git a/tests/cmdline/cmd_sys_exit_0.py.exp b/tests/cmdline/cmd_sys_exit_0.py.exp new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_0.py.exp @@ -0,0 +1 @@ + diff --git a/tests/cmdline/cmd_sys_exit_error.py b/tests/cmdline/cmd_sys_exit_error.py new file mode 100644 index 0000000000000..ecac15e94f1bf --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_error.py @@ -0,0 +1,5 @@ +# cmdline: +# test sys.exit() functionality and exit codes +import sys + +sys.exit(123) diff --git a/tests/cmdline/cmd_sys_exit_error.py.exp b/tests/cmdline/cmd_sys_exit_error.py.exp new file mode 100644 index 0000000000000..3911f71d6244d --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_error.py.exp @@ -0,0 +1 @@ +CRASH \ No newline at end of file diff --git a/tests/cmdline/cmd_sys_exit_none.py b/tests/cmdline/cmd_sys_exit_none.py new file mode 100644 index 0000000000000..66e19666589ed --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_none.py @@ -0,0 +1,5 @@ +# cmdline: +# test sys.exit(None) - should exit with code 0 +import sys + +sys.exit(None) diff --git a/tests/cmdline/cmd_sys_exit_none.py.exp b/tests/cmdline/cmd_sys_exit_none.py.exp new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_none.py.exp @@ -0,0 +1 @@ + diff --git a/tests/cmdline/repl_autocomplete_underscore.py b/tests/cmdline/repl_autocomplete_underscore.py new file mode 100644 index 0000000000000..98bbb699200d3 --- /dev/null +++ b/tests/cmdline/repl_autocomplete_underscore.py @@ -0,0 +1,33 @@ +# Test REPL autocompletion filtering of underscore attributes + +# Start paste mode +{\x05} +class TestClass: + def __init__(self): + self.public_attr = 1 + self._private_attr = 2 + self.__very_private = 3 + + def public_method(self): + pass + + def _private_method(self): + pass + + @property + def public_property(self): + return 42 + + @property + def _private_property(self): + return 99 + +{\x04} +# Paste executed + +# Create an instance +obj = TestClass() + +# Test tab completion on the instance +# The tab character after `obj.` and 'a' below triggers the completions +obj.{\x09}{\x09}a{\x09} diff --git a/tests/cmdline/repl_autocomplete_underscore.py.exp b/tests/cmdline/repl_autocomplete_underscore.py.exp new file mode 100644 index 0000000000000..f9720ef233180 --- /dev/null +++ b/tests/cmdline/repl_autocomplete_underscore.py.exp @@ -0,0 +1,41 @@ +MicroPython \.\+ version +Type "help()" for more information. +>>> # Test REPL autocompletion filtering of underscore attributes +>>> +>>> # Start paste mode +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== class TestClass: +=== def __init__(self): +=== self.public_attr = 1 +=== self._private_attr = 2 +=== self.__very_private = 3 +=== +=== def public_method(self): +=== pass +=== +=== def _private_method(self): +=== pass +=== +=== @property +=== def public_property(self): +=== return 42 +=== +=== @property +=== def _private_property(self): +=== return 99 +=== +=== +>>> # Paste executed +>>> +>>> # Create an instance +>>> obj = TestClass() +>>> +>>> # Test tab completion on the instance +>>> # The tab character after `obj.` and 'a' below triggers the completions +>>> obj.public_ +public_attr public_method public_property +>>> obj.public_attr +1 +>>> diff --git a/tests/cmdline/repl_lock.py b/tests/cmdline/repl_lock.py new file mode 100644 index 0000000000000..77e2e9327401a --- /dev/null +++ b/tests/cmdline/repl_lock.py @@ -0,0 +1,7 @@ +import micropython +micropython.heap_lock() +1+1 +micropython.heap_lock() +None # Cause the repl's line storage to be enlarged ---------------- +micropython.heap_lock() +raise SystemExit diff --git a/tests/cmdline/repl_lock.py.exp b/tests/cmdline/repl_lock.py.exp new file mode 100644 index 0000000000000..d921fd6ae8e99 --- /dev/null +++ b/tests/cmdline/repl_lock.py.exp @@ -0,0 +1,10 @@ +MicroPython \.\+ version +Type "help()" for more information. +>>> import micropython +>>> micropython.heap_lock() +>>> 1+1 +2 +>>> micropython.heap_lock() +>>> None # Cause the repl's line storage to be enlarged ---------------- +>>> micropython.heap_lock() +>>> raise SystemExit diff --git a/tests/cmdline/repl_paste.py b/tests/cmdline/repl_paste.py new file mode 100644 index 0000000000000..7cec450fce7bf --- /dev/null +++ b/tests/cmdline/repl_paste.py @@ -0,0 +1,90 @@ +# Test REPL paste mode functionality + +# Basic paste mode with a simple function +{\x05} +def hello(): + print('Hello from paste mode!') +hello() +{\x04} + +# Paste mode with multiple indentation levels +{\x05} +def calculate(n): + if n > 0: + for i in range(n): + if i % 2 == 0: + print(f'Even: {i}') + else: + print(f'Odd: {i}') + else: + print('n must be positive') + +calculate(5) +{\x04} + +# Paste mode with blank lines +{\x05} +def function_with_blanks(): + print('First line') + + print('After blank line') + + + print('After two blank lines') + +function_with_blanks() +{\x04} + +# Paste mode with class definition and multiple methods +{\x05} +class TestClass: + def __init__(self, value): + self.value = value + + def display(self): + print(f'Value is: {self.value}') + + def double(self): + self.value *= 2 + return self.value + +obj = TestClass(21) +obj.display() +print(f'Doubled: {obj.double()}') +obj.display() +{\x04} + +# Paste mode with exception handling +{\x05} +try: + x = 1 / 0 +except ZeroDivisionError: + print('Caught division by zero') +finally: + print('Finally block executed') +{\x04} + +# Cancel paste mode with Ctrl-C +{\x05} +print('This should not execute') +{\x03} + +# Normal REPL still works after cancelled paste +print('Back to normal REPL') + +# Paste mode with syntax error +{\x05} +def bad_syntax(: + print('Missing parameter') +{\x04} + +# Paste mode with runtime error +{\x05} +def will_error(): + undefined_variable + +will_error() +{\x04} + +# Final test to show REPL is still functioning +1 + 2 + 3 diff --git a/tests/cmdline/repl_paste.py.exp b/tests/cmdline/repl_paste.py.exp new file mode 100644 index 0000000000000..2b837f85cf993 --- /dev/null +++ b/tests/cmdline/repl_paste.py.exp @@ -0,0 +1,133 @@ +MicroPython \.\+ version +Type "help()" for more information. +>>> # Test REPL paste mode functionality +>>> +>>> # Basic paste mode with a simple function +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def hello(): +=== print('Hello from paste mode!') +=== hello() +=== +Hello from paste mode! +>>> +>>> # Paste mode with multiple indentation levels +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def calculate(n): +=== if n > 0: +=== for i in range(n): +=== if i % 2 == 0: +=== print(f'Even: {i}') +=== else: +=== print(f'Odd: {i}') +=== else: +=== print('n must be positive') +=== +=== calculate(5) +=== +Even: 0 +Odd: 1 +Even: 2 +Odd: 3 +Even: 4 +>>> +>>> # Paste mode with blank lines +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def function_with_blanks(): +=== print('First line') +=== +=== print('After blank line') +=== +=== +=== print('After two blank lines') +=== +=== function_with_blanks() +=== +First line +After blank line +After two blank lines +>>> +>>> # Paste mode with class definition and multiple methods +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== class TestClass: +=== def __init__(self, value): +=== self.value = value +=== +=== def display(self): +=== print(f'Value is: {self.value}') +=== +=== def double(self): +=== self.value *= 2 +=== return self.value +=== +=== obj = TestClass(21) +=== obj.display() +=== print(f'Doubled: {obj.double()}') +=== obj.display() +=== +Value is: 21 +Doubled: 42 +Value is: 42 +>>> +>>> # Paste mode with exception handling +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== try: +=== x = 1 / 0 +=== except ZeroDivisionError: +=== print('Caught division by zero') +=== finally: +=== print('Finally block executed') +=== +Caught division by zero +Finally block executed +>>> +>>> # Cancel paste mode with Ctrl-C +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== print('This should not execute') +=== +>>> +>>> +>>> # Normal REPL still works after cancelled paste +>>> print('Back to normal REPL') +Back to normal REPL +>>> +>>> # Paste mode with syntax error +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def bad_syntax(: +=== print('Missing parameter') +=== +Traceback (most recent call last): + File "", line 2 +SyntaxError: invalid syntax +>>> +>>> # Paste mode with runtime error +>>> +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== +=== def will_error(): +=== undefined_variable +=== +=== will_error() +=== +Traceback (most recent call last): + File "", line 5, in + File "", line 3, in will_error +NameError: name 'undefined_variable' isn't defined +>>> +>>> # Final test to show REPL is still functioning +>>> 1 + 2 + 3 +6 +>>> diff --git a/tests/cpydiff/core_class_initsubclass.py b/tests/cpydiff/core_class_initsubclass.py new file mode 100644 index 0000000000000..8683271dcb0f2 --- /dev/null +++ b/tests/cpydiff/core_class_initsubclass.py @@ -0,0 +1,21 @@ +""" +categories: Core,Classes +description: ``__init_subclass__`` isn't automatically called. +cause: MicroPython does not currently implement PEP 487. +workaround: Manually call ``__init_subclass__`` after class creation if needed. e.g.:: + + class A(Base): + pass + A.__init_subclass__() + +""" + + +class Base: + @classmethod + def __init_subclass__(cls): + print(f"Base.__init_subclass__({cls.__name__})") + + +class A(Base): + pass diff --git a/tests/cpydiff/core_class_initsubclass_autoclassmethod.py b/tests/cpydiff/core_class_initsubclass_autoclassmethod.py new file mode 100644 index 0000000000000..b2f7628976c92 --- /dev/null +++ b/tests/cpydiff/core_class_initsubclass_autoclassmethod.py @@ -0,0 +1,31 @@ +""" +categories: Core,Classes +description: ``__init_subclass__`` isn't an implicit classmethod. +cause: MicroPython does not currently implement PEP 487. ``__init_subclass__`` is not currently in the list of special-cased class/static methods. +workaround: Decorate declarations of ``__init_subclass__`` with ``@classmethod``. +""" + + +def regularize_spelling(text, prefix="bound_"): + # for regularizing across the CPython "method" vs MicroPython "bound_method" spelling for the type of a bound classmethod + if text.startswith(prefix): + return text[len(prefix) :] + return text + + +class A: + def __init_subclass__(cls): + pass + + @classmethod + def manual_decorated(cls): + pass + + +a = type(A.__init_subclass__).__name__ +b = type(A.manual_decorated).__name__ + +print(regularize_spelling(a)) +print(regularize_spelling(b)) +if a != b: + print("FAIL") diff --git a/tests/cpydiff/core_class_initsubclass_kwargs.py b/tests/cpydiff/core_class_initsubclass_kwargs.py new file mode 100644 index 0000000000000..ed5157afeaede --- /dev/null +++ b/tests/cpydiff/core_class_initsubclass_kwargs.py @@ -0,0 +1,22 @@ +""" +categories: Core,Classes +description: MicroPython doesn't support parameterized ``__init_subclass__`` class customization. +cause: MicroPython does not currently implement PEP 487. The MicroPython syntax tree does not include a kwargs node after the class inheritance list. +workaround: Use class variables or another mechanism to specify base-class customizations. +""" + + +class Base: + @classmethod + def __init_subclass__(cls, arg=None, **kwargs): + cls.init_subclass_was_called = True + print(f"Base.__init_subclass__({cls.__name__}, {arg=!r}, {kwargs=!r})") + + +class A(Base, arg="arg"): + pass + + +# Regularize across MicroPython not automatically calling __init_subclass__ either. +if not getattr(A, "init_subclass_was_called", False): + A.__init_subclass__() diff --git a/tests/cpydiff/core_class_initsubclass_super.py b/tests/cpydiff/core_class_initsubclass_super.py new file mode 100644 index 0000000000000..d18671a74bf8e --- /dev/null +++ b/tests/cpydiff/core_class_initsubclass_super.py @@ -0,0 +1,22 @@ +""" +categories: Core,Classes +description: ``__init_subclass__`` can't be defined a cooperatively-recursive way. +cause: MicroPython does not currently implement PEP 487. The base object type does not have an ``__init_subclass__`` implementation. +workaround: Omit the recursive ``__init_subclass__`` call unless it's known that the grandparent also defines it. +""" + + +class Base: + @classmethod + def __init_subclass__(cls, **kwargs): + cls.init_subclass_was_called = True + super().__init_subclass__(**kwargs) + + +class A(Base): + pass + + +# Regularize across MicroPython not automatically calling __init_subclass__ either. +if not getattr(A, "init_subclass_was_called", False): + A.__init_subclass__() diff --git a/tests/cpydiff/core_fstring_concat.py b/tests/cpydiff/core_fstring_concat.py index 3daa13d75360e..2fbe1b961a1e6 100644 --- a/tests/cpydiff/core_fstring_concat.py +++ b/tests/cpydiff/core_fstring_concat.py @@ -1,5 +1,5 @@ """ -categories: Core +categories: Core,f-strings description: f-strings don't support concatenation with adjacent literals if the adjacent literals contain braces cause: MicroPython is optimised for code space. workaround: Use the + operator between literal strings when they are not both f-strings diff --git a/tests/cpydiff/core_fstring_parser.py b/tests/cpydiff/core_fstring_parser.py index 87cf1e63ed8c2..4964b9707aceb 100644 --- a/tests/cpydiff/core_fstring_parser.py +++ b/tests/cpydiff/core_fstring_parser.py @@ -1,5 +1,5 @@ """ -categories: Core +categories: Core,f-strings description: f-strings cannot support expressions that require parsing to resolve unbalanced nested braces and brackets cause: MicroPython is optimised for code space. workaround: Always use balanced braces and brackets in expressions inside f-strings diff --git a/tests/cpydiff/core_fstring_repr.py b/tests/cpydiff/core_fstring_repr.py index d37fb48db758c..2589a34b7e3b9 100644 --- a/tests/cpydiff/core_fstring_repr.py +++ b/tests/cpydiff/core_fstring_repr.py @@ -1,5 +1,5 @@ """ -categories: Core +categories: Core,f-strings description: f-strings don't support !a conversions cause: MicropPython does not implement ascii() workaround: None diff --git a/tests/cpydiff/core_import_all.py b/tests/cpydiff/core_import_all.py deleted file mode 100644 index 0fbe9d4d4ecd6..0000000000000 --- a/tests/cpydiff/core_import_all.py +++ /dev/null @@ -1,10 +0,0 @@ -""" -categories: Core,import -description: __all__ is unsupported in __init__.py in MicroPython. -cause: Not implemented. -workaround: Manually import the sub-modules directly in __init__.py using ``from . import foo, bar``. -""" - -from modules3 import * - -foo.hello() diff --git a/tests/cpydiff/modules3/__init__.py b/tests/cpydiff/modules3/__init__.py deleted file mode 100644 index 27a2bf2ad9044..0000000000000 --- a/tests/cpydiff/modules3/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ["foo"] diff --git a/tests/cpydiff/modules3/foo.py b/tests/cpydiff/modules3/foo.py deleted file mode 100644 index dd9b9d4ddd4c4..0000000000000 --- a/tests/cpydiff/modules3/foo.py +++ /dev/null @@ -1,2 +0,0 @@ -def hello(): - print("hello") diff --git a/tests/cpydiff/modules_errno_enotsup.py b/tests/cpydiff/modules_errno_enotsup.py new file mode 100644 index 0000000000000..80e5ad9d03220 --- /dev/null +++ b/tests/cpydiff/modules_errno_enotsup.py @@ -0,0 +1,10 @@ +""" +categories: Modules,errno +description: MicroPython does not include ``ENOTSUP`` as a name for errno 95. +cause: MicroPython does not implement the ``ENOTSUP`` canonical alias for ``EOPNOTSUPP`` added in CPython 3.2. +workaround: Use ``errno.EOPNOTSUPP`` in place of ``errno.ENOTSUP``. +""" + +import errno + +print(f"{errno.errorcode[errno.EOPNOTSUPP]=!s}") diff --git a/tests/cpydiff/modules_struct_fewargs.py b/tests/cpydiff/modules_struct_fewargs.py index 49b2a3213c898..f6346a67938ed 100644 --- a/tests/cpydiff/modules_struct_fewargs.py +++ b/tests/cpydiff/modules_struct_fewargs.py @@ -1,6 +1,6 @@ """ categories: Modules,struct -description: Struct pack with too few args, not checked by uPy +description: Struct pack with too few args, not checked by MicroPython cause: Unknown workaround: Unknown """ diff --git a/tests/cpydiff/modules_struct_manyargs.py b/tests/cpydiff/modules_struct_manyargs.py index e3b78930f219f..b2ea93b6c9330 100644 --- a/tests/cpydiff/modules_struct_manyargs.py +++ b/tests/cpydiff/modules_struct_manyargs.py @@ -1,6 +1,6 @@ """ categories: Modules,struct -description: Struct pack with too many args, not checked by uPy +description: Struct pack with too many args, not checked by MicroPython cause: Unknown workaround: Unknown """ diff --git a/tests/cpydiff/modules_struct_whitespace_in_format.py b/tests/cpydiff/modules_struct_whitespace_in_format.py index a7a1d2facdfff..8b609425eb01c 100644 --- a/tests/cpydiff/modules_struct_whitespace_in_format.py +++ b/tests/cpydiff/modules_struct_whitespace_in_format.py @@ -1,6 +1,6 @@ """ categories: Modules,struct -description: Struct pack with whitespace in format, whitespace ignored by CPython, error on uPy +description: Struct pack with whitespace in format, whitespace ignored by CPython, error on MicroPython cause: MicroPython is optimised for code size. workaround: Don't use spaces in format strings. """ diff --git a/tests/cpydiff/syntax_arg_unpacking.py b/tests/cpydiff/syntax_arg_unpacking.py index e54832ddb9165..7133a8a28272c 100644 --- a/tests/cpydiff/syntax_arg_unpacking.py +++ b/tests/cpydiff/syntax_arg_unpacking.py @@ -1,5 +1,5 @@ """ -categories: Syntax +categories: Syntax,Unpacking description: Argument unpacking does not work if the argument being unpacked is the nth or greater argument where n is the number of bits in an MP_SMALL_INT. cause: The implementation uses an MP_SMALL_INT to flag args that need to be unpacked. workaround: Use fewer arguments. diff --git a/tests/cpydiff/syntax_literal_underscore.py b/tests/cpydiff/syntax_literal_underscore.py new file mode 100644 index 0000000000000..4b1406e9f3f71 --- /dev/null +++ b/tests/cpydiff/syntax_literal_underscore.py @@ -0,0 +1,19 @@ +""" +categories: Syntax,Literals +description: MicroPython accepts underscores in numeric literals where CPython doesn't +cause: Different parser implementation + +MicroPython's tokenizer ignores underscores in numeric literals, while CPython +rejects multiple consecutive underscores and underscores after the last digit. + +workaround: Remove the underscores not accepted by CPython. +""" + +try: + print(eval("1__1")) +except SyntaxError: + print("Should not work") +try: + print(eval("1_")) +except SyntaxError: + print("Should not work") diff --git a/tests/cpydiff/syntax_spaces.py b/tests/cpydiff/syntax_spaces.py index 03d25d56199d1..670cefdeac23e 100644 --- a/tests/cpydiff/syntax_spaces.py +++ b/tests/cpydiff/syntax_spaces.py @@ -1,8 +1,15 @@ """ -categories: Syntax,Spaces -description: uPy requires spaces between literal numbers and keywords, CPy doesn't -cause: Unknown -workaround: Unknown +categories: Syntax,Literals +description: MicroPython requires spaces between literal numbers and keywords or ".", CPython doesn't +cause: Different parser implementation + +MicroPython's tokenizer treats a sequence like ``1and`` as a single token, while CPython treats it as two tokens. + +Since CPython 3.11, when the literal number is followed by a token, this syntax causes a ``SyntaxWarning`` for an "invalid literal". When a literal number is followed by a "." denoting attribute access, CPython does not warn. + +workaround: Add a space between the integer literal and the intended next token. + +This also fixes the ``SyntaxWarning`` in CPython. """ try: @@ -17,3 +24,7 @@ print(eval("1if 1else 0")) except SyntaxError: print("Should have worked") +try: + print(eval("0x1.to_bytes(1)")) +except SyntaxError: + print("Should have worked") diff --git a/tests/cpydiff/types_complex_parser.py b/tests/cpydiff/types_complex_parser.py new file mode 100644 index 0000000000000..4a012987d9e48 --- /dev/null +++ b/tests/cpydiff/types_complex_parser.py @@ -0,0 +1,14 @@ +""" +categories: Types,complex +description: MicroPython's complex() accepts certain incorrect values that CPython rejects +cause: MicroPython is highly optimized for memory usage. +workaround: Do not use non-standard complex literals as argument to complex() + +MicroPython's ``complex()`` function accepts literals that contain a space and +no sign between the real and imaginary parts, and interprets it as a plus. +""" + +try: + print(complex("1 1j")) +except ValueError: + print("ValueError") diff --git a/tests/cpydiff/types_float_implicit_conversion.py b/tests/cpydiff/types_float_implicit_conversion.py index 3726839fac6d1..764c9e4e6ed7a 100644 --- a/tests/cpydiff/types_float_implicit_conversion.py +++ b/tests/cpydiff/types_float_implicit_conversion.py @@ -1,6 +1,6 @@ """ categories: Types,float -description: uPy allows implicit conversion of objects in maths operations while CPython does not. +description: MicroPython allows implicit conversion of objects in maths operations while CPython does not. cause: Unknown workaround: Objects should be wrapped in ``float(obj)`` for compatibility with CPython. """ diff --git a/tests/cpydiff/types_float_rounding.py b/tests/cpydiff/types_float_rounding.py deleted file mode 100644 index 206e359ed9be7..0000000000000 --- a/tests/cpydiff/types_float_rounding.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -categories: Types,float -description: uPy and CPython outputs formats may differ -cause: Unknown -workaround: Unknown -""" - -print("%.1g" % -9.9) diff --git a/tests/cpydiff/types_oserror_errnomap.py b/tests/cpydiff/types_oserror_errnomap.py new file mode 100644 index 0000000000000..6627bd2af4ab7 --- /dev/null +++ b/tests/cpydiff/types_oserror_errnomap.py @@ -0,0 +1,48 @@ +""" +categories: Types,OSError +description: OSError constructor returns a plain OSError for all errno values, rather than a relevant subtype. +cause: MicroPython does not include the CPython-standard OSError subclasses. +workaround: Catch OSError and use its errno attribute to discriminate the cause. +""" + +import errno + +errno_list = [ # i.e. the set implemented by micropython + errno.EPERM, + errno.ENOENT, + errno.EIO, + errno.EBADF, + errno.EAGAIN, + errno.ENOMEM, + errno.EACCES, + errno.EEXIST, + errno.ENODEV, + errno.EISDIR, + errno.EINVAL, + errno.EOPNOTSUPP, + errno.EADDRINUSE, + errno.ECONNABORTED, + errno.ECONNRESET, + errno.ENOBUFS, + errno.ENOTCONN, + errno.ETIMEDOUT, + errno.ECONNREFUSED, + errno.EHOSTUNREACH, + errno.EALREADY, + errno.EINPROGRESS, +] + + +def errno_output_type(n): + try: + raise OSError(n, "") + except OSError as e: + return f"{type(e).__name__}" + except Exception as e: + return f"non-OSError {type(e).__name__}" + else: + return "no error" + + +for n in errno_list: + print(errno.errorcode[n], "=", errno_output_type(n)) diff --git a/tests/cpydiff/types_range_limits.py b/tests/cpydiff/types_range_limits.py new file mode 100644 index 0000000000000..e53d5fd4088f3 --- /dev/null +++ b/tests/cpydiff/types_range_limits.py @@ -0,0 +1,26 @@ +""" +categories: Types,range +description: Range objects with large start or stop arguments misbehave. +cause: Intermediate calculations overflow the C mp_int_t type +workaround: Avoid using such ranges +""" + +from sys import maxsize + +# A range including `maxsize-1` cannot be created +try: + print(range(-maxsize - 1, 0)) +except OverflowError: + print("OverflowError") + +# A range with `stop-start` exceeding sys.maxsize has incorrect len(), while CPython cannot calculate len(). +try: + print(len(range(-maxsize, maxsize))) +except OverflowError: + print("OverflowError") + +# A range with `stop-start` exceeding sys.maxsize has incorrect len() +try: + print(len(range(-maxsize, maxsize, maxsize))) +except OverflowError: + print("OverflowError") diff --git a/tests/cpydiff/types_str_formatsep.py b/tests/cpydiff/types_str_formatsep.py new file mode 100644 index 0000000000000..05d0b8d3d2cf4 --- /dev/null +++ b/tests/cpydiff/types_str_formatsep.py @@ -0,0 +1,19 @@ +""" +categories: Types,str +description: MicroPython accepts the "," grouping option with any radix, unlike CPython +cause: To reduce code size, MicroPython does not issue an error for this combination +workaround: Do not use a format string like ``{:,b}`` if CPython compatibility is required. +""" + +try: + print("{:,b}".format(99)) +except ValueError: + print("ValueError") +try: + print("{:,x}".format(99)) +except ValueError: + print("ValueError") +try: + print("{:,o}".format(99)) +except ValueError: + print("ValueError") diff --git a/tests/cpydiff/types_str_formatsep_float.py b/tests/cpydiff/types_str_formatsep_float.py new file mode 100644 index 0000000000000..b487cd3758e73 --- /dev/null +++ b/tests/cpydiff/types_str_formatsep_float.py @@ -0,0 +1,11 @@ +""" +categories: Types,str +description: MicroPython accepts but does not properly implement the "," or "_" grouping character for float values +cause: To reduce code size, MicroPython does not implement this combination. Grouping characters will not appear in the number's significant digits and will appear at incorrect locations in leading zeros. +workaround: Do not use a format string like ``{:,f}`` if exact CPython compatibility is required. +""" + +print("{:,f}".format(3141.159)) +print("{:_f}".format(3141.159)) +print("{:011,.2f}".format(3141.159)) +print("{:011_.2f}".format(3141.159)) diff --git a/tests/extmod/asyncio_basic.py.exp b/tests/extmod/asyncio_basic.py.exp deleted file mode 100644 index 478e22abc8ff6..0000000000000 --- a/tests/extmod/asyncio_basic.py.exp +++ /dev/null @@ -1,6 +0,0 @@ -start -after sleep -short -long -negative -took 200 400 0 diff --git a/tests/extmod/asyncio_event_queue.py b/tests/extmod/asyncio_event_queue.py new file mode 100644 index 0000000000000..e0125b1aefe13 --- /dev/null +++ b/tests/extmod/asyncio_event_queue.py @@ -0,0 +1,64 @@ +# Ensure that an asyncio task can wait on an Event when the +# _task_queue is empty +# https://github.com/micropython/micropython/issues/16569 + +try: + import asyncio +except ImportError: + print("SKIP") + raise SystemExit + +# This test requires checking that the asyncio scheduler +# remains active "indefinitely" when the task queue is empty. +# +# To check this, we need another independent scheduler that +# can wait for a certain amount of time. So we have to +# create one using micropython.schedule() and time.ticks_ms() +# +# Technically, this code breaks the rules, as it is clearly +# documented that Event.set() should _NOT_ be called from a +# schedule (soft IRQ) because in some cases, a race condition +# can occur, resulting in a crash. However: +# - since the risk of a race condition in that specific +# case has been analysed and excluded +# - given that there is no other simple alternative to +# write this test case, +# an exception to the rule was deemed acceptable. See +# https://github.com/micropython/micropython/pull/16772 + +import micropython, time + +try: + micropython.schedule +except AttributeError: + print("SKIP") + raise SystemExit + + +evt = asyncio.Event() + + +def schedule_watchdog(end_ticks): + if time.ticks_diff(end_ticks, time.ticks_ms()) <= 0: + print("asyncio still pending, unlocking event") + # Caution: about to call Event.set() from a schedule + # (see the note in the comment above) + evt.set() + return + micropython.schedule(schedule_watchdog, end_ticks) + + +async def foo(): + print("foo waiting") + schedule_watchdog(time.ticks_add(time.ticks_ms(), 100)) + await evt.wait() + print("foo done") + + +async def main(): + print("main started") + await foo() + print("main done") + + +asyncio.run(main()) diff --git a/tests/extmod/asyncio_event_queue.py.exp b/tests/extmod/asyncio_event_queue.py.exp new file mode 100644 index 0000000000000..ee42c96d83ed8 --- /dev/null +++ b/tests/extmod/asyncio_event_queue.py.exp @@ -0,0 +1,5 @@ +main started +foo waiting +asyncio still pending, unlocking event +foo done +main done diff --git a/tests/extmod/asyncio_heaplock.py b/tests/extmod/asyncio_heaplock.py index 8326443f0e6c5..9e9908de1cb6a 100644 --- a/tests/extmod/asyncio_heaplock.py +++ b/tests/extmod/asyncio_heaplock.py @@ -4,7 +4,11 @@ # - StreamWriter.write, stream is blocked and data to write is a bytes object # - StreamWriter.write, when stream is not blocked -import micropython +try: + import asyncio, micropython +except ImportError: + print("SKIP") + raise SystemExit # strict stackless builds can't call functions without allocating a frame on the heap try: @@ -24,12 +28,6 @@ def f(x): print("SKIP") raise SystemExit -try: - import asyncio -except ImportError: - print("SKIP") - raise SystemExit - class TestStream: def __init__(self, blocked): diff --git a/tests/extmod/asyncio_iterator_event.py b/tests/extmod/asyncio_iterator_event.py new file mode 100644 index 0000000000000..f61fefcf051be --- /dev/null +++ b/tests/extmod/asyncio_iterator_event.py @@ -0,0 +1,86 @@ +# Ensure that an asyncio task can wait on an Event when the +# _task_queue is empty, in the context of an async iterator +# https://github.com/micropython/micropython/issues/16318 + +try: + import asyncio +except ImportError: + print("SKIP") + raise SystemExit + +# This test requires checking that the asyncio scheduler +# remains active "indefinitely" when the task queue is empty. +# +# To check this, we need another independent scheduler that +# can wait for a certain amount of time. So we have to +# create one using micropython.schedule() and time.ticks_ms() +# +# Technically, this code breaks the rules, as it is clearly +# documented that Event.set() should _NOT_ be called from a +# schedule (soft IRQ) because in some cases, a race condition +# can occur, resulting in a crash. However: +# - since the risk of a race condition in that specific +# case has been analysed and excluded +# - given that there is no other simple alternative to +# write this test case, +# an exception to the rule was deemed acceptable. See +# https://github.com/micropython/micropython/pull/16772 + +import micropython, time + +try: + micropython.schedule +except AttributeError: + print("SKIP") + raise SystemExit + +ai = None + + +def schedule_watchdog(end_ticks): + if time.ticks_diff(end_ticks, time.ticks_ms()) <= 0: + print("good: asyncio iterator is still pending, exiting") + # Caution: ai.fetch_data() will invoke Event.set() + # (see the note in the comment above) + ai.fetch_data(None) + return + micropython.schedule(schedule_watchdog, end_ticks) + + +async def test(ai): + for x in range(3): + await asyncio.sleep(0.1) + ai.fetch_data("bar {}".format(x)) + + +class AsyncIterable: + def __init__(self): + self.message = None + self.evt = asyncio.Event() + + def __aiter__(self): + return self + + async def __anext__(self): + await self.evt.wait() + self.evt.clear() + if self.message is None: + raise StopAsyncIteration + return self.message + + def fetch_data(self, message): + self.message = message + self.evt.set() + + +async def main(): + global ai + ai = AsyncIterable() + asyncio.create_task(test(ai)) + schedule_watchdog(time.ticks_add(time.ticks_ms(), 500)) + async for message in ai: + print(message) + print("end main") + + +asyncio.run(main()) diff --git a/tests/extmod/asyncio_iterator_event.py.exp b/tests/extmod/asyncio_iterator_event.py.exp new file mode 100644 index 0000000000000..a1893197d02ef --- /dev/null +++ b/tests/extmod/asyncio_iterator_event.py.exp @@ -0,0 +1,5 @@ +bar 0 +bar 1 +bar 2 +good: asyncio iterator is still pending, exiting +end main diff --git a/tests/extmod/asyncio_lock.py.exp b/tests/extmod/asyncio_lock.py.exp deleted file mode 100644 index a37dfcbd2e519..0000000000000 --- a/tests/extmod/asyncio_lock.py.exp +++ /dev/null @@ -1,41 +0,0 @@ -False -True -False -have lock ----- -task start 1 -task start 2 -task start 3 -task have 1 0 -task have 2 0 -task have 3 0 -task have 1 1 -task have 2 1 -task have 3 1 -task have 1 2 -task end 1 -task have 2 2 -task end 2 -task have 3 2 -task end 3 ----- -task have True -task release False -task have True -task release False -task have again -task have again ----- -task got 0 -task release 0 -task cancel 1 -task got 2 -task release 2 -False ----- -task got 0 -task cancel 1 -task release 0 -task got 2 -task cancel 2 -False diff --git a/tests/extmod/asyncio_set_exception_handler.py b/tests/extmod/asyncio_set_exception_handler.py index 5935f0f4ebeaa..0ac4a624224ce 100644 --- a/tests/extmod/asyncio_set_exception_handler.py +++ b/tests/extmod/asyncio_set_exception_handler.py @@ -12,7 +12,7 @@ def custom_handler(loop, context): async def task(i): - # Raise with 2 args so exception prints the same in uPy and CPython + # Raise with 2 args so exception prints the same in MicroPython and CPython raise ValueError(i, i + 1) diff --git a/tests/extmod/asyncio_wait_for_linked_task.py b/tests/extmod/asyncio_wait_for_linked_task.py new file mode 100644 index 0000000000000..4dda62d5476c7 --- /dev/null +++ b/tests/extmod/asyncio_wait_for_linked_task.py @@ -0,0 +1,66 @@ +# Test asyncio.wait_for, with dependent tasks +# https://github.com/micropython/micropython/issues/16759 + +try: + import asyncio +except ImportError: + print("SKIP") + raise SystemExit + + +# CPython 3.12 deprecated calling get_event_loop() when there is no current event +# loop, so to make this test run on CPython requires setting the event loop. +if hasattr(asyncio, "set_event_loop"): + asyncio.set_event_loop(asyncio.new_event_loop()) + + +class Worker: + def __init__(self): + self._eventLoop = None + self._tasks = [] + + def launchTask(self, asyncJob): + if self._eventLoop is None: + self._eventLoop = asyncio.get_event_loop() + return self._eventLoop.create_task(asyncJob) + + async def job(self, prerequisite, taskName): + if prerequisite: + await prerequisite + await asyncio.sleep(0.1) + print(taskName, "work completed") + + def planTasks(self): + self._tasks.append(self.launchTask(self.job(None, "task0"))) + self._tasks.append(self.launchTask(self.job(self._tasks[0], "task1"))) + self._tasks.append(self.launchTask(self.job(self._tasks[1], "task2"))) + + async def waitForTask(self, taskIdx): + return await self._tasks[taskIdx] + + def syncWaitForTask(self, taskIdx): + return self._eventLoop.run_until_complete(self._tasks[taskIdx]) + + +async def async_test(): + print("--- async test") + worker = Worker() + worker.planTasks() + await worker.waitForTask(0) + print("-> task0 done") + await worker.waitForTask(2) + print("-> task2 done") + + +def sync_test(): + print("--- sync test") + worker = Worker() + worker.planTasks() + worker.syncWaitForTask(0) + print("-> task0 done") + worker.syncWaitForTask(2) + print("-> task2 done") + + +asyncio.get_event_loop().run_until_complete(async_test()) +sync_test() diff --git a/tests/extmod/asyncio_wait_task.py.exp b/tests/extmod/asyncio_wait_task.py.exp deleted file mode 100644 index 514a434223315..0000000000000 --- a/tests/extmod/asyncio_wait_task.py.exp +++ /dev/null @@ -1,12 +0,0 @@ -start -task 1 -task 2 ----- -start -hello -world -took 200 200 -task_raise -ValueError -task_raise -ValueError diff --git a/tests/extmod/binascii_hexlify.py b/tests/extmod/binascii_hexlify.py index d06029aabaffb..ae90b67336586 100644 --- a/tests/extmod/binascii_hexlify.py +++ b/tests/extmod/binascii_hexlify.py @@ -1,5 +1,5 @@ try: - import binascii + from binascii import hexlify except ImportError: print("SKIP") raise SystemExit @@ -10,10 +10,10 @@ b"\x7f\x80\xff", b"1234ABCDabcd", ): - print(binascii.hexlify(x)) + print(hexlify(x)) # Two-argument version (now supported in CPython) -print(binascii.hexlify(b"123", ":")) +print(hexlify(b"123", ":")) # zero length buffer -print(binascii.hexlify(b"", b":")) +print(hexlify(b"", b":")) diff --git a/tests/extmod/binascii_unhexlify.py b/tests/extmod/binascii_unhexlify.py index 731b6a2bd4d10..fe1d50780eeac 100644 --- a/tests/extmod/binascii_unhexlify.py +++ b/tests/extmod/binascii_unhexlify.py @@ -1,5 +1,5 @@ try: - import binascii + from binascii import unhexlify except ImportError: print("SKIP") raise SystemExit @@ -10,18 +10,18 @@ b"7f80ff", b"313233344142434461626364", ): - print(binascii.unhexlify(x)) + print(unhexlify(x)) # CIRCUITPY-CHANGE # Unicode strings can be decoded print(binascii.unhexlify("313233344142434461626364")) try: - a = binascii.unhexlify(b"0") # odd buffer length + a = unhexlify(b"0") # odd buffer length except ValueError: print("ValueError") try: - a = binascii.unhexlify(b"gg") # digit not hex + a = unhexlify(b"gg") # digit not hex except ValueError: print("ValueError") diff --git a/tests/extmod/framebuf_blit.py b/tests/extmod/framebuf_blit.py new file mode 100644 index 0000000000000..b1d98b330a838 --- /dev/null +++ b/tests/extmod/framebuf_blit.py @@ -0,0 +1,68 @@ +# Test FrameBuffer.blit method. + +try: + import framebuf +except ImportError: + print("SKIP") + raise SystemExit + + +def printbuf(): + print("--8<--") + for y in range(h): + for x in range(w): + print("%02x" % buf[(x + y * w)], end="") + print() + print("-->8--") + + +w = 5 +h = 4 +buf = bytearray(w * h) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) + +fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8) +fbuf2.fill(0xFF) + +# Blit another FrameBuffer, at various locations. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.blit(fbuf2, x, y) + printbuf() + +# Blit a bytes object. +fbuf.fill(0) +image = (b"\x10\x11\x12\x13", 2, 2, framebuf.GS8) +fbuf.blit(image, 1, 1) +printbuf() + +# Blit a bytes object that has a stride. +fbuf.fill(0) +image = (b"\x20\x21\xff\x22\x23\xff", 2, 2, framebuf.GS8, 3) +fbuf.blit(image, 1, 1) +printbuf() + +# Blit a bytes object with a bytes palette. +fbuf.fill(0) +image = (b"\x00\x01\x01\x00", 2, 2, framebuf.GS8) +palette = (b"\xa1\xa2", 2, 1, framebuf.GS8) +fbuf.blit(image, 1, 1, -1, palette) +printbuf() + +# Not enough elements in the tuple. +try: + fbuf.blit((0, 0, 0), 0, 0) +except ValueError: + print("ValueError") + +# Too many elements in the tuple. +try: + fbuf.blit((0, 0, 0, 0, 0, 0), 0, 0) +except ValueError: + print("ValueError") + +# Bytes too small. +try: + fbuf.blit((b"", 1, 1, framebuf.GS8), 0, 0) +except ValueError: + print("ValueError") diff --git a/tests/extmod/framebuf_blit.py.exp b/tests/extmod/framebuf_blit.py.exp new file mode 100644 index 0000000000000..e340f1990c783 --- /dev/null +++ b/tests/extmod/framebuf_blit.py.exp @@ -0,0 +1,45 @@ +--8<-- +ff00000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +ffff000000 +ffff000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +00ffff0000 +00ffff0000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +00000000ff +-->8-- +--8<-- +0000000000 +0010110000 +0012130000 +0000000000 +-->8-- +--8<-- +0000000000 +0020210000 +0022230000 +0000000000 +-->8-- +--8<-- +0000000000 +00a1a20000 +00a2a10000 +0000000000 +-->8-- +ValueError +ValueError +ValueError diff --git a/tests/extmod/hashlib_md5.py b/tests/extmod/hashlib_md5.py index 5f925fc97d02e..ebbe9155e0493 100644 --- a/tests/extmod/hashlib_md5.py +++ b/tests/extmod/hashlib_md5.py @@ -1,8 +1,7 @@ try: import hashlib except ImportError: - # This is neither uPy, nor cPy, so must be uPy with - # hashlib module disabled. + # MicroPython with hashlib module disabled. print("SKIP") raise SystemExit diff --git a/tests/extmod/hashlib_sha1.py b/tests/extmod/hashlib_sha1.py index af23033a591f2..46ffb73fcbe01 100644 --- a/tests/extmod/hashlib_sha1.py +++ b/tests/extmod/hashlib_sha1.py @@ -1,8 +1,7 @@ try: import hashlib except ImportError: - # This is neither uPy, nor cPy, so must be uPy with - # hashlib module disabled. + # MicroPython with hashlib module disabled. print("SKIP") raise SystemExit diff --git a/tests/extmod/hashlib_sha256.py b/tests/extmod/hashlib_sha256.py index 95cd301d160d0..209fcb3987792 100644 --- a/tests/extmod/hashlib_sha256.py +++ b/tests/extmod/hashlib_sha256.py @@ -1,8 +1,7 @@ try: import hashlib except ImportError: - # This is neither uPy, nor cPy, so must be uPy with - # hashlib module disabled. + # MicroPython with hashlib module disabled. print("SKIP") raise SystemExit diff --git a/tests/extmod/json_dump.py b/tests/extmod/json_dump.py index 897d33cc81253..0beb4f5f85698 100644 --- a/tests/extmod/json_dump.py +++ b/tests/extmod/json_dump.py @@ -16,11 +16,11 @@ # dump to a small-int not allowed try: json.dump(123, 1) -except (AttributeError, OSError): # CPython and uPy have different errors +except (AttributeError, OSError): # CPython and MicroPython have different errors print("Exception") # dump to an object not allowed try: json.dump(123, {}) -except (AttributeError, OSError): # CPython and uPy have different errors +except (AttributeError, OSError): # CPython and MicroPython have different errors print("Exception") diff --git a/tests/extmod/json_dump_iobase.py b/tests/extmod/json_dump_iobase.py index 94d317b87968f..81105e36dccec 100644 --- a/tests/extmod/json_dump_iobase.py +++ b/tests/extmod/json_dump_iobase.py @@ -18,7 +18,7 @@ def __init__(self): def write(self, buf): if type(buf) == bytearray: - # uPy passes a bytearray, CPython passes a str + # MicroPython passes a bytearray, CPython passes a str buf = str(buf, "ascii") self.buf += buf return len(buf) diff --git a/tests/extmod/json_dump_separators.py b/tests/extmod/json_dump_separators.py index 4f8e56dceb536..ce39294820fa9 100644 --- a/tests/extmod/json_dump_separators.py +++ b/tests/extmod/json_dump_separators.py @@ -25,20 +25,20 @@ # dump to a small-int not allowed try: json.dump(123, 1, separators=sep) - except (AttributeError, OSError): # CPython and uPy have different errors + except (AttributeError, OSError): # CPython and MicroPython have different errors print("Exception") # dump to an object not allowed try: json.dump(123, {}, separators=sep) - except (AttributeError, OSError): # CPython and uPy have different errors + except (AttributeError, OSError): # CPython and MicroPython have different errors print("Exception") try: s = StringIO() json.dump(False, s, separators={"a": 1}) -except (TypeError, ValueError): # CPython and uPy have different errors +except (TypeError, ValueError): # CPython and MicroPython have different errors print("Exception") # invalid separator types diff --git a/tests/extmod/json_dumps_extra.py b/tests/extmod/json_dumps_extra.py index a410b0ee0ef63..70efc86645115 100644 --- a/tests/extmod/json_dumps_extra.py +++ b/tests/extmod/json_dumps_extra.py @@ -1,4 +1,4 @@ -# test uPy json behaviour that's not valid in CPy +# test MicroPython json behaviour that's not valid in CPy # CIRCUITPY-CHANGE: This behavior matches CPython print("SKIP") raise SystemExit diff --git a/tests/extmod/json_dumps_separators.py b/tests/extmod/json_dumps_separators.py index a3a9ec308f09d..0a95f489a08ec 100644 --- a/tests/extmod/json_dumps_separators.py +++ b/tests/extmod/json_dumps_separators.py @@ -39,7 +39,7 @@ try: json.dumps(False, separators={"a": 1}) -except (TypeError, ValueError): # CPython and uPy have different errors +except (TypeError, ValueError): # CPython and MicroPython have different errors print("Exception") # invalid separator types diff --git a/tests/extmod/json_loads.py b/tests/extmod/json_loads.py index f9073c121e2ef..092402d715d69 100644 --- a/tests/extmod/json_loads.py +++ b/tests/extmod/json_loads.py @@ -71,3 +71,27 @@ def my_print(o): my_print(json.loads("[null] a")) except ValueError: print("ValueError") + +# incomplete object declaration +try: + my_print(json.loads('{"a":0,')) +except ValueError: + print("ValueError") + +# incomplete nested array declaration +try: + my_print(json.loads('{"a":0, [')) +except ValueError: + print("ValueError") + +# incomplete array declaration +try: + my_print(json.loads("[0,")) +except ValueError: + print("ValueError") + +# incomplete nested object declaration +try: + my_print(json.loads('[0, {"a":0, ')) +except ValueError: + print("ValueError") diff --git a/tests/extmod/json_loads_bytes.py.exp b/tests/extmod/json_loads_bytes.py.exp deleted file mode 100644 index c2735a99052d2..0000000000000 --- a/tests/extmod/json_loads_bytes.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -[1, 2] -[None] diff --git a/tests/extmod/json_loads_int_64.py b/tests/extmod/json_loads_int_64.py new file mode 100644 index 0000000000000..f6236f1904a87 --- /dev/null +++ b/tests/extmod/json_loads_int_64.py @@ -0,0 +1,16 @@ +# Parse 64-bit integers from JSON payloads. +# +# This also exercises parsing integers from strings +# where the value may not be null terminated (last line) +try: + import json +except ImportError: + print("SKIP") + raise SystemExit + + +print(json.loads("9111222333444555666")) +print(json.loads("-9111222333444555666")) +print(json.loads("9111222333444555666")) +print(json.loads("-9111222333444555666")) +print(json.loads('["9111222333444555666777",9111222333444555666]')) diff --git a/tests/extmod/machine_hard_timer.py b/tests/extmod/machine_hard_timer.py new file mode 100644 index 0000000000000..8fe42ea850842 --- /dev/null +++ b/tests/extmod/machine_hard_timer.py @@ -0,0 +1,45 @@ +import sys + +try: + from machine import Timer + from time import sleep_ms +except: + print("SKIP") + raise SystemExit + +if sys.platform == "esp8266": + timer = Timer(0) +else: + # Hardware timers are not implemented. + print("SKIP") + raise SystemExit + +# Test both hard and soft IRQ handlers and both one-shot and periodic +# timers. We adjust period in tests/extmod/machine_soft_timer.py, so try +# adjusting freq here instead. The heap should be locked in hard callbacks +# and unlocked in soft callbacks. + + +def callback(t): + print("callback", mode[1], kind[1], freq, end=" ") + try: + allocate = bytearray(1) + print("unlocked") + except MemoryError: + print("locked") + + +modes = [(Timer.ONE_SHOT, "one-shot"), (Timer.PERIODIC, "periodic")] +kinds = [(False, "soft"), (True, "hard")] + +for mode in modes: + for kind in kinds: + for freq in 50, 25: + timer.init( + mode=mode[0], + freq=freq, + hard=kind[0], + callback=callback, + ) + sleep_ms(90) + timer.deinit() diff --git a/tests/extmod/machine_hard_timer.py.exp b/tests/extmod/machine_hard_timer.py.exp new file mode 100644 index 0000000000000..26cdc644fdd08 --- /dev/null +++ b/tests/extmod/machine_hard_timer.py.exp @@ -0,0 +1,16 @@ +callback one-shot soft 50 unlocked +callback one-shot soft 25 unlocked +callback one-shot hard 50 locked +callback one-shot hard 25 locked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 25 unlocked +callback periodic soft 25 unlocked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 25 locked +callback periodic hard 25 locked diff --git a/tests/extmod/machine_timer.py b/tests/extmod/machine_timer.py new file mode 100644 index 0000000000000..ef97ea4e94955 --- /dev/null +++ b/tests/extmod/machine_timer.py @@ -0,0 +1,48 @@ +import sys + +try: + from machine import Timer + from time import sleep_ms +except: + print("SKIP") + raise SystemExit + +if sys.platform in ("esp32", "esp8266", "nrf"): + # Software timers aren't implemented on the esp32 and esp8266 ports. + # The nrf port doesn't support selection of hard and soft callbacks, + # and only allows Timer(period=N), not Timer(freq=N). + print("SKIP") + raise SystemExit +else: + timer_id = -1 + +# Test both hard and soft IRQ handlers and both one-shot and periodic +# timers. We adjust period in tests/extmod/machine_soft_timer.py, so try +# adjusting freq here instead. The heap should be locked in hard callbacks +# and unlocked in soft callbacks. + + +def callback(t): + print("callback", mode[1], kind[1], freq, end=" ") + try: + allocate = bytearray(1) + print("unlocked") + except MemoryError: + print("locked") + + +modes = [(Timer.ONE_SHOT, "one-shot"), (Timer.PERIODIC, "periodic")] +kinds = [(False, "soft"), (True, "hard")] + +for mode in modes: + for kind in kinds: + for freq in 50, 25: + timer = Timer( + timer_id, + mode=mode[0], + freq=freq, + hard=kind[0], + callback=callback, + ) + sleep_ms(90) + timer.deinit() diff --git a/tests/extmod/machine_timer.py.exp b/tests/extmod/machine_timer.py.exp new file mode 100644 index 0000000000000..26cdc644fdd08 --- /dev/null +++ b/tests/extmod/machine_timer.py.exp @@ -0,0 +1,16 @@ +callback one-shot soft 50 unlocked +callback one-shot soft 25 unlocked +callback one-shot hard 50 locked +callback one-shot hard 25 locked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 25 unlocked +callback periodic soft 25 unlocked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 25 locked +callback periodic hard 25 locked diff --git a/tests/extmod/platform_basic.py b/tests/extmod/platform_basic.py new file mode 100644 index 0000000000000..eb6f2be13c17a --- /dev/null +++ b/tests/extmod/platform_basic.py @@ -0,0 +1,8 @@ +try: + import platform +except ImportError: + print("SKIP") + raise SystemExit + +print(type(platform.python_compiler())) +print(type(platform.libc_ver())) diff --git a/tests/extmod/random_extra_float.py b/tests/extmod/random_extra_float.py index 3b37ed8dcef2f..03973c583492d 100644 --- a/tests/extmod/random_extra_float.py +++ b/tests/extmod/random_extra_float.py @@ -1,12 +1,8 @@ try: import random -except ImportError: - print("SKIP") - raise SystemExit -try: - random.randint -except AttributeError: + random.random +except (ImportError, AttributeError): print("SKIP") raise SystemExit diff --git a/tests/extmod/re_error.py b/tests/extmod/re_error.py index f61d0913289e1..bd678c9d25156 100644 --- a/tests/extmod/re_error.py +++ b/tests/extmod/re_error.py @@ -11,7 +11,7 @@ def test_re(r): try: re.compile(r) print("OK") - except: # uPy and CPy use different errors, so just ignore the type + except: # MPy and CPy use different errors, so just ignore the type print("Error") diff --git a/tests/extmod/re_start_end_pos.py b/tests/extmod/re_start_end_pos.py new file mode 100644 index 0000000000000..bd16584374b89 --- /dev/null +++ b/tests/extmod/re_start_end_pos.py @@ -0,0 +1,78 @@ +# test start and end pos specification + +try: + import re +except ImportError: + print("SKIP") + raise SystemExit + + +def print_groups(match): + print("----") + try: + if match is not None: + i = 0 + while True: + print(match.group(i)) + i += 1 + except IndexError: + pass + + +p = re.compile(r"o") +m = p.match("dog") +print_groups(m) + +m = p.match("dog", 1) +print_groups(m) + +m = p.match("dog", 2) +print_groups(m) + +# No match past end of input +m = p.match("dog", 5) +print_groups(m) + +m = p.match("dog", 0, 1) +print_groups(m) + +# Caret only matches the actual beginning +p = re.compile(r"^o") +m = p.match("dog", 1) +print_groups(m) + +# End at beginning means searching empty string +p = re.compile(r"o") +m = p.match("dog", 1, 1) +print_groups(m) + +# End before the beginning doesn't match anything +m = p.match("dog", 2, 1) +print_groups(m) + +# Negative starting values don't crash +m = p.search("dog", -2) +print_groups(m) + +m = p.search("dog", -2, -5) +print_groups(m) + +# Search also works +print("--search") + +p = re.compile(r"o") +m = p.search("dog") +print_groups(m) + +m = p.search("dog", 1) +print_groups(m) + +m = p.search("dog", 2) +print_groups(m) + +# Negative starting values don't crash +m = p.search("dog", -2) +print_groups(m) + +m = p.search("dog", -2, -5) +print_groups(m) diff --git a/tests/extmod/re_sub.py b/tests/extmod/re_sub.py index 3959949724d98..bb9aa111287e6 100644 --- a/tests/extmod/re_sub.py +++ b/tests/extmod/re_sub.py @@ -62,7 +62,7 @@ def A(): except: print("invalid group") -# invalid group with very large number (to test overflow in uPy) +# invalid group with very large number (to test overflow in MicroPython) try: re.sub("(a)", "b\\199999999999999999999999999999999999999", "a") except: diff --git a/tests/extmod/re_sub_unmatched.py.exp b/tests/extmod/re_sub_unmatched.py.exp deleted file mode 100644 index 1e5f0fda0554d..0000000000000 --- a/tests/extmod/re_sub_unmatched.py.exp +++ /dev/null @@ -1 +0,0 @@ -1-a2 diff --git a/tests/extmod/socket_badconstructor.py b/tests/extmod/socket_badconstructor.py new file mode 100644 index 0000000000000..4a9d2668c7f1a --- /dev/null +++ b/tests/extmod/socket_badconstructor.py @@ -0,0 +1,22 @@ +# Test passing in bad values to socket.socket constructor. + +try: + import socket +except: + print("SKIP") + raise SystemExit + +try: + s = socket.socket(None) +except TypeError: + print("TypeError") + +try: + s = socket.socket(socket.AF_INET, None) +except TypeError: + print("TypeError") + +try: + s = socket.socket(socket.AF_INET, socket.SOCK_RAW, None) +except TypeError: + print("TypeError") diff --git a/tests/extmod/socket_fileno.py b/tests/extmod/socket_fileno.py new file mode 100644 index 0000000000000..da15825e3d56b --- /dev/null +++ b/tests/extmod/socket_fileno.py @@ -0,0 +1,17 @@ +# Test socket.fileno() functionality + +try: + import socket +except ImportError: + print("SKIP") + raise SystemExit + +if not hasattr(socket.socket, "fileno"): + print("SKIP") + raise SystemExit + +s = socket.socket() +print(s.fileno() >= 0) + +s.close() +print(s.fileno()) # should print -1 diff --git a/tests/extmod/time_mktime.py b/tests/extmod/time_mktime.py new file mode 100644 index 0000000000000..7fc643dc3cb8c --- /dev/null +++ b/tests/extmod/time_mktime.py @@ -0,0 +1,120 @@ +# test conversion from date tuple to timestamp and back + +try: + import time + + time.localtime +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +# Range of date expected to work on all MicroPython platforms +MIN_YEAR = 1970 +MAX_YEAR = 2099 +# CPython properly supported date range: +# - on Windows: year 1970 to 3000+ +# - on Unix: year 1583 to 3000+ + +# Start test from Jan 1, 2001 13:00 (Feb 2000 might already be broken) +SAFE_DATE = (2001, 1, 1, 13, 0, 0, 0, 0, -1) + + +# mktime function that checks that the result is reversible +def safe_mktime(date_tuple): + try: + res = time.mktime(date_tuple) + chk = time.localtime(res) + except OverflowError: + print("safe_mktime:", date_tuple, "overflow error") + return None + if chk[0:5] != date_tuple[0:5]: + print("safe_mktime:", date_tuple[0:5], " -> ", res, " -> ", chk[0:5]) + return None + return res + + +# localtime function that checks that the result is reversible +def safe_localtime(timestamp): + try: + res = time.localtime(timestamp) + chk = time.mktime(res) + except OverflowError: + print("safe_localtime:", timestamp, "overflow error") + return None + if chk != timestamp: + print("safe_localtime:", timestamp, " -> ", res, " -> ", chk) + return None + return res + + +# look for smallest valid timestamps by iterating backwards on tuple +def test_bwd(date_tuple): + curr_stamp = safe_mktime(date_tuple) + year = date_tuple[0] + month = date_tuple[1] - 1 + if month < 1: + year -= 1 + month = 12 + while year >= MIN_YEAR: + while month >= 1: + next_tuple = (year, month) + date_tuple[2:] + next_stamp = safe_mktime(next_tuple) + # at this stage, only test consistency and monotonicity + if next_stamp is None or next_stamp >= curr_stamp: + return date_tuple + date_tuple = next_tuple + curr_stamp = next_stamp + month -= 1 + year -= 1 + month = 12 + return date_tuple + + +# test day-by-day to ensure that every date is properly converted +def test_fwd(start_date): + DAYS_PER_MONTH = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + curr_stamp = safe_mktime(start_date) + curr_date = safe_localtime(curr_stamp) + while curr_date[0] <= MAX_YEAR: + if curr_date[2] < 15: + skip_days = 13 + else: + skip_days = 1 + next_stamp = curr_stamp + skip_days * 86400 + next_date = safe_localtime(next_stamp) + if next_date is None: + return curr_date + if next_date[2] != curr_date[2] + skip_days: + # next month + if next_date[2] != 1: + print("wrong day of month:", next_date) + return curr_date + # check the number of days in previous month + month_days = DAYS_PER_MONTH[curr_date[1]] + if month_days == 28 and curr_date[0] % 4 == 0: + if curr_date[0] % 100 != 0 or curr_date[0] % 400 == 0: + month_days += 1 + if curr_date[2] != month_days: + print("wrong day count in prev month:", curr_date[2], "vs", month_days) + return curr_date + if next_date[1] != curr_date[1] + 1: + # next year + if curr_date[1] != 12: + print("wrong month count in prev year:", curr_date[1]) + return curr_date + if next_date[1] != 1: + print("wrong month:", next_date) + return curr_date + if next_date[0] != curr_date[0] + 1: + print("wrong year:", next_date) + return curr_date + curr_stamp = next_stamp + curr_date = next_date + return curr_date + + +small_date = test_bwd(SAFE_DATE) +large_date = test_fwd(small_date) +print("tested from", small_date[0:3], "to", large_date[0:3]) +print(small_date[0:3], "wday is", small_date[6]) +print(large_date[0:3], "wday is", large_date[6]) diff --git a/tests/extmod/time_res.py b/tests/extmod/time_res.py index 548bef1f1747c..ef20050b914d7 100644 --- a/tests/extmod/time_res.py +++ b/tests/extmod/time_res.py @@ -37,9 +37,12 @@ def test(): time.sleep_ms(100) for func_name, _ in EXPECTED_MAP: try: - time_func = getattr(time, func_name, None) or globals()[func_name] + if func_name.endswith("_time"): + time_func = globals()[func_name] + else: + time_func = getattr(time, func_name) now = time_func() # may raise AttributeError - except (KeyError, AttributeError): + except AttributeError: continue try: results_map[func_name].add(now) diff --git a/tests/extmod/tls_dtls.py b/tests/extmod/tls_dtls.py index b2d716769d3f7..753ab2fee4f40 100644 --- a/tests/extmod/tls_dtls.py +++ b/tests/extmod/tls_dtls.py @@ -34,9 +34,19 @@ def ioctl(self, req, arg): # Wrap the DTLS Server dtls_server_ctx = SSLContext(PROTOCOL_DTLS_SERVER) dtls_server_ctx.verify_mode = CERT_NONE -dtls_server = dtls_server_ctx.wrap_socket(server_socket, do_handshake_on_connect=False) +dtls_server = dtls_server_ctx.wrap_socket( + server_socket, do_handshake_on_connect=False, client_id=b"dummy_client_id" +) print("Wrapped DTLS Server") +# wrap DTLS server with invalid client_id +try: + dtls_server = dtls_server_ctx.wrap_socket( + server_socket, do_handshake_on_connect=False, client_id=4 + ) +except OSError: + print("Failed to wrap DTLS Server with invalid client_id") + # Wrap the DTLS Client dtls_client_ctx = SSLContext(PROTOCOL_DTLS_CLIENT) dtls_client_ctx.verify_mode = CERT_NONE diff --git a/tests/extmod/tls_dtls.py.exp b/tests/extmod/tls_dtls.py.exp index 78d72bff18816..dbd005d0edfb8 100644 --- a/tests/extmod/tls_dtls.py.exp +++ b/tests/extmod/tls_dtls.py.exp @@ -1,3 +1,4 @@ Wrapped DTLS Server +Failed to wrap DTLS Server with invalid client_id Wrapped DTLS Client OK diff --git a/tests/extmod/ssl_noleak.py b/tests/extmod/tls_noleak.py similarity index 100% rename from tests/extmod/ssl_noleak.py rename to tests/extmod/tls_noleak.py diff --git a/tests/extmod/tls_threads.py b/tests/extmod/tls_threads.py new file mode 100644 index 0000000000000..1e0c3d23d2f38 --- /dev/null +++ b/tests/extmod/tls_threads.py @@ -0,0 +1,58 @@ +# Ensure that SSL sockets can be allocated from multiple +# threads without thread safety issues + +try: + import _thread + import io + import tls + import time +except ImportError: + print("SKIP") + raise SystemExit + +import unittest + + +class TestSocket(io.IOBase): + def write(self, buf): + return len(buf) + + def readinto(self, buf): + return 0 + + def ioctl(self, cmd, arg): + return 0 + + def setblocking(self, value): + pass + + +ITERS = 256 + + +class TLSThreads(unittest.TestCase): + def test_sslsocket_threaded(self): + self.done = False + # only run in two threads: too much RAM demand otherwise, and rp2 only + # supports two anyhow + _thread.start_new_thread(self._alloc_many_sockets, (True,)) + self._alloc_many_sockets(False) + while not self.done: + time.sleep(0.1) + print("done") + + def _alloc_many_sockets(self, set_done_flag): + print("start", _thread.get_ident()) + ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT) + ctx.verify_mode = tls.CERT_NONE + for n in range(ITERS): + s = TestSocket() + s = ctx.wrap_socket(s, do_handshake_on_connect=False) + s.close() # Free associated resources now from thread, not in a GC pass + print("done", _thread.get_ident()) + if set_done_flag: + self.done = True + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/extmod/uctypes_addressof.py b/tests/extmod/uctypes_addressof.py index c83089d0f72af..213fcc05eee2b 100644 --- a/tests/extmod/uctypes_addressof.py +++ b/tests/extmod/uctypes_addressof.py @@ -12,5 +12,8 @@ print(uctypes.addressof(uctypes.bytearray_at(1 << i, 8))) # Test address that is bigger than the greatest small-int but still within the address range. -large_addr = maxsize + 1 -print(uctypes.addressof(uctypes.bytearray_at(large_addr, 8)) == large_addr) +try: + large_addr = maxsize + 1 + print(uctypes.addressof(uctypes.bytearray_at(large_addr, 8)) == large_addr) +except OverflowError: + print(True) # systems with 64-bit bigints will overflow on the above operation diff --git a/tests/extmod/uctypes_array_load_store.py b/tests/extmod/uctypes_array_load_store.py index 9de079998566f..695352da57983 100644 --- a/tests/extmod/uctypes_array_load_store.py +++ b/tests/extmod/uctypes_array_load_store.py @@ -6,6 +6,13 @@ print("SKIP") raise SystemExit +# 'int' needs to be able to represent UINT64 for this test +try: + int("FF" * 8, 16) +except OverflowError: + print("SKIP") + raise SystemExit + N = 5 for endian in ("NATIVE", "LITTLE_ENDIAN", "BIG_ENDIAN"): diff --git a/tests/extmod/vfs_blockdev_invalid.py b/tests/extmod/vfs_blockdev_invalid.py index 4d00f4b00273a..29d6bd6b2f9f7 100644 --- a/tests/extmod/vfs_blockdev_invalid.py +++ b/tests/extmod/vfs_blockdev_invalid.py @@ -70,8 +70,8 @@ def test(vfs_class): try: with fs.open("test", "r") as f: print("opened") - except OSError as e: - print("OSError", e) + except Exception as e: + print(type(e), e) # This variant should succeed on open, may fail on read # unless the filesystem cached the contents already @@ -81,8 +81,8 @@ def test(vfs_class): bdev.read_res = res print("read 1", f.read(1)) print("read rest", f.read()) - except OSError as e: - print("OSError", e) + except Exception as e: + print(type(e), e) test(vfs.VfsLfs2) diff --git a/tests/extmod/vfs_blockdev_invalid.py.exp b/tests/extmod/vfs_blockdev_invalid.py.exp index 13695e0d88916..0ea0353501de3 100644 --- a/tests/extmod/vfs_blockdev_invalid.py.exp +++ b/tests/extmod/vfs_blockdev_invalid.py.exp @@ -2,27 +2,27 @@ opened read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 5] EIO + [Errno 5] EIO read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 22] EINVAL + [Errno 22] EINVAL read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 22] EINVAL + [Errno 22] EINVAL read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 22] EINVAL + can't convert str to int read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa opened read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO + [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 diff --git a/tests/extmod/vfs_fat_ilistdir_del.py b/tests/extmod/vfs_fat_ilistdir_del.py index a6e24ec92f3b5..964e6b12868eb 100644 --- a/tests/extmod/vfs_fat_ilistdir_del.py +++ b/tests/extmod/vfs_fat_ilistdir_del.py @@ -1,8 +1,7 @@ # Test ilistdir __del__ for VfsFat using a RAM device. -import gc try: - import os, vfs + import gc, os, vfs vfs.VfsFat except (ImportError, AttributeError): diff --git a/tests/extmod/vfs_lfs.py b/tests/extmod/vfs_lfs.py index 3ad57fd9c38f1..40d58e9c9f743 100644 --- a/tests/extmod/vfs_lfs.py +++ b/tests/extmod/vfs_lfs.py @@ -136,7 +136,7 @@ def test(bdev, vfs_class): print(fs.getcwd()) fs.chdir("../testdir") print(fs.getcwd()) - fs.chdir("../..") + fs.chdir("..") print(fs.getcwd()) fs.chdir(".//testdir") print(fs.getcwd()) diff --git a/tests/extmod/vfs_lfs_error.py b/tests/extmod/vfs_lfs_error.py index 2ac7629bfa8fc..73cdf3437330b 100644 --- a/tests/extmod/vfs_lfs_error.py +++ b/tests/extmod/vfs_lfs_error.py @@ -1,7 +1,7 @@ # Test for VfsLittle using a RAM device, testing error handling try: - import vfs + import errno, vfs vfs.VfsLfs1 vfs.VfsLfs2 @@ -41,14 +41,14 @@ def test(bdev, vfs_class): # mkfs with too-small block device try: vfs_class.mkfs(RAMBlockDevice(1)) - except OSError: - print("mkfs OSError") + except OSError as er: + print("mkfs OSError", er.errno > 0) # mount with invalid filesystem try: vfs_class(bdev) - except OSError: - print("mount OSError") + except OSError as er: + print("mount OSError", er.errno > 0) # set up for following tests vfs_class.mkfs(bdev) @@ -60,60 +60,60 @@ def test(bdev, vfs_class): # ilistdir try: fs.ilistdir("noexist") - except OSError: - print("ilistdir OSError") + except OSError as er: + print("ilistdir OSError", er) # remove try: fs.remove("noexist") - except OSError: - print("remove OSError") + except OSError as er: + print("remove OSError", er) # rmdir try: fs.rmdir("noexist") - except OSError: - print("rmdir OSError") + except OSError as er: + print("rmdir OSError", er) # rename try: fs.rename("noexist", "somethingelse") - except OSError: - print("rename OSError") + except OSError as er: + print("rename OSError", er) # mkdir try: fs.mkdir("testdir") - except OSError: - print("mkdir OSError") + except OSError as er: + print("mkdir OSError", er) # chdir to nonexistent try: fs.chdir("noexist") - except OSError: - print("chdir OSError") + except OSError as er: + print("chdir OSError", er) print(fs.getcwd()) # check still at root # chdir to file try: fs.chdir("testfile") - except OSError: - print("chdir OSError") + except OSError as er: + print("chdir OSError", er) print(fs.getcwd()) # check still at root # stat try: fs.stat("noexist") - except OSError: - print("stat OSError") + except OSError as er: + print("stat OSError", er) # error during seek with fs.open("testfile", "r") as f: f.seek(1 << 30) # SEEK_SET try: f.seek(1 << 30, 1) # SEEK_CUR - except OSError: - print("seek OSError") + except OSError as er: + print("seek OSError", er) bdev = RAMBlockDevice(30) diff --git a/tests/extmod/vfs_lfs_error.py.exp b/tests/extmod/vfs_lfs_error.py.exp index f4327f6962ec3..440607ed84b22 100644 --- a/tests/extmod/vfs_lfs_error.py.exp +++ b/tests/extmod/vfs_lfs_error.py.exp @@ -1,28 +1,28 @@ test -mkfs OSError -mount OSError -ilistdir OSError -remove OSError -rmdir OSError -rename OSError -mkdir OSError -chdir OSError +mkfs OSError True +mount OSError True +ilistdir OSError [Errno 2] ENOENT +remove OSError [Errno 2] ENOENT +rmdir OSError [Errno 2] ENOENT +rename OSError [Errno 2] ENOENT +mkdir OSError [Errno 17] EEXIST +chdir OSError [Errno 2] ENOENT / -chdir OSError +chdir OSError [Errno 2] ENOENT / -stat OSError -seek OSError +stat OSError [Errno 2] ENOENT +seek OSError [Errno 22] EINVAL test -mkfs OSError -mount OSError -ilistdir OSError -remove OSError -rmdir OSError -rename OSError -mkdir OSError -chdir OSError +mkfs OSError True +mount OSError True +ilistdir OSError [Errno 2] ENOENT +remove OSError [Errno 2] ENOENT +rmdir OSError [Errno 2] ENOENT +rename OSError [Errno 2] ENOENT +mkdir OSError [Errno 17] EEXIST +chdir OSError [Errno 2] ENOENT / -chdir OSError +chdir OSError [Errno 2] ENOENT / -stat OSError -seek OSError +stat OSError [Errno 2] ENOENT +seek OSError [Errno 22] EINVAL diff --git a/tests/extmod/vfs_lfs_ilistdir_del.py b/tests/extmod/vfs_lfs_ilistdir_del.py index 7b59bc412d983..828c85a258856 100644 --- a/tests/extmod/vfs_lfs_ilistdir_del.py +++ b/tests/extmod/vfs_lfs_ilistdir_del.py @@ -1,8 +1,7 @@ # Test ilistdir __del__ for VfsLittle using a RAM device. -import gc try: - import vfs + import gc, vfs vfs.VfsLfs2 except (ImportError, AttributeError): @@ -71,5 +70,10 @@ def test(bdev, vfs_class): fs.open("/test", "w").close() -bdev = RAMBlockDevice(30) +try: + bdev = RAMBlockDevice(30) +except MemoryError: + print("SKIP") + raise SystemExit + test(bdev, vfs.VfsLfs2) diff --git a/tests/extmod/vfs_mountinfo.py b/tests/extmod/vfs_mountinfo.py index f674e80763409..b31dc60ce76d7 100644 --- a/tests/extmod/vfs_mountinfo.py +++ b/tests/extmod/vfs_mountinfo.py @@ -5,7 +5,6 @@ except ImportError: print("SKIP") raise SystemExit -import errno class Filesystem: diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py index d060c0b9c84f3..b3ca2753ba9cf 100644 --- a/tests/extmod/vfs_posix.py +++ b/tests/extmod/vfs_posix.py @@ -29,7 +29,21 @@ print(type(os.stat("/"))) # listdir and ilistdir -print(type(os.listdir("/"))) +target = "/" +try: + import platform + + # On Android non-root users are permitted full filesystem access only to + # selected directories. To let this test pass on bionic, the internal + # user-accessible storage area root is enumerated instead of the + # filesystem root. "/storage/emulated/0" should be there on pretty much + # any recent-ish device; querying the proper location requires a JNI + # round-trip, not really worth it. + if platform.platform().startswith("Android-"): + target = "/storage/emulated/0" +except ImportError: + pass +print(type(os.listdir(target))) # mkdir os.mkdir(temp_dir) diff --git a/tests/extmod/vfs_posix_ilistdir_del.py b/tests/extmod/vfs_posix_ilistdir_del.py index 78d7c854c543c..8b5984cd81c05 100644 --- a/tests/extmod/vfs_posix_ilistdir_del.py +++ b/tests/extmod/vfs_posix_ilistdir_del.py @@ -1,8 +1,7 @@ # Test ilistdir __del__ for VfsPosix. -import gc try: - import os, vfs + import gc, os, vfs vfs.VfsPosix except (ImportError, AttributeError): diff --git a/tests/extmod/vfs_posix_paths.py b/tests/extmod/vfs_posix_paths.py index b4fedc6716f01..c06318748a327 100644 --- a/tests/extmod/vfs_posix_paths.py +++ b/tests/extmod/vfs_posix_paths.py @@ -31,7 +31,7 @@ fs.mkdir("subdir/one") print('listdir("/"):', sorted(i[0] for i in fs.ilistdir("/"))) print('listdir("."):', sorted(i[0] for i in fs.ilistdir("."))) -print('getcwd() in {"", "/"}:', fs.getcwd() in {"", "/"}) +print('getcwd() in ("", "/"):', fs.getcwd() in ("", "/")) print('chdir("subdir"):', fs.chdir("subdir")) print("getcwd():", fs.getcwd()) print('mkdir("two"):', fs.mkdir("two")) diff --git a/tests/extmod/vfs_posix_paths.py.exp b/tests/extmod/vfs_posix_paths.py.exp index ecc13222aaaeb..d6a0960efd249 100644 --- a/tests/extmod/vfs_posix_paths.py.exp +++ b/tests/extmod/vfs_posix_paths.py.exp @@ -1,6 +1,6 @@ listdir("/"): ['subdir'] listdir("."): ['subdir'] -getcwd() in {"", "/"}: True +getcwd() in ("", "/"): True chdir("subdir"): None getcwd(): /subdir mkdir("two"): None diff --git a/tests/extmod/vfs_posix_readonly.py b/tests/extmod/vfs_posix_readonly.py new file mode 100644 index 0000000000000..e7821381006fd --- /dev/null +++ b/tests/extmod/vfs_posix_readonly.py @@ -0,0 +1,99 @@ +# Test for VfsPosix + +try: + import gc, os, vfs, errno + + vfs.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +# We need a directory for testing that doesn't already exist. +# Skip the test if it does exist. +temp_dir = "vfs_posix_readonly_test_dir" +try: + os.stat(temp_dir) + raise SystemExit("Target directory {} exists".format(temp_dir)) +except OSError: + pass + +# mkdir (skip test if whole filesystem is readonly) +try: + os.mkdir(temp_dir) +except OSError as e: + if e.errno == errno.EROFS: + print("SKIP") + raise SystemExit + +fs_factory = lambda: vfs.VfsPosix(temp_dir) + +# mount +fs = fs_factory() +vfs.mount(fs, "/vfs") + +with open("/vfs/file", "w") as f: + f.write("content") + +# test reading works +with open("/vfs/file") as f: + print("file:", f.read()) + +os.mkdir("/vfs/emptydir") + +# umount +vfs.umount("/vfs") + +# mount read-only +fs = fs_factory() +vfs.mount(fs, "/vfs", readonly=True) + +# test reading works +with open("/vfs/file") as f: + print("file 2:", f.read()) + +# test writing fails +try: + with open("/vfs/test_write", "w"): + pass + print("opened") +except OSError as er: + print(repr(er)) + +# test removing fails +try: + os.unlink("/vfs/file") + print("unlinked") +except OSError as er: + print(repr(er)) + +# test renaming fails +try: + os.rename("/vfs/file2", "/vfs/renamed") + print("renamed") +except OSError as er: + print(repr(er)) + +# test removing directory fails +try: + os.rmdir("/vfs/emptydir") + print("rmdir'd") +except OSError as er: + print(repr(er)) + +# test creating directory fails +try: + os.mkdir("/vfs/emptydir2") + print("mkdir'd") +except OSError as er: + print(repr(er)) + +# umount +vfs.umount("/vfs") + +fs = fs_factory() +vfs.mount(fs, "/vfs") + +os.rmdir("/vfs/emptydir") +os.unlink("/vfs/file") + +os.rmdir(temp_dir) diff --git a/tests/extmod/vfs_posix_readonly.py.exp b/tests/extmod/vfs_posix_readonly.py.exp new file mode 100644 index 0000000000000..40e4316775ff3 --- /dev/null +++ b/tests/extmod/vfs_posix_readonly.py.exp @@ -0,0 +1,7 @@ +file: content +file 2: content +OSError(30,) +OSError(30,) +OSError(30,) +OSError(30,) +OSError(30,) diff --git a/tests/extmod/vfs_rom.py b/tests/extmod/vfs_rom.py index 770b6863b9c43..18ae1f5cf96c8 100644 --- a/tests/extmod/vfs_rom.py +++ b/tests/extmod/vfs_rom.py @@ -25,7 +25,7 @@ # An mpy file with four constant objects: str, bytes, long-int, float. test_mpy = ( # header - b"M\x06\x00\x1f" # mpy file header + b"M\x06\x00\x1e" # mpy file header, -msmall-int-bits=30 b"\x06" # n_qstr b"\x05" # n_obj # qstrs @@ -394,6 +394,7 @@ class TestMounted(TestBase): def setUp(self): self.orig_sys_path = list(sys.path) self.orig_cwd = os.getcwd() + sys.path = [] vfs.mount(vfs.VfsRom(self.romfs), "/test_rom") def tearDown(self): diff --git a/tests/extmod/websocket_toobig.py b/tests/extmod/websocket_toobig.py new file mode 100644 index 0000000000000..f4c5a74bbceac --- /dev/null +++ b/tests/extmod/websocket_toobig.py @@ -0,0 +1,28 @@ +try: + import io + import errno + import websocket +except ImportError: + print("SKIP") + raise SystemExit + +try: + buf = "x" * 65536 +except MemoryError: + print("SKIP") + raise SystemExit + + +# do a websocket write and then return the raw data from the stream +def ws_write(msg, sz): + s = io.BytesIO() + ws = websocket.websocket(s) + ws.write(msg) + s.seek(0) + return s.read(sz) + + +try: + print(ws_write(buf, 1)) +except OSError as e: + print("ioctl: ENOBUFS:", e.errno == errno.ENOBUFS) diff --git a/tests/extmod/websocket_toobig.py.exp b/tests/extmod/websocket_toobig.py.exp new file mode 100644 index 0000000000000..3bbd95282fdfe --- /dev/null +++ b/tests/extmod/websocket_toobig.py.exp @@ -0,0 +1 @@ +ioctl: ENOBUFS: True diff --git a/tests/extmod_hardware/machine_counter.py b/tests/extmod_hardware/machine_counter.py new file mode 100644 index 0000000000000..62ac1fed47ce7 --- /dev/null +++ b/tests/extmod_hardware/machine_counter.py @@ -0,0 +1,90 @@ +# Test machine.Counter implementation +# +# IMPORTANT: This test requires hardware connections: the out_pin and in_pin +# must be wired together. + +try: + from machine import Counter +except ImportError: + print("SKIP") + raise SystemExit + +import sys +from machine import Pin + +if "esp32" in sys.platform: + id = 0 + out_pin = 4 + in_pin = 5 +else: + print("Please add support for this test on this platform.") + raise SystemExit + +import unittest + +out_pin = Pin(out_pin, mode=Pin.OUT) +in_pin = Pin(in_pin, mode=Pin.IN) + + +def toggle(times): + for _ in range(times): + out_pin(1) + out_pin(0) + + +class TestCounter(unittest.TestCase): + def setUp(self): + out_pin(0) + self.counter = Counter(id, in_pin) + + def tearDown(self): + self.counter.deinit() + + def assertCounter(self, value): + self.assertEqual(self.counter.value(), value) + + def test_connections(self): + # Test the hardware connections are correct. If this test fails, all tests will fail. + out_pin(1) + self.assertEqual(1, in_pin()) + out_pin(0) + self.assertEqual(0, in_pin()) + + def test_count_rising(self): + self.assertCounter(0) + toggle(100) + self.assertCounter(100) + out_pin(1) + self.assertEqual(self.counter.value(0), 101) + self.assertCounter(0) # calling value(0) resets + out_pin(0) + self.assertCounter(0) # no rising edge + out_pin(1) + self.assertCounter(1) + + def test_change_directions(self): + self.assertCounter(0) + toggle(100) + self.assertCounter(100) + self.counter.init(in_pin, direction=Counter.DOWN) + self.assertCounter(0) # calling init() zeroes the counter + self.counter.value(100) # need to manually reset the value + self.assertCounter(100) + toggle(25) + self.assertCounter(75) + + def test_count_falling(self): + self.counter.init(in_pin, direction=Counter.UP, edge=Counter.FALLING) + toggle(20) + self.assertCounter(20) + out_pin(1) + self.assertCounter(20) # no falling edge + out_pin(0) + self.assertCounter(21) + self.counter.value(-(2**24)) + toggle(20) + self.assertCounter(-(2**24 - 20)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/extmod_hardware/machine_encoder.py b/tests/extmod_hardware/machine_encoder.py new file mode 100644 index 0000000000000..c218c8bfb646a --- /dev/null +++ b/tests/extmod_hardware/machine_encoder.py @@ -0,0 +1,153 @@ +# Test machine.Encoder implementation +# +# IMPORTANT: This test requires hardware connections: +# - out0_pin and in0_pin must be wired together. +# - out1_pin and in1_pin must be wired together. + +try: + from machine import Encoder +except ImportError: + print("SKIP") + raise SystemExit + +import sys +import unittest +from machine import Pin +from target_wiring import encoder_loopback_id, encoder_loopback_out_pins, encoder_loopback_in_pins + +PRINT = False +PIN_INIT_VALUE = 1 + +id = encoder_loopback_id +out0_pin, out1_pin = encoder_loopback_out_pins +in0_pin, in1_pin = encoder_loopback_in_pins + +out0_pin = Pin(out0_pin, mode=Pin.OUT) +in0_pin = Pin(in0_pin, mode=Pin.IN) +out1_pin = Pin(out1_pin, mode=Pin.OUT) +in1_pin = Pin(in1_pin, mode=Pin.IN) + + +class TestEncoder(unittest.TestCase): + def setUp(self): + out0_pin(PIN_INIT_VALUE) + out1_pin(PIN_INIT_VALUE) + self.enc = Encoder(id, in0_pin, in1_pin, phases=1) + self.enc2 = Encoder(id + 1, in0_pin, in1_pin, phases=2) + self.enc4 = Encoder(id + 2, in0_pin, in1_pin, phases=4) + self.pulses = 0 # track the expected encoder position in software + if PRINT: + print( + "\nout0_pin() out1_pin() enc.value() enc2.value() enc4.value() |", + out0_pin(), + out1_pin(), + "|", + self.enc.value(), + self.enc2.value(), + self.enc4.value(), + ) + + def tearDown(self): + self.enc.deinit() + try: + self.enc2.deinit() + except: + pass + try: + self.enc4.deinit() + except: + pass + + def rotate(self, pulses): + for _ in range(abs(pulses)): + self.pulses += 1 if (pulses > 0) else -1 + if pulses > 0: + if self.pulses % 2: + out0_pin(not out0_pin()) + else: + out1_pin(not out1_pin()) + else: + if self.pulses % 2: + out1_pin(not out1_pin()) + else: + out0_pin(not out0_pin()) + if PRINT: + print( + "out0_pin() out1_pin() enc.value() enc2.value() enc4.value() pulses self.pulses |", + out0_pin(), + out1_pin(), + "|", + self.enc.value(), + self.enc2.value(), + self.enc4.value(), + "|", + pulses, + self.pulses, + ) + + def assertPosition(self, value, value2=None, value4=None): + self.assertEqual(self.enc.value(), value) + if not value2 is None: + self.assertEqual(self.enc2.value(), value2) + if not value4 is None: + self.assertEqual(self.enc4.value(), value4) + pass + + @unittest.skipIf(sys.platform == "mimxrt", "cannot read back the pin") + def test_connections(self): + # Test the hardware connections are correct. If this test fails, all tests will fail. + for ch, outp, inp in ((0, out0_pin, in0_pin), (1, out1_pin, in1_pin)): + print("Testing channel ", ch) + outp(1) + self.assertEqual(1, inp()) + outp(0) + self.assertEqual(0, inp()) + + def test_basics(self): + self.assertPosition(0) + self.rotate(100) + self.assertPosition(100 // 4, 100 // 2, 100) + self.rotate(-100) + self.assertPosition(0) + + def test_partial(self): + # With phase=1 (default), need 4x pulses to count a rotation + self.assertPosition(0) + self.rotate(1) + self.assertPosition(1, 1, 1) + self.rotate(1) + self.assertPosition(1, 1, 2) + self.rotate(1) + self.assertPosition(1, 2, 3) + self.rotate(1) + self.assertPosition(1, 2, 4) # +4 + self.rotate(1) + self.assertPosition(2, 3, 5) + self.rotate(1) + self.assertPosition(2, 3, 6) + self.rotate(1) + self.assertPosition(2, 4, 7) + self.rotate(1) + self.assertPosition(2, 4, 8) # +4 + self.rotate(-1) + self.assertPosition(2, 4, 7) + self.rotate(-3) + self.assertPosition(1, 2, 4) # -4 + self.rotate(-4) + self.assertPosition(0, 0, 0) # -4 + self.rotate(-1) + self.assertPosition(0, 0, -1) + self.rotate(-1) + self.assertPosition(0, -1, -2) + self.rotate(-1) + self.assertPosition(0, -1, -3) + self.rotate(-1) + self.assertPosition(-1, -2, -4) # -4 + self.rotate(-1) + self.assertPosition(-1, -2, -5) + self.rotate(-3) + self.assertPosition(-2, -4, -8) # -4 + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/extmod_hardware/machine_i2c_target.py b/tests/extmod_hardware/machine_i2c_target.py new file mode 100644 index 0000000000000..763e6f4771e0f --- /dev/null +++ b/tests/extmod_hardware/machine_i2c_target.py @@ -0,0 +1,307 @@ +# Test machine.I2CTarget. +# +# IMPORTANT: This test requires hardware connections: a SoftI2C instance must be +# wired to a hardware I2C target. See pin definitions below. + +import sys + +try: + from machine import Pin, SoftI2C, I2CTarget +except ImportError: + print("SKIP") + raise SystemExit + +import unittest + +ADDR = 67 + +kwargs_target = {} + +# Configure pins based on the target. +if sys.platform == "alif" and sys.implementation._build == "ALIF_ENSEMBLE": + args_controller = {"scl": "P1_1", "sda": "P1_0"} + args_target = (0,) # on pins P0_3/P0_2 +elif sys.platform == "esp32": + args_controller = {"scl": 5, "sda": 6} + args_target = (0,) # on pins 9/8 for C3 and S3, 18/19 for others + kwargs_target = {"scl": 9, "sda": 8} +elif sys.platform == "rp2": + args_controller = {"scl": 5, "sda": 4} + args_target = (1,) +elif sys.platform == "pyboard": + if sys.implementation._build == "NUCLEO_WB55": + args_controller = {"scl": "B8", "sda": "B9"} + args_target = (3,) + else: + args_controller = {"scl": "X1", "sda": "X2"} + args_target = ("X",) +elif "zephyr-nucleo_wb55rg" in sys.implementation._machine: + # PB8=I2C1_SCL, PB9=I2C1_SDA (on Arduino header D15/D14) + # PC0=I2C3_SCL, PC1=I2C3_SDA (on Arduino header A0/A1) + args_controller = {"scl": Pin(("gpiob", 8)), "sda": Pin(("gpiob", 9))} + args_target = ("i2c3",) +elif "zephyr-rpi_pico" in sys.implementation._machine: + args_controller = {"scl": Pin(("gpio0", 5)), "sda": Pin(("gpio0", 4))} + args_target = ("i2c1",) # on gpio7/gpio6 +elif sys.platform == "mimxrt": + if "Teensy" in sys.implementation._machine: + args_controller = {"scl": "A6", "sda": "A3"} # D20/D17 + else: + args_controller = {"scl": "D0", "sda": "D1"} + args_target = (0,) # pins 19/18 On Teensy 4.x +elif sys.platform == "samd": + args_controller = {"scl": "D5", "sda": "D1"} + args_target = () +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def config_pull_up(): + Pin(args_controller["scl"], Pin.OPEN_DRAIN, Pin.PULL_UP) + Pin(args_controller["sda"], Pin.OPEN_DRAIN, Pin.PULL_UP) + + +class TestMemory(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.mem = bytearray(8) + cls.i2c = SoftI2C(**args_controller) + cls.i2c_target = I2CTarget(*args_target, **kwargs_target, addr=ADDR, mem=cls.mem) + config_pull_up() + + @classmethod + def tearDownClass(cls): + cls.i2c_target.deinit() + + def test_scan(self): + self.assertIn(ADDR, self.i2c.scan()) + + def test_write(self): + self.mem[:] = b"01234567" + self.i2c.writeto_mem(ADDR, 0, b"test") + self.assertEqual(self.mem, bytearray(b"test4567")) + self.i2c.writeto_mem(ADDR, 4, b"TEST") + self.assertEqual(self.mem, bytearray(b"testTEST")) + + def test_write_wrap(self): + self.mem[:] = b"01234567" + self.i2c.writeto_mem(ADDR, 6, b"test") + self.assertEqual(self.mem, bytearray(b"st2345te")) + + @unittest.skipIf(sys.platform == "esp32", "write lengths larger than buffer unsupported") + def test_write_wrap_large(self): + self.mem[:] = b"01234567" + self.i2c.writeto_mem(ADDR, 0, b"testTESTmore") + self.assertEqual(self.mem, bytearray(b"moreTEST")) + + def test_read(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.readfrom_mem(ADDR, 0, 4), b"0123") + self.assertEqual(self.i2c.readfrom_mem(ADDR, 4, 4), b"4567") + + def test_read_wrap(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.readfrom_mem(ADDR, 0, 4), b"0123") + self.assertEqual(self.i2c.readfrom_mem(ADDR, 2, 4), b"2345") + self.assertEqual(self.i2c.readfrom_mem(ADDR, 6, 4), b"6701") + + @unittest.skipIf(sys.platform == "esp32", "read lengths larger than buffer unsupported") + def test_read_wrap_large(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.readfrom_mem(ADDR, 0, 12), b"012345670123") + + def test_write_read(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.writeto(ADDR, b"\x02"), 1) + self.assertEqual(self.i2c.readfrom(ADDR, 4), b"2345") + + @unittest.skipIf(sys.platform == "esp32", "read after read unsupported") + def test_write_read_read(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.writeto(ADDR, b"\x02"), 1) + self.assertEqual(self.i2c.readfrom(ADDR, 4), b"2345") + self.assertEqual(self.i2c.readfrom(ADDR, 4), b"7012") + + +@unittest.skipUnless(hasattr(I2CTarget, "IRQ_END_READ"), "IRQ unsupported") +class TestMemoryIRQ(unittest.TestCase): + @staticmethod + def irq_handler(i2c_target): + flags = i2c_target.irq().flags() + TestMemoryIRQ.events[TestMemoryIRQ.num_events] = flags + TestMemoryIRQ.events[TestMemoryIRQ.num_events + 1] = i2c_target.memaddr + TestMemoryIRQ.num_events += 2 + + @classmethod + def setUpClass(cls): + cls.mem = bytearray(8) + cls.events = [0] * 8 + cls.num_events = 0 + cls.i2c = SoftI2C(**args_controller) + cls.i2c_target = I2CTarget(*args_target, **kwargs_target, addr=ADDR, mem=cls.mem) + cls.i2c_target.irq(TestMemoryIRQ.irq_handler) + config_pull_up() + + @classmethod + def tearDownClass(cls): + cls.i2c_target.deinit() + + @unittest.skipIf(sys.platform == "esp32", "scan doesn't trigger IRQ_END_WRITE") + def test_scan(self): + TestMemoryIRQ.num_events = 0 + self.i2c.scan() + self.assertEqual(self.events[: self.num_events], [I2CTarget.IRQ_END_WRITE, 0]) + + def test_write(self): + TestMemoryIRQ.num_events = 0 + self.mem[:] = b"01234567" + self.i2c.writeto_mem(ADDR, 2, b"test") + self.assertEqual(self.mem, bytearray(b"01test67")) + self.assertEqual(self.events[: self.num_events], [I2CTarget.IRQ_END_WRITE, 2]) + + def test_read(self): + TestMemoryIRQ.num_events = 0 + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.readfrom_mem(ADDR, 2, 4), b"2345") + self.assertEqual(self.events[: self.num_events], [I2CTarget.IRQ_END_READ, 2]) + + +@unittest.skipUnless(hasattr(I2CTarget, "IRQ_WRITE_REQ"), "IRQ unsupported") +@unittest.skipIf(sys.platform == "mimxrt", "not working") +@unittest.skipIf(sys.platform == "pyboard", "can't queue more than one byte") +@unittest.skipIf(sys.platform == "samd", "not working") +@unittest.skipIf(sys.platform == "zephyr", "must call readinto/write in IRQ handler") +class TestPolling(unittest.TestCase): + @staticmethod + def irq_handler(i2c_target, buf=bytearray(1)): + flags = i2c_target.irq().flags() + if flags & I2CTarget.IRQ_READ_REQ: + i2c_target.write(b"0123") + + @classmethod + def setUpClass(cls): + cls.i2c = SoftI2C(**args_controller) + cls.i2c_target = I2CTarget(*args_target, addr=ADDR) + cls.i2c_target.irq( + TestPolling.irq_handler, + I2CTarget.IRQ_WRITE_REQ | I2CTarget.IRQ_READ_REQ, + hard=True, + ) + config_pull_up() + + @classmethod + def tearDownClass(cls): + cls.i2c_target.deinit() + + def test_read(self): + # Can't write data up front, must wait until IRQ_READ_REQ. + # self.assertEqual(self.i2c_target.write(b"abcd"), 4) + self.assertEqual(self.i2c.readfrom(ADDR, 4), b"0123") + + def test_write(self): + # Can do the read outside the IRQ, but requires IRQ_WRITE_REQ trigger to be set. + self.assertEqual(self.i2c.writeto(ADDR, b"0123"), 4) + buf = bytearray(8) + self.assertEqual(self.i2c_target.readinto(buf), 4) + self.assertEqual(buf, b"0123\x00\x00\x00\x00") + + +@unittest.skipUnless(hasattr(I2CTarget, "IRQ_ADDR_MATCH_READ"), "IRQ unsupported") +class TestIRQ(unittest.TestCase): + @staticmethod + def irq_handler(i2c_target, buf=bytearray(1)): + flags = i2c_target.irq().flags() + TestIRQ.events[TestIRQ.num_events] = flags + TestIRQ.num_events += 1 + if flags & I2CTarget.IRQ_READ_REQ: + i2c_target.write(b"Y") + if flags & I2CTarget.IRQ_WRITE_REQ: + i2c_target.readinto(buf) + TestIRQ.events[TestIRQ.num_events] = buf[0] + TestIRQ.num_events += 1 + + @classmethod + def setUpClass(cls): + cls.events = [0] * 8 + cls.num_events = 0 + cls.i2c = SoftI2C(**args_controller) + cls.i2c_target = I2CTarget(*args_target, addr=ADDR) + cls.i2c_target.irq( + TestIRQ.irq_handler, + I2CTarget.IRQ_ADDR_MATCH_READ + | I2CTarget.IRQ_ADDR_MATCH_WRITE + | I2CTarget.IRQ_WRITE_REQ + | I2CTarget.IRQ_READ_REQ + | I2CTarget.IRQ_END_READ + | I2CTarget.IRQ_END_WRITE, + hard=True, + ) + config_pull_up() + + @classmethod + def tearDownClass(cls): + cls.i2c_target.deinit() + + def test_scan(self): + TestIRQ.num_events = 0 + self.i2c.scan() + self.assertEqual( + self.events[: self.num_events], + [ + I2CTarget.IRQ_ADDR_MATCH_WRITE, + I2CTarget.IRQ_END_WRITE, + ], + ) + + def test_write(self): + TestIRQ.num_events = 0 + self.i2c.writeto(ADDR, b"XYZ") + self.assertEqual( + self.events[: self.num_events], + [ + I2CTarget.IRQ_ADDR_MATCH_WRITE, + I2CTarget.IRQ_WRITE_REQ, + ord(b"X"), + I2CTarget.IRQ_WRITE_REQ, + ord(b"Y"), + I2CTarget.IRQ_WRITE_REQ, + ord(b"Z"), + I2CTarget.IRQ_END_WRITE, + ], + ) + + def test_read(self): + TestIRQ.num_events = 0 + self.assertEqual(self.i2c.readfrom(ADDR, 1), b"Y") + self.assertEqual( + self.events[: self.num_events], + [ + I2CTarget.IRQ_ADDR_MATCH_READ, + I2CTarget.IRQ_READ_REQ, + I2CTarget.IRQ_READ_REQ, + I2CTarget.IRQ_END_READ, + ], + ) + + def test_write_read(self): + TestIRQ.num_events = 0 + self.i2c.writeto(ADDR, b"X", False) + self.assertEqual(self.i2c.readfrom(ADDR, 1), b"Y") + self.assertEqual( + self.events[: self.num_events], + [ + I2CTarget.IRQ_ADDR_MATCH_WRITE, + I2CTarget.IRQ_WRITE_REQ, + ord(b"X"), + I2CTarget.IRQ_END_WRITE, + I2CTarget.IRQ_ADDR_MATCH_READ, + I2CTarget.IRQ_READ_REQ, + I2CTarget.IRQ_READ_REQ, + I2CTarget.IRQ_END_READ, + ], + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/feature_check/float.py b/tests/feature_check/float.py deleted file mode 100644 index d6d2a99d2429d..0000000000000 --- a/tests/feature_check/float.py +++ /dev/null @@ -1,13 +0,0 @@ -# detect how many bits of precision the floating point implementation has - -try: - float -except NameError: - print(0) -else: - if float("1.0000001") == float("1.0"): - print(30) - elif float("1e300") == float("inf"): - print(32) - else: - print(64) diff --git a/tests/feature_check/float.py.exp b/tests/feature_check/float.py.exp deleted file mode 100644 index 900731ffd51ff..0000000000000 --- a/tests/feature_check/float.py.exp +++ /dev/null @@ -1 +0,0 @@ -64 diff --git a/tests/feature_check/inlineasm_rv32_zba.py b/tests/feature_check/inlineasm_rv32_zba.py new file mode 100644 index 0000000000000..81228819042ee --- /dev/null +++ b/tests/feature_check/inlineasm_rv32_zba.py @@ -0,0 +1,10 @@ +# check if RISC-V 32 inline asm supported Zba opcodes + + +@micropython.asm_rv32 +def f(): + sh1add(a0, a0, a0) + + +f() +print("rv32_zba") diff --git a/tests/feature_check/inlineasm_rv32_zba.py.exp b/tests/feature_check/inlineasm_rv32_zba.py.exp new file mode 100644 index 0000000000000..fde22f5f40090 --- /dev/null +++ b/tests/feature_check/inlineasm_rv32_zba.py.exp @@ -0,0 +1 @@ +rv32_zba diff --git a/tests/feature_check/int_64.py b/tests/feature_check/int_64.py new file mode 100644 index 0000000000000..4d053782ca82b --- /dev/null +++ b/tests/feature_check/int_64.py @@ -0,0 +1,2 @@ +# Check whether 64-bit long integers are supported +print(1 << 62) diff --git a/tests/feature_check/int_64.py.exp b/tests/feature_check/int_64.py.exp new file mode 100644 index 0000000000000..aef5454e66263 --- /dev/null +++ b/tests/feature_check/int_64.py.exp @@ -0,0 +1 @@ +4611686018427387904 diff --git a/tests/feature_check/io_module.py b/tests/feature_check/io_module.py deleted file mode 100644 index 9094e605316ba..0000000000000 --- a/tests/feature_check/io_module.py +++ /dev/null @@ -1,6 +0,0 @@ -try: - import io - - print("io") -except ImportError: - print("no") diff --git a/tests/feature_check/io_module.py.exp b/tests/feature_check/io_module.py.exp deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/feature_check/repl_emacs_check.py.exp b/tests/feature_check/repl_emacs_check.py.exp index 82a4e28ee4f84..5fe8ba1cd2df8 100644 --- a/tests/feature_check/repl_emacs_check.py.exp +++ b/tests/feature_check/repl_emacs_check.py.exp @@ -1,5 +1,5 @@ MicroPython \.\+ version -Use \.\+ +Type "help()" for more information. >>> # Check for emacs keys in REPL >>> t = \.\+ >>> t == 2 diff --git a/tests/feature_check/repl_words_move_check.py.exp b/tests/feature_check/repl_words_move_check.py.exp index 82a4e28ee4f84..5fe8ba1cd2df8 100644 --- a/tests/feature_check/repl_words_move_check.py.exp +++ b/tests/feature_check/repl_words_move_check.py.exp @@ -1,5 +1,5 @@ MicroPython \.\+ version -Use \.\+ +Type "help()" for more information. >>> # Check for emacs keys in REPL >>> t = \.\+ >>> t == 2 diff --git a/tests/feature_check/target_info.py b/tests/feature_check/target_info.py index f60f3b319192e..e95530023d7cb 100644 --- a/tests/feature_check/target_info.py +++ b/tests/feature_check/target_info.py @@ -19,5 +19,21 @@ "xtensa", "xtensawin", "rv32imc", -][sys_mpy >> 10] -print(platform, arch) + "rv64imc", +][(sys_mpy >> 10) & 0x0F] +arch_flags = sys_mpy >> 16 +build = getattr(sys.implementation, "_build", "unknown") +thread = getattr(sys.implementation, "_thread", None) + +# Detect how many bits of precision the floating point implementation has. +try: + if float("1.0000001") == float("1.0"): + float_prec = 30 + elif float("1e300") == float("inf"): + float_prec = 32 + else: + float_prec = 64 +except NameError: + float_prec = 0 + +print(platform, arch, arch_flags, build, thread, float_prec, len("α") == 1) diff --git a/tests/float/cmath_fun.py b/tests/float/cmath_fun.py index 39011733b02b4..0037d7c65596c 100644 --- a/tests/float/cmath_fun.py +++ b/tests/float/cmath_fun.py @@ -51,6 +51,9 @@ print("%.5g" % ret) elif type(ret) == tuple: print("%.5g %.5g" % ret) + elif f_name == "exp": + # exp amplifies REPR_C inaccuracies, so we need to check one digit less + print("complex(%.4g, %.4g)" % (real, ret.imag)) else: # some test (eg cmath.sqrt(-0.5)) disagree with CPython with tiny real part real = ret.real diff --git a/tests/float/float_array.py b/tests/float/float_array.py index 3d128da83819f..cfff3b220c657 100644 --- a/tests/float/float_array.py +++ b/tests/float/float_array.py @@ -19,4 +19,10 @@ def test(a): test(array("f")) test(array("d")) -print("{:.4f}".format(array("f", bytes(array("I", [0x3DCCCCCC])))[0])) +# hand-crafted floats, including non-standard nan +for float_hex in (0x3DCCCCCC, 0x7F800024, 0x7FC00004): + f = array("f", bytes(array("I", [float_hex])))[0] + if type(f) is float: + print("{:.4e}".format(f)) + else: + print(f) diff --git a/tests/float/float_format.py b/tests/float/float_format.py index 98ed0eb096fa4..0eb8b232b063a 100644 --- a/tests/float/float_format.py +++ b/tests/float/float_format.py @@ -2,14 +2,25 @@ # general rounding for val in (116, 1111, 1234, 5010, 11111): - print("%.0f" % val) - print("%.1f" % val) - print("%.3f" % val) + print("Test on %d / 1000:" % val) + for fmt in ("%.5e", "%.3e", "%.1e", "%.0e", "%.3f", "%.1f", "%.0f", "%.3g", "%.1g", "%.0g"): + print(fmt, fmt % (val / 1000)) + +# make sure round-up to the next unit is handled properly +for val in range(4, 9): + divi = 10**val + print("Test on 99994 / (10 ** %d):" % val) + for fmt in ("%.5e", "%.3e", "%.1e", "%.0e", "%.3f", "%.1f", "%.0f", "%.3g", "%.1g", "%.0g"): + print(fmt, fmt % (99994 / divi)) # make sure rounding is done at the correct precision for prec in range(8): print(("%%.%df" % prec) % 6e-5) +# make sure trailing zeroes are added properly +for prec in range(8): + print(("%%.%df" % prec) % 1e19) + # check certain cases that had a digit value of 10 render as a ":" character print("%.2e" % float("9" * 51 + "e-39")) print("%.2e" % float("9" * 40 + "e-21")) diff --git a/tests/float/float_format_accuracy.py b/tests/float/float_format_accuracy.py new file mode 100644 index 0000000000000..f9467f9c05d89 --- /dev/null +++ b/tests/float/float_format_accuracy.py @@ -0,0 +1,73 @@ +# Test accuracy of `repr` conversions. +# This test also increases code coverage for corner cases. + +try: + import array, math, random +except ImportError: + print("SKIP") + raise SystemExit + +# The largest errors come from seldom used very small numbers, near the +# limit of the representation. So we keep them out of this test to keep +# the max relative error display useful. +if float("1e-100") == 0.0: + # single-precision + float_type = "f" + float_size = 4 + # testing range + min_expo = -96 # i.e. not smaller than 1.0e-29 + # Expected results (given >=50'000 samples): + # - MICROPY_FLTCONV_IMPL_EXACT: 100% exact conversions + # - MICROPY_FLTCONV_IMPL_APPROX: >=98.53% exact conversions, max relative error <= 1.01e-7 + min_success = 0.980 # with only 1200 samples, the success rate is lower + max_rel_err = 1.1e-7 + # REPR_C is typically used with FORMAT_IMPL_BASIC, which has a larger error + is_REPR_C = float("1.0000001") == float("1.0") + if is_REPR_C: # REPR_C + min_success = 0.83 + max_rel_err = 5.75e-07 +else: + # double-precision + float_type = "d" + float_size = 8 + # testing range + min_expo = -845 # i.e. not smaller than 1.0e-254 + # Expected results (given >=200'000 samples): + # - MICROPY_FLTCONV_IMPL_EXACT: 100% exact conversions + # - MICROPY_FLTCONV_IMPL_APPROX: >=99.83% exact conversions, max relative error <= 2.7e-16 + min_success = 0.997 # with only 1200 samples, the success rate is lower + max_rel_err = 2.7e-16 + + +# Deterministic pseudorandom generator. Designed to be uniform +# on mantissa values and exponents, not on the represented number +def pseudo_randfloat(): + rnd_buff = bytearray(float_size) + for _ in range(float_size): + rnd_buff[_] = random.getrandbits(8) + return array.array(float_type, rnd_buff)[0] + + +random.seed(42) +stats = 0 +N = 1200 +max_err = 0 +for _ in range(N): + f = pseudo_randfloat() + while type(f) is not float or math.isinf(f) or math.isnan(f) or math.frexp(f)[1] <= min_expo: + f = pseudo_randfloat() + + str_f = repr(f) + f2 = float(str_f) + if f2 == f: + stats += 1 + else: + error = abs((f2 - f) / f) + if max_err < error: + max_err = error + +print(N, "values converted") +if stats / N >= min_success and max_err <= max_rel_err: + print("float format accuracy OK") +else: + print("FAILED: repr rate=%.3f%% max_err=%.3e" % (100 * stats / N, max_err)) diff --git a/tests/float/float_format_ints.py b/tests/float/float_format_ints.py index df4444166c5fa..7b7b30c4b340b 100644 --- a/tests/float/float_format_ints.py +++ b/tests/float/float_format_ints.py @@ -12,14 +12,42 @@ print(title, "with format", f_fmt, "gives", f_fmt.format(f)) print(title, "with format", g_fmt, "gives", g_fmt.format(f)) +# The tests below check border cases involving all mantissa bits. +# In case of REPR_C, where the mantissa is missing two bits, the +# the string representation for such numbers might not always be exactly +# the same but nevertheless be correct, so we must allow a few exceptions. +is_REPR_C = float("1.0000001") == float("1.0") + # 16777215 is 2^24 - 1, the largest integer that can be completely held # in a float32. -print("{:f}".format(16777215)) +val_str = "{:f}".format(16777215) + +# When using REPR_C, 16777215.0 is the same as 16777212.0 or 16777214.4 +# (depending on the implementation of pow() function, the result may differ) +if is_REPR_C and (val_str == "16777212.000000" or val_str == "16777214.400000"): + val_str = "16777215.000000" + +print(val_str) + # 4294967040 = 16777215 * 128 is the largest integer that is exactly # represented by a float32 and that will also fit within a (signed) int32. # The upper bound of our integer-handling code is actually double this, # but that constant might cause trouble on systems using 32 bit ints. -print("{:f}".format(2147483520)) +val_str = "{:f}".format(2147483520) + +# When using FLOAT_IMPL_FLOAT, 2147483520.0 == 2147483500.0 +# Both representations are valid, the second being "simpler" +is_float32 = float("1e300") == float("inf") +if is_float32 and val_str == "2147483500.000000": + val_str = "2147483520.000000" + +# When using REPR_C, 2147483520.0 is the same as 2147483200.0 +# Both representations are valid, the second being "simpler" +if is_REPR_C and val_str == "2147483200.000000": + val_str = "2147483520.000000" + +print(val_str) + # Very large positive integers can be a test for precision and resolution. # This is a weird way to represent 1e38 (largest power of 10 for float32). print("{:.6e}".format(float("9" * 30 + "e8"))) diff --git a/tests/float/float_parse_doubleprec.py b/tests/float/float_parse_doubleprec.py index 81fcadcee88b4..c1b0b4823b038 100644 --- a/tests/float/float_parse_doubleprec.py +++ b/tests/float/float_parse_doubleprec.py @@ -19,3 +19,9 @@ print(float("1.00000000000000000000e-307")) print(float("10.0000000000000000000e-308")) print(float("100.000000000000000000e-309")) + +# ensure repr() adds an extra digit when needed for accurate parsing +print(float(repr(float("2.0") ** 100)) == float("2.0") ** 100) + +# ensure repr does not add meaningless extra digits (1.234999999999) +print(repr(1.2345)) diff --git a/tests/float/float_struct_e.py b/tests/float/float_struct_e.py index 403fbc5db4cde..ba4134f3393ed 100644 --- a/tests/float/float_struct_e.py +++ b/tests/float/float_struct_e.py @@ -32,7 +32,7 @@ for i in (j, -j): x = struct.pack("", "=", "^"): for fill in ("", " ", "0", "@"): for sign in ("", "+", "-", " "): - # An empty precision defaults to 6, but when uPy is + # An empty precision defaults to 6, but when MicroPython is # configured to use a float, we can only use a # precision of 6 with numbers less than 10 and still # get results that compare to CPython (which uses @@ -164,7 +164,7 @@ def test_fmt(conv, fill, alignment, sign, prefix, width, precision, type, arg): for alignment in ("", "<", ">", "=", "^"): for fill in ("", " ", "0", "@"): for sign in ("", "+", "-", " "): - # An empty precision defaults to 6, but when uPy is + # An empty precision defaults to 6, but when MicroPython is # configured to use a float, we can only use a # precision of 6 with numbers less than 10 and still # get results that compare to CPython (which uses diff --git a/tests/float/string_format_fp30.py b/tests/float/string_format_fp30.py deleted file mode 100644 index 5f0b213daa342..0000000000000 --- a/tests/float/string_format_fp30.py +++ /dev/null @@ -1,42 +0,0 @@ -def test(fmt, *args): - print("{:8s}".format(fmt) + ">" + fmt.format(*args) + "<") - - -test("{:10.4}", 123.456) -test("{:10.4e}", 123.456) -test("{:10.4e}", -123.456) -# test("{:10.4f}", 123.456) -# test("{:10.4f}", -123.456) -test("{:10.4g}", 123.456) -test("{:10.4g}", -123.456) -test("{:10.4n}", 123.456) -test("{:e}", 100) -test("{:f}", 200) -test("{:g}", 300) - -test("{:10.4E}", 123.456) -test("{:10.4E}", -123.456) -# test("{:10.4F}", 123.456) -# test("{:10.4F}", -123.456) -test("{:10.4G}", 123.456) -test("{:10.4G}", -123.456) - -test("{:06e}", float("inf")) -test("{:06e}", float("-inf")) -test("{:06e}", float("nan")) - -# The following fails right now -# test("{:10.1}", 0.0) - -print("%.0f" % (1.750000 % 0.08333333333)) -# Below isn't compatible with single-precision float -# print("%.1f" % (1.750000 % 0.08333333333)) -# print("%.2f" % (1.750000 % 0.08333333333)) -# print("%.12f" % (1.750000 % 0.08333333333)) - -# tests for errors in format string - -try: - "{:10.1b}".format(0.0) -except ValueError: - print("ValueError") diff --git a/tests/float/string_format_modulo.py b/tests/float/string_format_modulo.py index 3c206b7393588..55314643351db 100644 --- a/tests/float/string_format_modulo.py +++ b/tests/float/string_format_modulo.py @@ -6,7 +6,7 @@ print("%u" % 1.0) # these 3 have different behaviour in Python 3.x versions -# uPy raises a TypeError, following Python 3.5 (earlier versions don't) +# MicroPython raises a TypeError, following Python 3.5 (earlier versions don't) # print("%x" % 18.0) # print("%o" % 18.0) # print("%X" % 18.0) diff --git a/tests/float/string_format_modulo3.py b/tests/float/string_format_modulo3.py index f9d9c43cdf42d..f8aeeda20f29c 100644 --- a/tests/float/string_format_modulo3.py +++ b/tests/float/string_format_modulo3.py @@ -1,3 +1,3 @@ -# uPy and CPython outputs differ for the following +# Test corner cases where MicroPython and CPython outputs used to differ in the past print("%.1g" % -9.9) # round up 'g' with '-' sign print("%.2g" % 99.9) # round up diff --git a/tests/float/string_format_modulo3.py.exp b/tests/float/string_format_modulo3.py.exp deleted file mode 100644 index 71432b3404519..0000000000000 --- a/tests/float/string_format_modulo3.py.exp +++ /dev/null @@ -1,2 +0,0 @@ --10 -100 diff --git a/tests/frozen/frozentest.mpy b/tests/frozen/frozentest.mpy index 7f1163956bafeacef9bda191aa3376fbf589c350..c16da5d20fb73f0e09e3136812783ff305a25b0f 100644 GIT binary patch delta 12 TcmX@Yc!H7X|Gx=Gn79}KBo_qk delta 7 OcmX@Xc!Y5x(-8m**aGGN diff --git a/tests/frozen/frozentest.py b/tests/frozen/frozentest.py index 78cdd60bf0431..74bdfb3b267f2 100644 --- a/tests/frozen/frozentest.py +++ b/tests/frozen/frozentest.py @@ -1,4 +1,4 @@ -print("uPy") +print("interned") print("a long string that is not interned") print("a string that has unicode αβγ chars") print(b"bytes 1234\x01") diff --git a/tests/import/builtin_ext.py b/tests/import/builtin_ext.py index 9d2344d400dae..79ee901ea6774 100644 --- a/tests/import/builtin_ext.py +++ b/tests/import/builtin_ext.py @@ -1,3 +1,9 @@ +try: + import uos, utime +except ImportError: + print("SKIP") + raise SystemExit + # Verify that sys is a builtin. import sys diff --git a/tests/import/builtin_import.py b/tests/import/builtin_import.py index 734498d1be47b..364b0bae912ae 100644 --- a/tests/import/builtin_import.py +++ b/tests/import/builtin_import.py @@ -20,3 +20,12 @@ __import__("xyz", None, None, None, -1) except ValueError: print("ValueError") + +# globals is not checked for level=0 +__import__("builtins", "globals") + +# globals must be a dict (or None) for level>0 +try: + __import__("builtins", "globals", None, None, 1) +except TypeError: + print("TypeError") diff --git a/tests/import/import_broken.py b/tests/import/import_broken.py index 3c7cf4a498528..440922157ef10 100644 --- a/tests/import/import_broken.py +++ b/tests/import/import_broken.py @@ -1,3 +1,9 @@ +try: + Exception.__class__ +except AttributeError: + print("SKIP") + raise SystemExit + import sys, pkg # Modules we import are usually added to sys.modules. diff --git a/tests/import/import_file.py b/tests/import/import_file.py index 90ec4e41e77aa..4cf307641c62d 100644 --- a/tests/import/import_file.py +++ b/tests/import/import_file.py @@ -1,3 +1,7 @@ +if "__file__" not in globals(): + print("SKIP") + raise SystemExit + import import1b print(import1b.__file__) diff --git a/tests/import/import_override.py b/tests/import/import_override.py index 029ebe54c1f70..0144e78cb9f32 100644 --- a/tests/import/import_override.py +++ b/tests/import/import_override.py @@ -2,6 +2,10 @@ def custom_import(name, globals, locals, fromlist, level): + # CPython always tries to import _io, so just let that through as-is. + if name == "_io": + return orig_import(name, globals, locals, fromlist, level) + print("import", name, fromlist, level) class M: diff --git a/tests/import/import_override.py.exp b/tests/import/import_override.py.exp deleted file mode 100644 index 365248da6d847..0000000000000 --- a/tests/import/import_override.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -import import1b None 0 -456 diff --git a/tests/import/import_override2.py b/tests/import/import_override2.py new file mode 100644 index 0000000000000..25aac44fe98d0 --- /dev/null +++ b/tests/import/import_override2.py @@ -0,0 +1,18 @@ +# test overriding __import__ combined with importing from the filesystem + + +def custom_import(name, globals, locals, fromlist, level): + if level > 0: + print("import", name, fromlist, level) + return orig_import(name, globals, locals, fromlist, level) + + +orig_import = __import__ +try: + __import__("builtins").__import__ = custom_import +except AttributeError: + print("SKIP") + raise SystemExit + +# import calls __import__ behind the scenes +import pkg7.subpkg1.subpkg2.mod3 diff --git a/tests/import/import_pkg7.py.exp b/tests/import/import_pkg7.py.exp deleted file mode 100644 index 8f21a615f6a61..0000000000000 --- a/tests/import/import_pkg7.py.exp +++ /dev/null @@ -1,8 +0,0 @@ -pkg __name__: pkg7 -pkg __name__: pkg7.subpkg1 -pkg __name__: pkg7.subpkg1.subpkg2 -mod1 -mod2 -mod1.foo -mod2.bar -ImportError diff --git a/tests/import/import_pkg9.py b/tests/import/import_pkg9.py index 4de028494f1a2..c2e0b618b6969 100644 --- a/tests/import/import_pkg9.py +++ b/tests/import/import_pkg9.py @@ -13,4 +13,4 @@ import pkg9.mod2 pkg9.mod1() -print(pkg9.mod2.__name__, type(pkg9.mod2).__name__) +print(pkg9.mod2.__name__, type(pkg9.mod2)) diff --git a/tests/import/import_star.py b/tests/import/import_star.py new file mode 100644 index 0000000000000..2cb21b877d728 --- /dev/null +++ b/tests/import/import_star.py @@ -0,0 +1,59 @@ +# test `from package import *` conventions, including __all__ support +# +# This test requires MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES + +try: + next(iter([]), 42) +except TypeError: + # 2-argument version of next() not supported + # we are probably not at MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES + print("SKIP") + raise SystemExit + +# 1. test default visibility +from pkgstar_default import * + +print("visibleFun" in globals()) +print("VisibleClass" in globals()) +print("_hiddenFun" in globals()) +print("_HiddenClass" in globals()) +print(visibleFun()) + +# 2. test explicit visibility as defined by __all__ (as an array) +from pkgstar_all_array import * + +print("publicFun" in globals()) +print("PublicClass" in globals()) +print("unlistedFun" in globals()) +print("UnlistedClass" in globals()) +print("_privateFun" in globals()) +print("_PrivateClass" in globals()) +print(publicFun()) +# test dynamic import as used in asyncio +print("dynamicFun" in globals()) +print(dynamicFun()) + +# 3. test explicit visibility as defined by __all__ (as an tuple) +from pkgstar_all_tuple import * + +print("publicFun2" in globals()) +print("PublicClass2" in globals()) +print("unlistedFun2" in globals()) +print("UnlistedClass2" in globals()) +print(publicFun2()) + +# 4. test reporting of missing entries in __all__ +try: + from pkgstar_all_miss import * + + print("missed detection of incorrect __all__ definition") +except AttributeError as er: + print("AttributeError triggered for bad __all__ definition") + +# 5. test reporting of invalid __all__ definition +try: + from pkgstar_all_inval import * + + print("missed detection of incorrect __all__ definition") +except TypeError as er: + print("TypeError triggered for bad __all__ definition") diff --git a/tests/import/import_star_error.py b/tests/import/import_star_error.py index 9e1757b6ef5a2..73d9c863d6f51 100644 --- a/tests/import/import_star_error.py +++ b/tests/import/import_star_error.py @@ -1,5 +1,10 @@ # test errors with import * +if not hasattr(object, "__init__"): + # target doesn't have MICROPY_CPYTHON_COMPAT enabled, so doesn't check for "import *" + print("SKIP") + raise SystemExit + # 'import *' is not allowed in function scope try: exec("def foo(): from x import *") diff --git a/tests/import/module_getattr.py.exp b/tests/import/module_getattr.py.exp deleted file mode 100644 index bc59c12aa16bd..0000000000000 --- a/tests/import/module_getattr.py.exp +++ /dev/null @@ -1 +0,0 @@ -False diff --git a/tests/import/pkg7/subpkg1/subpkg2/mod3.py b/tests/import/pkg7/subpkg1/subpkg2/mod3.py index b0f4279fcf57b..f7f4c1e65f46f 100644 --- a/tests/import/pkg7/subpkg1/subpkg2/mod3.py +++ b/tests/import/pkg7/subpkg1/subpkg2/mod3.py @@ -5,7 +5,9 @@ print(bar) # attempted relative import beyond top-level package +# On older versions of CPython (eg 3.8) this is a ValueError, but on +# newer CPython (eg 3.11) and MicroPython it's an ImportError. try: from .... import mod1 -except ImportError: +except (ImportError, ValueError): print("ImportError") diff --git a/tests/import/pkgstar_all_array/__init__.py b/tests/import/pkgstar_all_array/__init__.py new file mode 100644 index 0000000000000..03b012123fe0c --- /dev/null +++ b/tests/import/pkgstar_all_array/__init__.py @@ -0,0 +1,49 @@ +__all__ = ["publicFun", "PublicClass", "dynamicFun"] + + +# Definitions below should always be imported by a star import +def publicFun(): + return 1 + + +class PublicClass: + def __init__(self): + self._val = 1 + + +# If __all__ support is enabled, definitions below +# should not be imported by a star import +def unlistedFun(): + return 0 + + +class UnlistedClass: + def __init__(self): + self._val = 0 + + +# Definitions below should be not be imported by a star import +# (they start with an underscore, and are not listed in __all__) +def _privateFun(): + return -1 + + +class _PrivateClass: + def __init__(self): + self._val = -1 + + +# Test lazy loaded function, as used by extmod/asyncio: +# Works with a star import only if __all__ support is enabled +_attrs = { + "dynamicFun": "funcs", +} + + +def __getattr__(attr): + mod = _attrs.get(attr, None) + if mod is None: + raise AttributeError(attr) + value = getattr(__import__(mod, globals(), locals(), True, 1), attr) + globals()[attr] = value + return value diff --git a/tests/import/pkgstar_all_array/funcs.py b/tests/import/pkgstar_all_array/funcs.py new file mode 100644 index 0000000000000..7540d70f66bce --- /dev/null +++ b/tests/import/pkgstar_all_array/funcs.py @@ -0,0 +1,2 @@ +def dynamicFun(): + return 777 diff --git a/tests/import/pkgstar_all_inval/__init__.py b/tests/import/pkgstar_all_inval/__init__.py new file mode 100644 index 0000000000000..7022476c19be8 --- /dev/null +++ b/tests/import/pkgstar_all_inval/__init__.py @@ -0,0 +1 @@ +__all__ = 42 diff --git a/tests/import/pkgstar_all_miss/__init__.py b/tests/import/pkgstar_all_miss/__init__.py new file mode 100644 index 0000000000000..f9bbb538072bf --- /dev/null +++ b/tests/import/pkgstar_all_miss/__init__.py @@ -0,0 +1,8 @@ +__all__ = ("existingFun", "missingFun") + + +def existingFun(): + return None + + +# missingFun is not defined, should raise an error on import diff --git a/tests/import/pkgstar_all_tuple/__init__.py b/tests/import/pkgstar_all_tuple/__init__.py new file mode 100644 index 0000000000000..433ddc8e97639 --- /dev/null +++ b/tests/import/pkgstar_all_tuple/__init__.py @@ -0,0 +1,22 @@ +__all__ = ("publicFun2", "PublicClass2") + + +# Definitions below should always be imported by a star import +def publicFun2(): + return 2 + + +class PublicClass2: + def __init__(self): + self._val = 2 + + +# If __all__ support is enabled, definitions below +# should not be imported by a star import +def unlistedFun2(): + return 0 + + +class UnlistedClass2: + def __init__(self): + self._val = 0 diff --git a/tests/import/pkgstar_default/__init__.py b/tests/import/pkgstar_default/__init__.py new file mode 100644 index 0000000000000..4947e4ce7f180 --- /dev/null +++ b/tests/import/pkgstar_default/__init__.py @@ -0,0 +1,20 @@ +# When __all__ is undefined, star import should only +# show objects that do not start with an underscore + + +def visibleFun(): + return 42 + + +class VisibleClass: + def __init__(self): + self._val = 42 + + +def _hiddenFun(): + return -1 + + +class _HiddenClass: + def __init__(self): + self._val = -1 diff --git a/tests/inlineasm/rv32/asmzba.py b/tests/inlineasm/rv32/asmzba.py new file mode 100644 index 0000000000000..75f3573c864d6 --- /dev/null +++ b/tests/inlineasm/rv32/asmzba.py @@ -0,0 +1,18 @@ +@micropython.asm_rv32 +def test_sh1add(a0, a1): + sh1add(a0, a0, a1) + + +@micropython.asm_rv32 +def test_sh2add(a0, a1): + sh2add(a0, a0, a1) + + +@micropython.asm_rv32 +def test_sh3add(a0, a1): + sh3add(a0, a0, a1) + + +print(hex(test_sh1add(10, 20))) +print(hex(test_sh2add(10, 20))) +print(hex(test_sh3add(10, 20))) diff --git a/tests/inlineasm/rv32/asmzba.py.exp b/tests/inlineasm/rv32/asmzba.py.exp new file mode 100644 index 0000000000000..5f56bd95642a4 --- /dev/null +++ b/tests/inlineasm/rv32/asmzba.py.exp @@ -0,0 +1,3 @@ +0x28 +0x3c +0x64 diff --git a/tests/inlineasm/thumb/asmerrors.py b/tests/inlineasm/thumb/asmerrors.py new file mode 100644 index 0000000000000..a26f9322540fd --- /dev/null +++ b/tests/inlineasm/thumb/asmerrors.py @@ -0,0 +1,4 @@ +try: + exec("@micropython.asm_thumb\ndef l():\n a = di(a2, a2, -1)") +except SyntaxError as e: + print(e) diff --git a/tests/inlineasm/thumb/asmerrors.py.exp b/tests/inlineasm/thumb/asmerrors.py.exp new file mode 100644 index 0000000000000..7590f171e5184 --- /dev/null +++ b/tests/inlineasm/thumb/asmerrors.py.exp @@ -0,0 +1 @@ +expecting an assembler instruction diff --git a/tests/inlineasm/xtensa/asmargs.py b/tests/inlineasm/xtensa/asmargs.py new file mode 100644 index 0000000000000..2bfccfcc69bb2 --- /dev/null +++ b/tests/inlineasm/xtensa/asmargs.py @@ -0,0 +1,44 @@ +# test passing arguments + + +@micropython.asm_xtensa +def arg0(): + movi(a2, 1) + + +print(arg0()) + + +@micropython.asm_xtensa +def arg1(a2): + addi(a2, a2, 1) + + +print(arg1(1)) + + +@micropython.asm_xtensa +def arg2(a2, a3): + add(a2, a2, a3) + + +print(arg2(1, 2)) + + +@micropython.asm_xtensa +def arg3(a2, a3, a4): + add(a2, a2, a3) + add(a2, a2, a4) + + +print(arg3(1, 2, 3)) + + +@micropython.asm_xtensa +def arg4(a2, a3, a4, a5): + add(a2, a2, a3) + add(a2, a2, a4) + add(a2, a2, a5) + + +print(arg4(1, 2, 3, 4)) diff --git a/tests/inlineasm/xtensa/asmargs.py.exp b/tests/inlineasm/xtensa/asmargs.py.exp new file mode 100644 index 0000000000000..e33a6964f46e8 --- /dev/null +++ b/tests/inlineasm/xtensa/asmargs.py.exp @@ -0,0 +1,5 @@ +1 +2 +3 +6 +10 diff --git a/tests/inlineasm/xtensa/asmarith.py b/tests/inlineasm/xtensa/asmarith.py new file mode 100644 index 0000000000000..1c0934eb7a631 --- /dev/null +++ b/tests/inlineasm/xtensa/asmarith.py @@ -0,0 +1,119 @@ +@micropython.asm_xtensa +def f1(a2): + abs_(a2, a2) + + +for value in (10, -10, 0): + print(f1(value)) + + +ADDMI_TEMPLATE = """ +@micropython.asm_xtensa +def f1(a2) -> int: + addmi(a2, a2, {}) +print(f1(0)) +""" + +for value in (-32768, -32767, 32512, 32513, 0): + try: + exec(ADDMI_TEMPLATE.format(value)) + except SyntaxError as error: + print(error) + + +@micropython.asm_xtensa +def a2(a2, a3) -> int: + addx2(a2, a2, a3) + + +@micropython.asm_xtensa +def a4(a2, a3) -> int: + addx4(a2, a2, a3) + + +@micropython.asm_xtensa +def a8(a2, a3) -> int: + addx8(a2, a2, a3) + + +@micropython.asm_xtensa +def s2(a2, a3) -> int: + subx2(a2, a2, a3) + + +@micropython.asm_xtensa +def s4(a2, a3) -> int: + subx4(a2, a2, a3) + + +@micropython.asm_xtensa +def s8(a2, a3) -> int: + subx8(a2, a2, a3) + + +for first, second in ((100, 100), (-100, 100), (-100, -100), (100, -100)): + print("a2", a2(first, second)) + print("a4", a4(first, second)) + print("a8", a8(first, second)) + print("s2", s2(first, second)) + print("s4", s4(first, second)) + print("s8", s8(first, second)) + + +@micropython.asm_xtensa +def f5(a2) -> int: + neg(a2, a2) + + +for value in (0, -100, 100): + print(f5(value)) + + +@micropython.asm_xtensa +def f6(): + movi(a2, 0x100) + movi(a3, 1) + add(a2, a2, a3) + addi(a2, a2, 1) + addi(a2, a2, -2) + sub(a2, a2, a3) + + +print(hex(f6())) + + +@micropython.asm_xtensa +def f7(): + movi(a2, 0x10FF) + movi(a3, 1) + and_(a4, a2, a3) + or_(a4, a4, a3) + movi(a3, 0x200) + xor(a2, a4, a3) + + +print(hex(f7())) + + +@micropython.asm_xtensa +def f8(a2, a3): + add_n(a2, a2, a3) + + +print(f8(100, 200)) + + +@micropython.asm_xtensa +def f9(a2): + addi_n(a2, a2, 1) + + +print(f9(100)) + + +@micropython.asm_xtensa +def f10(a2, a3) -> uint: + mull(a2, a2, a3) + + +print(hex(f10(0xC0000000, 2))) diff --git a/tests/inlineasm/xtensa/asmarith.py.exp b/tests/inlineasm/xtensa/asmarith.py.exp new file mode 100644 index 0000000000000..7aba46a27d9b5 --- /dev/null +++ b/tests/inlineasm/xtensa/asmarith.py.exp @@ -0,0 +1,40 @@ +10 +10 +0 +-32768 +-32767 is not a multiple of 256 +32512 +'addmi' integer 32513 isn't within range -32768..32512 +0 +a2 300 +a4 500 +a8 900 +s2 100 +s4 300 +s8 700 +a2 -100 +a4 -300 +a8 -700 +s2 -300 +s4 -500 +s8 -900 +a2 -300 +a4 -500 +a8 -900 +s2 -100 +s4 -300 +s8 -700 +a2 100 +a4 300 +a8 700 +s2 300 +s4 500 +s8 900 +0 +100 +-100 +0xff +0x201 +300 +101 +0x80000000 diff --git a/tests/inlineasm/xtensa/asmbranch.py b/tests/inlineasm/xtensa/asmbranch.py new file mode 100644 index 0000000000000..22bcd5a7c71a3 --- /dev/null +++ b/tests/inlineasm/xtensa/asmbranch.py @@ -0,0 +1,299 @@ +# test branch instructions + + +@micropython.asm_xtensa +def tball(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + ball(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tball(0xFFFFFFFF, 0xFFFFFFFF)) +print(tball(0xFFFEFFFF, 0xFFFFFFFF)) +print(tball(0x00000000, 0xFFFFFFFF)) +print(tball(0xFFFFFFFF, 0x01010101)) + + +@micropython.asm_xtensa +def tbany(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bany(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbany(0xFFFFFFFF, 0xFFFFFFFF)) +print(tbany(0xFFFEFFFF, 0xFFFFFFFF)) +print(tbany(0x00000000, 0xFFFFFFFF)) +print(tbany(0xFFFFFFFF, 0x01010101)) + + +@micropython.asm_xtensa +def tbbc(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bbc(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbbc(0xFFFFFFFF, 4)) +print(tbbc(0xFFFEFFFF, 16)) +print(tbbc(0x00000000, 1)) + + +BBCI_TEMPLATE = """ +@micropython.asm_xtensa +def tbbci(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bbci(a3, {}, end) + movi(a2, -1) + label(end) + +print(tbbci({})) +""" + + +for value, bit in ((0xFFFFFFFF, 4), (0xFFFEFFFF, 16), (0x00000000, 1)): + try: + exec(BBCI_TEMPLATE.format(bit, value)) + except SyntaxError as error: + print(error) + + +@micropython.asm_xtensa +def tbbs(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bbs(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbbs(0x00000000, 4)) +print(tbbs(0x00010000, 16)) +print(tbbs(0xFFFFFFFF, 1)) + + +BBSI_TEMPLATE = """ +@micropython.asm_xtensa +def tbbsi(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bbsi(a3, {}, end) + movi(a2, -1) + label(end) + +print(tbbsi({})) +""" + + +for value, bit in ((0x00000000, 4), (0x00010000, 16), (0xFFFFFFFF, 1)): + try: + exec(BBSI_TEMPLATE.format(bit, value)) + except SyntaxError as error: + print(error) + + +@micropython.asm_xtensa +def tbeq(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + beq(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbeq(0x00000000, 0x00000000)) +print(tbeq(0x00010000, 0x00000000)) +print(tbeq(0xFFFFFFFF, 0xFFFFFFFF)) + + +@micropython.asm_xtensa +def tbeqz(a2) -> int: + mov(a3, a2) + movi(a2, 0) + beqz(a3, end) + movi(a2, -1) + label(end) + + +print(tbeqz(0)) +print(tbeqz(0x12345678)) +print(tbeqz(-1)) + + +@micropython.asm_xtensa +def tbge(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bge(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbge(0x00000000, 0x00000000)) +print(tbge(0x00010000, 0x00000000)) +print(tbge(0xF0000000, 0xFFFFFFFF)) + + +@micropython.asm_xtensa +def tbgeu(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bgeu(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbgeu(0x00000000, 0x00000000)) +print(tbgeu(0x00010000, 0x00000000)) +print(tbgeu(0xF0000000, 0xFFFFFFFF)) +print(tbgeu(0xFFFFFFFF, 0xF0000000)) + + +@micropython.asm_xtensa +def tbgez(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bgez(a3, end) + movi(a2, -1) + label(end) + + +print(tbgez(0)) +print(tbgez(0x12345678)) +print(tbgez(-1)) + + +@micropython.asm_xtensa +def tblt(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + blt(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tblt(0x00000000, 0x00000000)) +print(tblt(0x00010000, 0x00000000)) +print(tblt(0xF0000000, 0xFFFFFFFF)) + + +@micropython.asm_xtensa +def tbltu(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bltu(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbltu(0x00000000, 0x00000000)) +print(tbltu(0x00010000, 0x00000000)) +print(tbltu(0xF0000000, 0xFFFFFFFF)) +print(tbltu(0xFFFFFFFF, 0xF0000000)) + + +@micropython.asm_xtensa +def tbltz(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bltz(a3, end) + movi(a2, -1) + label(end) + + +print(tbltz(0)) +print(tbltz(0x12345678)) +print(tbltz(-1)) + + +@micropython.asm_xtensa +def tbnall(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bnall(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbnall(0xFFFFFFFF, 0xFFFFFFFF)) +print(tbnall(0xFFFEFFFF, 0xFFFFFFFF)) +print(tbnall(0x00000000, 0xFFFFFFFF)) +print(tbnall(0xFFFFFFFF, 0x01010101)) + + +@micropython.asm_xtensa +def tbne(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bne(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbne(0x00000000, 0x00000000)) +print(tbne(0x00010000, 0x00000000)) +print(tbne(0xFFFFFFFF, 0xFFFFFFFF)) + + +@micropython.asm_xtensa +def tbnez(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bnez(a3, end) + movi(a2, -1) + label(end) + + +print(tbnez(0)) +print(tbnez(0x12345678)) +print(tbnez(-1)) + + +@micropython.asm_xtensa +def tbnone(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bnone(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbnone(0xFFFFFFFF, 0xFFFFFFFF)) +print(tbnone(0xFFFEFFFF, 0xFFFFFFFF)) +print(tbnone(0x00000000, 0xFFFFFFFF)) +print(tbnone(0x10101010, 0x01010101)) + + +@micropython.asm_xtensa +def tbeqz_n(a2) -> int: + mov(a3, a2) + movi(a2, 0) + beqz_n(a3, end) + movi(a2, -1) + label(end) + + +print(tbeqz_n(0)) +print(tbeqz_n(0x12345678)) +print(tbeqz_n(-1)) + + +@micropython.asm_xtensa +def tbnez_n(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bnez(a3, end) + movi(a2, -1) + label(end) + + +print(tbnez_n(0)) +print(tbnez_n(0x12345678)) +print(tbnez_n(-1)) diff --git a/tests/inlineasm/xtensa/asmbranch.py.exp b/tests/inlineasm/xtensa/asmbranch.py.exp new file mode 100644 index 0000000000000..319a4b435ef2f --- /dev/null +++ b/tests/inlineasm/xtensa/asmbranch.py.exp @@ -0,0 +1,66 @@ +0 +-1 +-1 +0 +0 +0 +-1 +0 +-1 +0 +0 +-1 +0 +0 +-1 +0 +0 +-1 +0 +0 +0 +-1 +0 +0 +-1 +-1 +0 +0 +-1 +0 +0 +-1 +0 +0 +0 +-1 +-1 +-1 +0 +-1 +-1 +0 +-1 +-1 +-1 +0 +-1 +0 +0 +-1 +-1 +0 +-1 +-1 +0 +0 +-1 +-1 +0 +0 +0 +-1 +-1 +-1 +0 +0 diff --git a/tests/inlineasm/xtensa/asmjump.py b/tests/inlineasm/xtensa/asmjump.py new file mode 100644 index 0000000000000..f41c9482319da --- /dev/null +++ b/tests/inlineasm/xtensa/asmjump.py @@ -0,0 +1,26 @@ +@micropython.asm_xtensa +def jump() -> int: + movi(a2, 0) + j(NEXT) + addi(a2, a2, 1) + j(DONE) + label(NEXT) + addi(a2, a2, 2) + label(DONE) + + +print(jump()) + + +@micropython.asm_xtensa +def jumpx() -> int: + call0(ENTRY) + label(ENTRY) + movi(a2, 0) + addi(a3, a0, 12) + jx(a3) + movi(a2, 1) + movi(a2, 2) + + +print(jumpx()) diff --git a/tests/inlineasm/xtensa/asmjump.py.exp b/tests/inlineasm/xtensa/asmjump.py.exp new file mode 100644 index 0000000000000..51993f072d583 --- /dev/null +++ b/tests/inlineasm/xtensa/asmjump.py.exp @@ -0,0 +1,2 @@ +2 +2 diff --git a/tests/inlineasm/xtensa/asmloadstore.py b/tests/inlineasm/xtensa/asmloadstore.py new file mode 100644 index 0000000000000..b185e30520cc9 --- /dev/null +++ b/tests/inlineasm/xtensa/asmloadstore.py @@ -0,0 +1,98 @@ +import array + +# On the 8266 the generated code gets put into the IRAM segment, which is only +# word-addressable. Therefore, to test byte and halfword load/store opcodes +# some memory must be reserved in the DRAM segment. + +BYTE_DATA = array.array("B", (0x11, 0x22, 0x33, 0x44)) +WORD_DATA = array.array("h", (100, 200, -100, -200)) +DWORD_DATA = array.array("i", (100_000, -200_000, 300_000, -400_000)) + + +@micropython.asm_xtensa +def tl32r() -> int: + nop() + j(CODE) + align(4) + label(DATA) + data(1, 1, 2, 3, 4, 5, 6, 7) + align(4) + label(CODE) + nop_n() + nop_n() + l32r(a2, DATA) + + +print(hex(tl32r())) + + +@micropython.asm_xtensa +def tl32i() -> uint: + call0(ENTRY) + label(ENTRY) + l32i(a2, a0, 0) + + +print(hex(tl32i())) + + +@micropython.asm_xtensa +def tl8ui(a2) -> uint: + mov(a3, a2) + l8ui(a2, a3, 1) + + +print(hex(tl8ui(BYTE_DATA))) + + +@micropython.asm_xtensa +def tl16ui(a2) -> uint: + mov(a3, a2) + l16ui(a2, a3, 2) + + +print(tl16ui(WORD_DATA)) + + +@micropython.asm_xtensa +def tl16si(a2) -> int: + mov(a3, a2) + l16si(a2, a3, 6) + + +print(tl16si(WORD_DATA)) + + +@micropython.asm_xtensa +def ts8i(a2, a3): + s8i(a3, a2, 1) + + +ts8i(BYTE_DATA, 0xFF) +print(BYTE_DATA) + + +@micropython.asm_xtensa +def ts16i(a2, a3): + s16i(a3, a2, 2) + + +ts16i(WORD_DATA, -123) +print(WORD_DATA) + + +@micropython.asm_xtensa +def ts32i(a2, a3) -> uint: + s32i(a3, a2, 4) + + +ts32i(DWORD_DATA, -123456) +print(DWORD_DATA) + + +@micropython.asm_xtensa +def tl32i_n(a2) -> uint: + l32i_n(a2, a2, 8) + + +print(tl32i_n(DWORD_DATA)) diff --git a/tests/inlineasm/xtensa/asmloadstore.py.exp b/tests/inlineasm/xtensa/asmloadstore.py.exp new file mode 100644 index 0000000000000..e6672df6f8132 --- /dev/null +++ b/tests/inlineasm/xtensa/asmloadstore.py.exp @@ -0,0 +1,9 @@ +0x4030201 +0xf8002022 +0x22 +200 +-200 +array('B', [17, 255, 51, 68]) +array('h', [100, -123, -100, -200]) +array('i', [100000, -123456, 300000, -400000]) +300000 diff --git a/tests/inlineasm/xtensa/asmmisc.py b/tests/inlineasm/xtensa/asmmisc.py new file mode 100644 index 0000000000000..271ab836625dc --- /dev/null +++ b/tests/inlineasm/xtensa/asmmisc.py @@ -0,0 +1,25 @@ +@micropython.asm_xtensa +def tnop(a2, a3, a4, a5): + nop() + + +out2 = tnop(0x100, 0x200, 0x300, 0x400) +print(out2 == 0x100) + + +@micropython.asm_xtensa +def tnop_n(a2, a3, a4, a5): + nop_n() + + +out2 = tnop_n(0x100, 0x200, 0x300, 0x400) +print(out2 == 0x100) + + +@micropython.asm_xtensa +def tmov_n(a2, a3): + mov_n(a4, a3) + add(a2, a4, a3) + + +print(tmov_n(0, 1)) diff --git a/tests/inlineasm/xtensa/asmmisc.py.exp b/tests/inlineasm/xtensa/asmmisc.py.exp new file mode 100644 index 0000000000000..eefaa35daf0f1 --- /dev/null +++ b/tests/inlineasm/xtensa/asmmisc.py.exp @@ -0,0 +1,3 @@ +True +True +2 diff --git a/tests/inlineasm/xtensa/asmshift.py b/tests/inlineasm/xtensa/asmshift.py new file mode 100644 index 0000000000000..271ca1ccd494c --- /dev/null +++ b/tests/inlineasm/xtensa/asmshift.py @@ -0,0 +1,137 @@ +@micropython.asm_xtensa +def lsl1(a2): + slli(a2, a2, 1) + + +print(hex(lsl1(0x123))) + + +@micropython.asm_xtensa +def lsl23(a2): + slli(a2, a2, 23) + + +print(hex(lsl23(1))) + + +@micropython.asm_xtensa +def lsr1(a2): + srli(a2, a2, 1) + + +print(hex(lsr1(0x123))) + + +@micropython.asm_xtensa +def lsr15(a2): + srli(a2, a2, 15) + + +print(hex(lsr15(0x80000000))) + + +@micropython.asm_xtensa +def asr1(a2): + srai(a2, a2, 1) + + +print(hex(asr1(0x123))) + + +@micropython.asm_xtensa +def asr31(a2): + srai(a2, a2, 31) + + +print(hex(asr31(0x80000000))) + + +@micropython.asm_xtensa +def lsl1r(a2): + movi(a3, 1) + ssl(a3) + sll(a2, a2) + + +print(hex(lsl1r(0x123))) + + +@micropython.asm_xtensa +def lsr1r(a2): + movi(a3, 1) + ssr(a3) + srl(a2, a2) + + +print(hex(lsr1r(0x123))) + + +@micropython.asm_xtensa +def asr1r(a2): + movi(a3, 1) + ssr(a3) + sra(a2, a2) + + +print(hex(asr1r(0x123))) + + +@micropython.asm_xtensa +def sll9(a2): + ssai(9) + sll(a2, a2) + + +print(hex(sll9(1))) + + +@micropython.asm_xtensa +def srlr(a2, a3): + ssa8l(a3) + srl(a2, a2) + + +print(hex(srlr(0x12340000, 2))) + + +@micropython.asm_xtensa +def sllr(a2, a3): + ssa8b(a3) + sll(a2, a2) + + +print(hex(sllr(0x1234, 2))) + + +@micropython.asm_xtensa +def srcr(a2, a3, a4): + ssr(a4) + src(a2, a2, a3) + + +print(hex(srcr(0x00000001, 0x80000000, 2))) + + +@micropython.asm_xtensa +def srai24(a2): + srai(a2, a2, 24) + + +print(hex(srai24(0x12345678))) + + +@micropython.asm_xtensa +def nsar(a2, a3): + nsa(a2, a3) + + +print(nsar(0x12345678, 0)) +print(nsar(0x12345678, -1)) + + +@micropython.asm_xtensa +def nsaur(a2, a3): + nsau(a2, a3) + + +print(nsaur(0x12345678, 0)) diff --git a/tests/inlineasm/xtensa/asmshift.py.exp b/tests/inlineasm/xtensa/asmshift.py.exp new file mode 100644 index 0000000000000..3e2bb3b4aefa9 --- /dev/null +++ b/tests/inlineasm/xtensa/asmshift.py.exp @@ -0,0 +1,17 @@ +0x246 +0x800000 +0x91 +0x10000 +0x91 +-0x1 +0x246 +0x91 +0x91 +0x800000 +0x1234 +0x12340000 +0x60000000 +0x12 +31 +31 +32 diff --git a/tests/internal_bench/class_create-0-empty.py b/tests/internal_bench/class_create-0-empty.py new file mode 100644 index 0000000000000..1fd8ccd925715 --- /dev/null +++ b/tests/internal_bench/class_create-0-empty.py @@ -0,0 +1,11 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-1-slots.py b/tests/internal_bench/class_create-1-slots.py new file mode 100644 index 0000000000000..9b3e4b9570da2 --- /dev/null +++ b/tests/internal_bench/class_create-1-slots.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + l = ["x"] + for i in range(num // 40): + + class X: + __slots__ = l + + +bench.run(test) diff --git a/tests/internal_bench/class_create-1.1-slots5.py b/tests/internal_bench/class_create-1.1-slots5.py new file mode 100644 index 0000000000000..ccac77dec9daa --- /dev/null +++ b/tests/internal_bench/class_create-1.1-slots5.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + l = ["a", "b", "c", "d", "x"] + for i in range(num // 40): + + class X: + __slots__ = l + + +bench.run(test) diff --git a/tests/internal_bench/class_create-2-classattr.py b/tests/internal_bench/class_create-2-classattr.py new file mode 100644 index 0000000000000..049a7dab170c1 --- /dev/null +++ b/tests/internal_bench/class_create-2-classattr.py @@ -0,0 +1,11 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + x = 1 + + +bench.run(test) diff --git a/tests/internal_bench/class_create-2.1-classattr5.py b/tests/internal_bench/class_create-2.1-classattr5.py new file mode 100644 index 0000000000000..5051e7dcca70d --- /dev/null +++ b/tests/internal_bench/class_create-2.1-classattr5.py @@ -0,0 +1,15 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + a = 0 + b = 0 + c = 0 + d = 0 + x = 1 + + +bench.run(test) diff --git a/tests/internal_bench/class_create-2.3-classattr5objs.py b/tests/internal_bench/class_create-2.3-classattr5objs.py new file mode 100644 index 0000000000000..74540865dcd14 --- /dev/null +++ b/tests/internal_bench/class_create-2.3-classattr5objs.py @@ -0,0 +1,20 @@ +import bench + + +class Class: + pass + + +def test(num): + instance = Class() + for i in range(num // 40): + + class X: + a = instance + b = instance + c = instance + d = instance + x = instance + + +bench.run(test) diff --git a/tests/internal_bench/class_create-3-instancemethod.py b/tests/internal_bench/class_create-3-instancemethod.py new file mode 100644 index 0000000000000..e8c201cb2c3c6 --- /dev/null +++ b/tests/internal_bench/class_create-3-instancemethod.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + def x(self): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-4-classmethod.py b/tests/internal_bench/class_create-4-classmethod.py new file mode 100644 index 0000000000000..f34962bc67117 --- /dev/null +++ b/tests/internal_bench/class_create-4-classmethod.py @@ -0,0 +1,13 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + @classmethod + def x(cls): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-4.1-classmethod_implicit.py b/tests/internal_bench/class_create-4.1-classmethod_implicit.py new file mode 100644 index 0000000000000..f2d1fcfd18821 --- /dev/null +++ b/tests/internal_bench/class_create-4.1-classmethod_implicit.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + def __new__(cls): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-5-staticmethod.py b/tests/internal_bench/class_create-5-staticmethod.py new file mode 100644 index 0000000000000..0633556667544 --- /dev/null +++ b/tests/internal_bench/class_create-5-staticmethod.py @@ -0,0 +1,13 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + @staticmethod + def x(): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-6-getattribute.py b/tests/internal_bench/class_create-6-getattribute.py new file mode 100644 index 0000000000000..10a4fe7ce8d9f --- /dev/null +++ b/tests/internal_bench/class_create-6-getattribute.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + def __getattribute__(self, name): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-6.1-getattr.py b/tests/internal_bench/class_create-6.1-getattr.py new file mode 100644 index 0000000000000..b4b9ba2f5525b --- /dev/null +++ b/tests/internal_bench/class_create-6.1-getattr.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + def __getattr__(self, name): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-6.2-property.py b/tests/internal_bench/class_create-6.2-property.py new file mode 100644 index 0000000000000..cf847b6dc9c9f --- /dev/null +++ b/tests/internal_bench/class_create-6.2-property.py @@ -0,0 +1,13 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + @property + def x(self): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-6.3-descriptor.py b/tests/internal_bench/class_create-6.3-descriptor.py new file mode 100644 index 0000000000000..7b0a635726380 --- /dev/null +++ b/tests/internal_bench/class_create-6.3-descriptor.py @@ -0,0 +1,17 @@ +import bench + + +class D: + def __get__(self, instance, owner=None): + pass + + +def test(num): + descriptor = D() + for i in range(num // 40): + + class X: + x = descriptor + + +bench.run(test) diff --git a/tests/internal_bench/class_create-7-inherit.py b/tests/internal_bench/class_create-7-inherit.py new file mode 100644 index 0000000000000..f48fb215e0ab8 --- /dev/null +++ b/tests/internal_bench/class_create-7-inherit.py @@ -0,0 +1,14 @@ +import bench + + +def test(num): + class B: + pass + + for i in range(num // 40): + + class X(B): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-7.1-inherit_initsubclass.py b/tests/internal_bench/class_create-7.1-inherit_initsubclass.py new file mode 100644 index 0000000000000..0660fa86258fe --- /dev/null +++ b/tests/internal_bench/class_create-7.1-inherit_initsubclass.py @@ -0,0 +1,16 @@ +import bench + + +def test(num): + class B: + @classmethod + def __init_subclass__(cls): + pass + + for i in range(num // 40): + + class X(B): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-8-metaclass_setname.py b/tests/internal_bench/class_create-8-metaclass_setname.py new file mode 100644 index 0000000000000..e4515b54279d1 --- /dev/null +++ b/tests/internal_bench/class_create-8-metaclass_setname.py @@ -0,0 +1,17 @@ +import bench + + +class D: + def __set_name__(self, owner, name): + pass + + +def test(num): + descriptor = D() + for i in range(num // 40): + + class X: + x = descriptor + + +bench.run(test) diff --git a/tests/internal_bench/class_create-8.1-metaclass_setname5.py b/tests/internal_bench/class_create-8.1-metaclass_setname5.py new file mode 100644 index 0000000000000..5daa3f8471bca --- /dev/null +++ b/tests/internal_bench/class_create-8.1-metaclass_setname5.py @@ -0,0 +1,21 @@ +import bench + + +class D: + def __set_name__(self, owner, name): + pass + + +def test(num): + descriptor = D() + for i in range(num // 40): + + class X: + a = descriptor + b = descriptor + c = descriptor + d = descriptor + x = descriptor + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-0-object.py b/tests/internal_bench/class_instance-0-object.py new file mode 100644 index 0000000000000..401c8ea7e3cc2 --- /dev/null +++ b/tests/internal_bench/class_instance-0-object.py @@ -0,0 +1,11 @@ +import bench + +X = object + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-0.1-object-gc.py b/tests/internal_bench/class_instance-0.1-object-gc.py new file mode 100644 index 0000000000000..7c475963a8e81 --- /dev/null +++ b/tests/internal_bench/class_instance-0.1-object-gc.py @@ -0,0 +1,13 @@ +import bench +import gc + +X = object + + +def test(num): + for i in range(num // 5): + x = X() + gc.collect() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-1-empty.py b/tests/internal_bench/class_instance-1-empty.py new file mode 100644 index 0000000000000..617d47a86e92b --- /dev/null +++ b/tests/internal_bench/class_instance-1-empty.py @@ -0,0 +1,13 @@ +import bench + + +class X: + pass + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-1.1-classattr.py b/tests/internal_bench/class_instance-1.1-classattr.py new file mode 100644 index 0000000000000..4e667533d4aa1 --- /dev/null +++ b/tests/internal_bench/class_instance-1.1-classattr.py @@ -0,0 +1,13 @@ +import bench + + +class X: + x = 0 + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-1.2-func.py b/tests/internal_bench/class_instance-1.2-func.py new file mode 100644 index 0000000000000..21bf7a1ac48fc --- /dev/null +++ b/tests/internal_bench/class_instance-1.2-func.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def f(self): + pass + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-1.3-empty-gc.py b/tests/internal_bench/class_instance-1.3-empty-gc.py new file mode 100644 index 0000000000000..a5108ef8e8156 --- /dev/null +++ b/tests/internal_bench/class_instance-1.3-empty-gc.py @@ -0,0 +1,15 @@ +import bench +import gc + + +class X: + pass + + +def test(num): + for i in range(num // 5): + x = X() + gc.collect() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-2-init.py b/tests/internal_bench/class_instance-2-init.py new file mode 100644 index 0000000000000..86619d3154840 --- /dev/null +++ b/tests/internal_bench/class_instance-2-init.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def __init__(self): + pass + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-2.1-init_super.py b/tests/internal_bench/class_instance-2.1-init_super.py new file mode 100644 index 0000000000000..38bca5fef877a --- /dev/null +++ b/tests/internal_bench/class_instance-2.1-init_super.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def __init__(self): + return super().__init__() + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-2.2-new.py b/tests/internal_bench/class_instance-2.2-new.py new file mode 100644 index 0000000000000..dc5e78ea5ef98 --- /dev/null +++ b/tests/internal_bench/class_instance-2.2-new.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def __new__(cls): + return super().__new__(cls) + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-3-del.py b/tests/internal_bench/class_instance-3-del.py new file mode 100644 index 0000000000000..af700f72a94a8 --- /dev/null +++ b/tests/internal_bench/class_instance-3-del.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def __del__(self): + pass + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-3.1-del-gc.py b/tests/internal_bench/class_instance-3.1-del-gc.py new file mode 100644 index 0000000000000..311c71c3571b0 --- /dev/null +++ b/tests/internal_bench/class_instance-3.1-del-gc.py @@ -0,0 +1,16 @@ +import bench +import gc + + +class X: + def __del__(self): + pass + + +def test(num): + for i in range(num // 5): + x = X() + gc.collect() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-4-slots.py b/tests/internal_bench/class_instance-4-slots.py new file mode 100644 index 0000000000000..51b067fedf0b7 --- /dev/null +++ b/tests/internal_bench/class_instance-4-slots.py @@ -0,0 +1,13 @@ +import bench + + +class X: + __slots__ = ["x"] + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-4.1-slots5.py b/tests/internal_bench/class_instance-4.1-slots5.py new file mode 100644 index 0000000000000..8f5c2ecb456d8 --- /dev/null +++ b/tests/internal_bench/class_instance-4.1-slots5.py @@ -0,0 +1,13 @@ +import bench + + +class X: + __slots__ = ["a", "b", "c", "d", "x"] + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/var-6.2-instance-speciallookup.py b/tests/internal_bench/var-6.2-instance-speciallookup.py new file mode 100644 index 0000000000000..fee12b2f930c3 --- /dev/null +++ b/tests/internal_bench/var-6.2-instance-speciallookup.py @@ -0,0 +1,19 @@ +import bench + + +class Foo: + def __init__(self): + self.num = 20000000 + + def __delattr__(self, name): # just trigger the 'special lookups' flag on the class + pass + + +def test(num): + o = Foo() + i = 0 + while i < o.num: + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-6.3-instance-property.py b/tests/internal_bench/var-6.3-instance-property.py new file mode 100644 index 0000000000000..b4426ef7928e1 --- /dev/null +++ b/tests/internal_bench/var-6.3-instance-property.py @@ -0,0 +1,17 @@ +import bench + + +class Foo: + @property + def num(self): + return 20000000 + + +def test(num): + o = Foo() + i = 0 + while i < o.num: + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-6.4-instance-descriptor.py b/tests/internal_bench/var-6.4-instance-descriptor.py new file mode 100644 index 0000000000000..b4df69f878f10 --- /dev/null +++ b/tests/internal_bench/var-6.4-instance-descriptor.py @@ -0,0 +1,20 @@ +import bench + + +class Descriptor: + def __get__(self, instance, owner=None): + return 20000000 + + +class Foo: + num = Descriptor() + + +def test(num): + o = Foo() + i = 0 + while i < o.num: + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-6.5-instance-getattr.py b/tests/internal_bench/var-6.5-instance-getattr.py new file mode 100644 index 0000000000000..3b2ef6721105f --- /dev/null +++ b/tests/internal_bench/var-6.5-instance-getattr.py @@ -0,0 +1,16 @@ +import bench + + +class Foo: + def __getattr__(self, name): + return 20000000 + + +def test(num): + o = Foo() + i = 0 + while i < o.num: + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-6.6-instance-builtin_ordered.py b/tests/internal_bench/var-6.6-instance-builtin_ordered.py new file mode 100644 index 0000000000000..02d7525230192 --- /dev/null +++ b/tests/internal_bench/var-6.6-instance-builtin_ordered.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + i = 0 + o = set() # object with largest rom-frozen ordered locals_dict + n = "__contains__" # last element in that dict for longest lookup + while i < num: + i += hasattr(o, n) # True, converts to 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9-getattr.py b/tests/internal_bench/var-9-getattr.py new file mode 100644 index 0000000000000..69d2bfed2e0ac --- /dev/null +++ b/tests/internal_bench/var-9-getattr.py @@ -0,0 +1,16 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + o.num = num + i = 0 + while i < getattr(o, "num", num): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.1-getattr_default.py b/tests/internal_bench/var-9.1-getattr_default.py new file mode 100644 index 0000000000000..e803d39b32667 --- /dev/null +++ b/tests/internal_bench/var-9.1-getattr_default.py @@ -0,0 +1,15 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + i = 0 + while i < getattr(o, "num", num): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.2-getattr_default_special.py b/tests/internal_bench/var-9.2-getattr_default_special.py new file mode 100644 index 0000000000000..c48ec0742cff6 --- /dev/null +++ b/tests/internal_bench/var-9.2-getattr_default_special.py @@ -0,0 +1,16 @@ +import bench + + +class Foo: + def __delattr__(self, name): # just trigger the 'special lookups' flag on the class + pass + + +def test(num): + o = Foo() + i = 0 + while i < getattr(o, "num", num): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.3-except_ok.py b/tests/internal_bench/var-9.3-except_ok.py new file mode 100644 index 0000000000000..efc1a8f858cd0 --- /dev/null +++ b/tests/internal_bench/var-9.3-except_ok.py @@ -0,0 +1,23 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + o.num = num + + def get(): + try: + return o.num + except AttributeError: + return num + + i = 0 + while i < get(): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.4-except_selfinduced.py b/tests/internal_bench/var-9.4-except_selfinduced.py new file mode 100644 index 0000000000000..544609ca4b6b3 --- /dev/null +++ b/tests/internal_bench/var-9.4-except_selfinduced.py @@ -0,0 +1,22 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + + def get(): + try: + raise AttributeError + except AttributeError: + return num + + i = 0 + while i < get(): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.5-except_error.py b/tests/internal_bench/var-9.5-except_error.py new file mode 100644 index 0000000000000..caf83fa46ab3d --- /dev/null +++ b/tests/internal_bench/var-9.5-except_error.py @@ -0,0 +1,22 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + + def get(): + try: + return o.num + except AttributeError: + return num + + i = 0 + while i < get(): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.6-except_error_special.py b/tests/internal_bench/var-9.6-except_error_special.py new file mode 100644 index 0000000000000..8bc395b4d7ef8 --- /dev/null +++ b/tests/internal_bench/var-9.6-except_error_special.py @@ -0,0 +1,23 @@ +import bench + + +class Foo: + def __delattr__(self, name): # just trigger the 'special lookups' flag on the class + pass + + +def test(num): + o = Foo() + + def get(): + try: + return o.num + except AttributeError: + return num + + i = 0 + while i < get(): + i += 1 + + +bench.run(test) diff --git a/tests/io/builtin_print_file.py b/tests/io/builtin_print_file.py index 822356a6cc305..e00f58635a9d2 100644 --- a/tests/io/builtin_print_file.py +++ b/tests/io/builtin_print_file.py @@ -13,5 +13,5 @@ try: print(file=1) -except (AttributeError, OSError): # CPython and uPy differ in error message +except (AttributeError, OSError): # CPython and MicroPython differ in error message print("Error") diff --git a/tests/io/file_seek.py b/tests/io/file_seek.py index 3990df8409051..b9f9593786fe6 100644 --- a/tests/io/file_seek.py +++ b/tests/io/file_seek.py @@ -30,5 +30,5 @@ try: f.seek(1) except (OSError, ValueError): - # CPy raises ValueError, uPy raises OSError + # CPy raises ValueError, MPy raises OSError print("OSError or ValueError") diff --git a/tests/micropython/builtin_execfile.py b/tests/micropython/builtin_execfile.py index 75a867bb94023..4fd4d66d4ee25 100644 --- a/tests/micropython/builtin_execfile.py +++ b/tests/micropython/builtin_execfile.py @@ -75,3 +75,24 @@ def open(self, file, mode): # Unmount the VFS object. vfs.umount(fs) + + +class EvilFilesystem: + def mount(self, readonly, mkfs): + print("mount", readonly, mkfs) + + def umount(self): + print("umount") + + def open(self, file, mode): + return None + + +fs = EvilFilesystem() +vfs.mount(fs, "/test_mnt") +try: + execfile("/test_mnt/test.py") + print("ExecFile succeeded") +except OSError: + print("OSError") +vfs.umount(fs) diff --git a/tests/micropython/builtin_execfile.py.exp b/tests/micropython/builtin_execfile.py.exp index 49703d570763d..d93dee547b6ea 100644 --- a/tests/micropython/builtin_execfile.py.exp +++ b/tests/micropython/builtin_execfile.py.exp @@ -5,3 +5,6 @@ open /test.py rb 123 TypeError umount +mount False False +OSError +umount diff --git a/tests/micropython/const_error.py b/tests/micropython/const_error.py index d35be530a7c8d..950360e4dc782 100644 --- a/tests/micropython/const_error.py +++ b/tests/micropython/const_error.py @@ -18,8 +18,6 @@ def test_syntax(code): # these operations are not supported within const test_syntax("A = const(1 @ 2)") -test_syntax("A = const(1 / 2)") -test_syntax("A = const(1 ** -2)") test_syntax("A = const(1 << -2)") test_syntax("A = const(1 >> -2)") test_syntax("A = const(1 % 0)") diff --git a/tests/micropython/const_error.py.exp b/tests/micropython/const_error.py.exp index 3edc3efe9c3e9..bef69eb32ea7d 100644 --- a/tests/micropython/const_error.py.exp +++ b/tests/micropython/const_error.py.exp @@ -5,5 +5,3 @@ SyntaxError SyntaxError SyntaxError SyntaxError -SyntaxError -SyntaxError diff --git a/tests/micropython/const_float.py b/tests/micropython/const_float.py new file mode 100644 index 0000000000000..c3a0df0276bf8 --- /dev/null +++ b/tests/micropython/const_float.py @@ -0,0 +1,23 @@ +# test constant optimisation, with consts that are floats + +from micropython import const + +# check we can make consts from floats +F1 = const(2.5) +F2 = const(-0.3) +print(type(F1), F1) +print(type(F2), F2) + +# check arithmetic with floats +F3 = const(F1 + F2) +F4 = const(F1**2) +print(F3, F4) + +# check int operations with float results +F5 = const(1 / 2) +F6 = const(2**-2) +print(F5, F6) + +# note: we also test float expression folding when +# we're compiling test cases in tests/float, as +# many expressions are resolved at compile time. diff --git a/tests/micropython/const_float.py.exp b/tests/micropython/const_float.py.exp new file mode 100644 index 0000000000000..17a86a6d936c2 --- /dev/null +++ b/tests/micropython/const_float.py.exp @@ -0,0 +1,4 @@ + 2.5 + -0.3 +2.2 6.25 +0.5 0.25 diff --git a/tests/micropython/const_math.py b/tests/micropython/const_math.py new file mode 100644 index 0000000000000..7ee5edc6d3240 --- /dev/null +++ b/tests/micropython/const_math.py @@ -0,0 +1,18 @@ +# Test expressions based on math module constants +try: + import math +except ImportError: + print("SKIP") + raise SystemExit + +from micropython import const + +# check that we can make consts from math constants +# (skip if the target has MICROPY_COMP_MODULE_CONST disabled) +try: + exec("two_pi = const(2.0 * math.pi)") +except SyntaxError: + print("SKIP") + raise SystemExit + +print(math.cos(two_pi)) diff --git a/tests/micropython/const_math.py.exp b/tests/micropython/const_math.py.exp new file mode 100644 index 0000000000000..d3827e75a5cad --- /dev/null +++ b/tests/micropython/const_math.py.exp @@ -0,0 +1 @@ +1.0 diff --git a/tests/micropython/decorator_error.py b/tests/micropython/decorator_error.py index 94772ac1e5fef..b5eae65b29209 100644 --- a/tests/micropython/decorator_error.py +++ b/tests/micropython/decorator_error.py @@ -1,4 +1,4 @@ -# test syntax errors for uPy-specific decorators +# test syntax errors for MicroPython-specific decorators def test_syntax(code): diff --git a/tests/micropython/emg_exc.py b/tests/micropython/emg_exc.py index a74757d028ff2..cf918b3dc8ac5 100644 --- a/tests/micropython/emg_exc.py +++ b/tests/micropython/emg_exc.py @@ -1,10 +1,9 @@ # test that emergency exceptions work -import micropython import sys try: - import io + import io, micropython except ImportError: print("SKIP") raise SystemExit diff --git a/tests/micropython/emg_exc.py.native.exp b/tests/micropython/emg_exc.py.native.exp new file mode 100644 index 0000000000000..9677c526a9cc8 --- /dev/null +++ b/tests/micropython/emg_exc.py.native.exp @@ -0,0 +1,2 @@ +ValueError: 1 + diff --git a/tests/micropython/extreme_exc.py b/tests/micropython/extreme_exc.py index ad819e408fd82..17323b566d3b5 100644 --- a/tests/micropython/extreme_exc.py +++ b/tests/micropython/extreme_exc.py @@ -1,6 +1,10 @@ # test some extreme cases of allocating exceptions and tracebacks -import micropython +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit # Check for stackless build, which can't call functions without # allocating a frame on the heap. diff --git a/tests/micropython/heap_lock.py b/tests/micropython/heap_lock.py index f2892a6dc581e..209a3143461a5 100644 --- a/tests/micropython/heap_lock.py +++ b/tests/micropython/heap_lock.py @@ -1,6 +1,10 @@ # check that heap_lock/heap_unlock work as expected -import micropython +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit l = [] l2 = list(range(100)) diff --git a/tests/micropython/heap_locked.py b/tests/micropython/heap_locked.py index d9e5b5d4090af..d9d99493dd6b7 100644 --- a/tests/micropython/heap_locked.py +++ b/tests/micropython/heap_locked.py @@ -1,8 +1,10 @@ # test micropython.heap_locked() -import micropython +try: + import micropython -if not hasattr(micropython, "heap_locked"): + micropython.heap_locked +except (AttributeError, ImportError): print("SKIP") raise SystemExit diff --git a/tests/micropython/heapalloc.py b/tests/micropython/heapalloc.py index e19f8d0255deb..010bf878a07b0 100644 --- a/tests/micropython/heapalloc.py +++ b/tests/micropython/heapalloc.py @@ -1,6 +1,10 @@ # check that we can do certain things without allocating heap memory -import micropython +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit # Check for stackless build, which can't call functions without # allocating a frame on heap. diff --git a/tests/micropython/heapalloc_exc_compressed.py b/tests/micropython/heapalloc_exc_compressed.py index aa071d641c21c..96fe3ca4f7e00 100644 --- a/tests/micropython/heapalloc_exc_compressed.py +++ b/tests/micropython/heapalloc_exc_compressed.py @@ -1,4 +1,10 @@ -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # Tests both code paths for built-in exception raising. # mp_obj_new_exception_msg_varg (exception requires decompression at raise-time to format) diff --git a/tests/micropython/heapalloc_exc_compressed_emg_exc.py b/tests/micropython/heapalloc_exc_compressed_emg_exc.py index 48ce9dd69e6e2..31d937b8f9345 100644 --- a/tests/micropython/heapalloc_exc_compressed_emg_exc.py +++ b/tests/micropython/heapalloc_exc_compressed_emg_exc.py @@ -1,4 +1,10 @@ -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # Does the full test from heapalloc_exc_compressed.py but while the heap is # locked (this can only work when the emergency exception buf is enabled). diff --git a/tests/micropython/heapalloc_exc_raise.py b/tests/micropython/heapalloc_exc_raise.py index 99810e0075064..917ccf42f1e16 100644 --- a/tests/micropython/heapalloc_exc_raise.py +++ b/tests/micropython/heapalloc_exc_raise.py @@ -1,6 +1,12 @@ # Test that we can raise and catch (preallocated) exception # without memory allocation. -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit e = ValueError("error") diff --git a/tests/micropython/heapalloc_fail_bytearray.py b/tests/micropython/heapalloc_fail_bytearray.py index 1bf7ddd600b93..9ee73d1c583f2 100644 --- a/tests/micropython/heapalloc_fail_bytearray.py +++ b/tests/micropython/heapalloc_fail_bytearray.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with bytearray -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit class GetSlice: diff --git a/tests/micropython/heapalloc_fail_dict.py b/tests/micropython/heapalloc_fail_dict.py index ce2d158bd09c6..04c79183578ff 100644 --- a/tests/micropython/heapalloc_fail_dict.py +++ b/tests/micropython/heapalloc_fail_dict.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with dict -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # create dict x = 1 diff --git a/tests/micropython/heapalloc_fail_list.py b/tests/micropython/heapalloc_fail_list.py index 9a2e9a555f3e2..7afb6dc1019b3 100644 --- a/tests/micropython/heapalloc_fail_list.py +++ b/tests/micropython/heapalloc_fail_list.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with list -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit class GetSlice: diff --git a/tests/micropython/heapalloc_fail_memoryview.py b/tests/micropython/heapalloc_fail_memoryview.py index da2d1abffa634..17e3e1262472c 100644 --- a/tests/micropython/heapalloc_fail_memoryview.py +++ b/tests/micropython/heapalloc_fail_memoryview.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with memoryview -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit class GetSlice: diff --git a/tests/micropython/heapalloc_fail_set.py b/tests/micropython/heapalloc_fail_set.py index 3c347660ad763..0c4d85eef62a8 100644 --- a/tests/micropython/heapalloc_fail_set.py +++ b/tests/micropython/heapalloc_fail_set.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with set -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # create set x = 1 diff --git a/tests/micropython/heapalloc_fail_tuple.py b/tests/micropython/heapalloc_fail_tuple.py index de79385e3e3cb..11718a8107b3a 100644 --- a/tests/micropython/heapalloc_fail_tuple.py +++ b/tests/micropython/heapalloc_fail_tuple.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with tuple -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # create tuple x = 1 diff --git a/tests/micropython/heapalloc_inst_call.py b/tests/micropython/heapalloc_inst_call.py index 14d8826bf06ce..f78aa3cf87798 100644 --- a/tests/micropython/heapalloc_inst_call.py +++ b/tests/micropython/heapalloc_inst_call.py @@ -1,6 +1,13 @@ # Test that calling clazz.__call__() with up to at least 3 arguments # doesn't require heap allocation. -import micropython + +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit class Foo0: diff --git a/tests/micropython/heapalloc_int_from_bytes.py b/tests/micropython/heapalloc_int_from_bytes.py index 5fe50443ae335..3310ea95d1472 100644 --- a/tests/micropython/heapalloc_int_from_bytes.py +++ b/tests/micropython/heapalloc_int_from_bytes.py @@ -1,6 +1,13 @@ # Test that int.from_bytes() for small number of bytes generates # small int. -import micropython + +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit micropython.heap_lock() print(int.from_bytes(b"1", "little")) diff --git a/tests/micropython/heapalloc_slice.py b/tests/micropython/heapalloc_slice.py new file mode 100644 index 0000000000000..62d96595c719e --- /dev/null +++ b/tests/micropython/heapalloc_slice.py @@ -0,0 +1,18 @@ +# slice operations that don't require allocation +try: + from micropython import heap_lock, heap_unlock +except (ImportError, AttributeError): + heap_lock = heap_unlock = lambda: 0 + +b = bytearray(range(10)) + +m = memoryview(b) + +heap_lock() + +b[3:5] = b"aa" +m[5:7] = b"bb" + +heap_unlock() + +print(b) diff --git a/tests/micropython/heapalloc_str.py b/tests/micropython/heapalloc_str.py index 39aa56ccd7834..6372df5d37b13 100644 --- a/tests/micropython/heapalloc_str.py +++ b/tests/micropython/heapalloc_str.py @@ -1,5 +1,12 @@ # String operations which don't require allocation -import micropython + +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit micropython.heap_lock() diff --git a/tests/micropython/heapalloc_super.py b/tests/micropython/heapalloc_super.py index 51afae3d83d06..6839eee330acd 100644 --- a/tests/micropython/heapalloc_super.py +++ b/tests/micropython/heapalloc_super.py @@ -1,5 +1,12 @@ # test super() operations which don't require allocation -import micropython + +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # Check for stackless build, which can't call functions without # allocating a frame on heap. diff --git a/tests/micropython/heapalloc_traceback.py.native.exp b/tests/micropython/heapalloc_traceback.py.native.exp new file mode 100644 index 0000000000000..d6ac26aa829e1 --- /dev/null +++ b/tests/micropython/heapalloc_traceback.py.native.exp @@ -0,0 +1,3 @@ +StopIteration +StopIteration: + diff --git a/tests/micropython/heapalloc_yield_from.py b/tests/micropython/heapalloc_yield_from.py index 950717189087d..9d4f8c6940f16 100644 --- a/tests/micropython/heapalloc_yield_from.py +++ b/tests/micropython/heapalloc_yield_from.py @@ -1,6 +1,12 @@ # Check that yield-from can work without heap allocation -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # Yielding from a function generator diff --git a/tests/micropython/import_mpy_native.py b/tests/micropython/import_mpy_native.py index ad63e025db2c8..59181b203bba9 100644 --- a/tests/micropython/import_mpy_native.py +++ b/tests/micropython/import_mpy_native.py @@ -54,12 +54,13 @@ def open(self, path, mode): # these are the test .mpy files # CIRCUITPY-CHANGE -valid_header = bytes([ord("C"), 6, mpy_arch, 31]) +small_int_bits = 31 +valid_header = bytes([77, 6, (mpy_arch & 0x3F), small_int_bits]) # fmt: off user_files = { # bad architecture (mpy_arch needed for sub-version) # CIRCUITPY-CHANGE - '/mod0.mpy': bytes([ord('C'), 6, 0xfc | mpy_arch, 31]), + '/mod0.mpy': bytes([ord('C'), 6, 0xfc | (mpy_arch & 3), small_int_bits]), # test loading of viper and asm '/mod1.mpy': valid_header + ( diff --git a/tests/micropython/import_mpy_native_gc.py b/tests/micropython/import_mpy_native_gc.py index 0d5b79f0a8dd4..5159cc9e9f9cf 100644 --- a/tests/micropython/import_mpy_native_gc.py +++ b/tests/micropython/import_mpy_native_gc.py @@ -57,9 +57,13 @@ def open(self, path, mode): # CIRCUITPY-CHANGE: 'C' instead of 'M' mpy marker. features0_file_contents = { # -march=x64 - 0x806: b'C\x06\n\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x8a\x02\xe93\x00\x00\x00\xf3\x0f\x1e\xfaSH\x8b\x1d\x7f\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6\xf3\x0f\x1e\xfaATUSH\x8b\x1dI\x00\x00\x00H\x8bG\x08L\x8bc(H\x8bx\x08A\xff\xd4H\x8d5#\x00\x00\x00H\x89\xc5H\x8b\x051\x00\x00\x00\x0f\xb7x\x02\xffShH\x89\xefA\xff\xd4H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x11$\r&\xa5 \x01"\xff', + 0x806: b"C\x06\x0b\x1f\x03\x002build/test_x64.native.mpy\x00\x08add1\x00\x0cunused\x00\x91B\xe9I\x00\x00\x00H\x8b\x05\xf4\x00\x00\x00H\x8b\x00\xc3H\x8b\x05\xf9\x00\x00\x00\xbe\x02\x00\x00\x00\x8b8H\x8b\x05\xdb\x00\x00\x00H\x8b@ \xff\xe0H\x8b\x05\xce\x00\x00\x00S\xbe\x02\x00\x00\x00H\x8bX \xffP\x18\xbe\x02\x00\x00\x00H\x8dx\x01H\x89\xd8[\xff\xe0AVAUATUSH\x8b\x1d\xa3\x00\x00\x00H\x8bG\x08L\x8bk(H\x8bx\x08A\xff\xd5L\x8b5\x95\x00\x00\x00L\x8bchH\x8d5r\x00\x00\x00H\x89\xc5H\x8b\x05\x88\x00\x00\x00A\x0f\xb7~\x04\xc7\x00@\xe2\x01\x00A\xff\xd4H\x8d5C\x00\x00\x00\xbfV\x00\x00\x00A\xff\xd4A\x0f\xb7~\x02H\x8d5\x1f\x00\x00\x00A\xff\xd4H\x89\xefA\xff\xd5H\x8b\x03[]A\\A]A^\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x04\x11@\rB\tD\xaf4\x016\xad8\x01:\xaf<\x01>\xff", # -march=armv6m - 0x1006: b"C\x06\x12\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x88\x02\x18\xe0\x00\x00\x10\xb5\tK\tJ{D\x9cX\x02!\xe3h\x98G\x03\x00\x01 \x00+\x02\xd0XC\x01;\xfa\xe7\x02!#i\x98G\x10\xbd\xc0Fj\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\nN\nK~D\xf4XChgiXh\xb8G\x05\x00\x07K\x08I\xf3XyDX\x88ck\x98G(\x00\xb8G h\xf8\xbd\xc0F:\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x11<\r>\xa58\x01:\xff", + 0x1006: b"C\x06\x13\x1f\x03\x008build/test_armv6m.native.mpy\x00\x08add1\x00\x0cunused\x00\x8eb0\xe0\x00\x00\x00\x00\x00\x00\x02K\x03J{D\x9bX\x18hpG\xd0\x00\x00\x00\x00\x00\x00\x00\x10\xb5\x05K\x05I\x06J{D\x9aX[X\x10h\x02!\x1bi\x98G\x10\xbd\xb8\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x10\xb5\x06K\x06J{D\x9bX\x02!\x1ci\xdbh\x98G\x02!\x010\xa0G\x10\xbd\xc0F\x96\x00\x00\x00\x00\x00\x00\x00\xf7\xb5\x12O\x12K\x7fD\xfdX\x12Lki|D\x00\x93ChXh\x00\x9b\x98G\x0fK\x01\x90\x0fJ\xfbXnk\x1a`\x0eK!\x00\xffX\xb8\x88\xb0G!\x00V \x081\xb0G!\x00x\x88\x101\xb0G\x01\x98\x00\x9b\x98G(h\xfe\xbd\xc0Fr\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x08\x00\x00\x00@\xe2\x01\x00\x04\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x04\x11p\rr\tt\xafd\x01f\xadh\x01j\xafl\x01n\xff", + # -march=xtensawin + 0x2806: b"C\x06+\x1f\x03\x00>build/test_xtensawin.native.mpy\x00\x08add1\x00\x0cunused\x00\x8a\x12\x06\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006A\x00\x81\xf9\xff(\x08\x1d\xf0\x00\x006A\x00\x91\xfb\xff\x81\xf5\xff\xa8\t\x88H\x0c+\xe0\x08\x00-\n\x1d\xf0\x00\x006A\x00\x81\xf0\xff\xad\x02xH\x888\x0c+\xe0\x08\x00\x0c+\x1b\xaa\xe0\x07\x00-\n\x1d\xf06A\x00a\xe9\xff\x88\x122&\x05\xa2(\x01\xe0\x03\x00q\xe6\xff\x81\xea\xff\x92\xa7\x89\xa0\x99\x11H\xd6]\n\xb1\xe3\xff\xa2\x17\x02\x99\x08\xe0\x04\x00\xb1\xe2\xff\\j\xe0\x04\x00\xb1\xe1\xff\xa2\x17\x01\xe0\x04\x00\xad\x05\xe0\x03\x00(\x06\x1d\xf0p\x18\x04\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\x11\x02\r\x04\x07\x06\x03\t\x0c\xaf\x01\x01\x03\xad\x05\x01\x07\xaf\t\x01\x0b\xff", + # -march=rv32imc + 0x2C06: b'C\x06/\x1f\x03\x00:build/test_rv32imc.native.mpy\x00\x08add1\x00\x0cunused\x00\x8fb\x97\x0f\x00\x00g\x80\x0f\x05\x97\x07\x00\x00\x83\xa7\x07\x0e\x88C\x82\x80\x97\x07\x00\x00\x83\xa7G\r\x17\x07\x00\x00\x03\'\xc7\r\x9cK\x08C\x89E\x82\x87A\x11\x97\x07\x00\x00\x83\xa7\xa7\x0b"\xc4\x80K\xdcG\x06\xc6\x89E\x82\x97\xa2\x87"D\xb2@\x89E\x05\x05A\x01\x82\x87\\A\x01\x11"\xcc\x17\x04\x00\x00\x03$$\tN\xc6\xc8C\x83)D\x01\x06\xce&\xcaJ\xc8R\xc4\x82\x99\x17\n\x00\x00\x03*\xca\x07\x03)D\x03\xaa\x84\xf9g\x03UJ\x00\x93\x87\x07$\x17\x07\x00\x00\x03\'\x07\x07\x1c\xc3\x97\x05\x00\x00\x93\x85\xe5\x03\x02\x99\x97\x05\x00\x00\x93\x85\xc5\x03\x13\x05`\x05\x02\x99\x03U*\x00\x97\x05\x00\x00\x93\x85%\x03\x02\x99&\x85\x82\x99\x08@\xf2@bD\xd2DBI\xb2I"J\x05a\x82\x80\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00,\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x04\x11t\rv\xafx\xadz\t|\xafh\x01j\xadl\x01n\xafp\x01r\xff', } # Populate armv7m-derived archs based on armv6m. @@ -67,7 +71,7 @@ def open(self, path, mode): features0_file_contents[arch] = features0_file_contents[0x1006] # Check that a .mpy exists for the target (ignore sub-version in lookup). -sys_implementation_mpy = sys.implementation._mpy & ~(3 << 8) +sys_implementation_mpy = (sys.implementation._mpy & ~(3 << 8)) & 0xFFFF if sys_implementation_mpy not in features0_file_contents: print("SKIP") raise SystemExit diff --git a/tests/micropython/kbd_intr.py b/tests/micropython/kbd_intr.py index 81977aaa52f6f..e1ed7185ef0a2 100644 --- a/tests/micropython/kbd_intr.py +++ b/tests/micropython/kbd_intr.py @@ -1,10 +1,10 @@ # test the micropython.kbd_intr() function -import micropython - try: + import micropython + micropython.kbd_intr -except AttributeError: +except (ImportError, AttributeError): print("SKIP") raise SystemExit diff --git a/tests/micropython/meminfo.py b/tests/micropython/meminfo.py index 9df341fbb8334..f4dd8fdb6042c 100644 --- a/tests/micropython/meminfo.py +++ b/tests/micropython/meminfo.py @@ -1,12 +1,14 @@ # tests meminfo functions in micropython module -import micropython +try: + import micropython -# these functions are not always available -if not hasattr(micropython, "mem_info"): + micropython.mem_info +except (ImportError, AttributeError): print("SKIP") -else: - micropython.mem_info() - micropython.mem_info(1) - micropython.qstr_info() - micropython.qstr_info(1) + raise SystemExit + +micropython.mem_info() +micropython.mem_info(1) +micropython.qstr_info() +micropython.qstr_info(1) diff --git a/tests/micropython/memstats.py b/tests/micropython/memstats.py index dee3a4ce2f225..0e2e7b1c0b326 100644 --- a/tests/micropython/memstats.py +++ b/tests/micropython/memstats.py @@ -1,17 +1,19 @@ # tests meminfo functions in micropython module -import micropython +try: + import micropython -# these functions are not always available -if not hasattr(micropython, "mem_total"): + micropython.mem_total +except (ImportError, AttributeError): print("SKIP") -else: - t = micropython.mem_total() - c = micropython.mem_current() - p = micropython.mem_peak() + raise SystemExit - l = list(range(10000)) +t = micropython.mem_total() +c = micropython.mem_current() +p = micropython.mem_peak() - print(micropython.mem_total() > t) - print(micropython.mem_current() > c) - print(micropython.mem_peak() > p) +l = list(range(10000)) + +print(micropython.mem_total() > t) +print(micropython.mem_current() > c) +print(micropython.mem_peak() > p) diff --git a/tests/micropython/opt_level.py b/tests/micropython/opt_level.py index dd5493a7a3cb8..789197d8825bf 100644 --- a/tests/micropython/opt_level.py +++ b/tests/micropython/opt_level.py @@ -1,4 +1,10 @@ -import micropython as micropython +# test micropython.opt_level() + +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit # check we can get and set the level micropython.opt_level(0) diff --git a/tests/micropython/opt_level_lineno.py b/tests/micropython/opt_level_lineno.py index 40650b6819221..ebb404c59fc2d 100644 --- a/tests/micropython/opt_level_lineno.py +++ b/tests/micropython/opt_level_lineno.py @@ -1,7 +1,14 @@ -import micropython as micropython +# test micropython.opt_level() and line numbers + +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit # check that level 3 doesn't store line numbers # the expected output is that any line is printed as "line 1" micropython.opt_level(3) + # CIRCUITPY-CHANGE: use traceback.print_exception() instead of sys.print_exception() exec("try:\n xyz\nexcept NameError as er:\n import traceback\n traceback.print_exception(er)") diff --git a/tests/micropython/opt_level_lineno.py.exp b/tests/micropython/opt_level_lineno.py.exp index 469b90ba7938a..b50f0628c81fb 100644 --- a/tests/micropython/opt_level_lineno.py.exp +++ b/tests/micropython/opt_level_lineno.py.exp @@ -1,3 +1,3 @@ Traceback (most recent call last): - File "", line 1, in + File "", line 1, in f NameError: name 'xyz' isn't defined diff --git a/tests/micropython/ringio_big.py b/tests/micropython/ringio_big.py new file mode 100644 index 0000000000000..ddbbae12a630c --- /dev/null +++ b/tests/micropython/ringio_big.py @@ -0,0 +1,29 @@ +# Check that micropython.RingIO works correctly. + +try: + import micropython + + micropython.RingIO +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +try: + # The maximum possible size + micropython.RingIO(bytearray(65535)) + micropython.RingIO(65534) + + try: + # Buffer may not be too big + micropython.RingIO(bytearray(65536)) + except ValueError as ex: + print(type(ex)) + + try: + # Size may not be too big + micropython.RingIO(65535) + except ValueError as ex: + print(type(ex)) +except MemoryError: + print("SKIP") + raise SystemExit diff --git a/tests/micropython/ringio_big.py.exp b/tests/micropython/ringio_big.py.exp new file mode 100644 index 0000000000000..72af34b383872 --- /dev/null +++ b/tests/micropython/ringio_big.py.exp @@ -0,0 +1,2 @@ + + diff --git a/tests/micropython/schedule.py b/tests/micropython/schedule.py index 6a91459ea3d54..f3dd32661267b 100644 --- a/tests/micropython/schedule.py +++ b/tests/micropython/schedule.py @@ -1,10 +1,10 @@ # test micropython.schedule() function -import micropython - try: + import micropython + micropython.schedule -except AttributeError: +except (ImportError, AttributeError): print("SKIP") raise SystemExit diff --git a/tests/micropython/stack_use.py b/tests/micropython/stack_use.py index 266885d9d1897..5d36fdca2fe01 100644 --- a/tests/micropython/stack_use.py +++ b/tests/micropython/stack_use.py @@ -1,7 +1,11 @@ # tests stack_use function in micropython module -import micropython -if not hasattr(micropython, "stack_use"): +try: + import micropython + + micropython.stack_use +except (ImportError, AttributeError): print("SKIP") -else: - print(type(micropython.stack_use())) # output varies + raise SystemExit + +print(type(micropython.stack_use())) # output varies diff --git a/tests/micropython/test_normalize_newlines.py b/tests/micropython/test_normalize_newlines.py new file mode 100644 index 0000000000000..f19aaa69a3f75 --- /dev/null +++ b/tests/micropython/test_normalize_newlines.py @@ -0,0 +1,14 @@ +# Test for normalize_newlines functionality +# This test verifies that test framework handles various newline combinations correctly + +# Note: This is more of an integration test since normalize_newlines is in the test framework +# The actual testing happens when this test is run through run-tests.py + +print("Testing newline handling") +print("Line 1\r\nLine 2") # Windows-style line ending - should be normalized +print("Line 3") # Normal line +print("Line 4") # Normal line +print("Line 5\nLine 6") # Unix-style line ending - already normalized + +# Test that literal \r in strings is preserved +print(repr("test\rstring")) # Should show 'test\rstring' not 'test\nstring' diff --git a/tests/micropython/test_normalize_newlines.py.exp b/tests/micropython/test_normalize_newlines.py.exp new file mode 100644 index 0000000000000..c4395468cf109 --- /dev/null +++ b/tests/micropython/test_normalize_newlines.py.exp @@ -0,0 +1,8 @@ +Testing newline handling +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +'test\rstring' diff --git a/tests/micropython/viper_large_jump.py b/tests/micropython/viper_large_jump.py new file mode 100644 index 0000000000000..1c5913dec1ea2 --- /dev/null +++ b/tests/micropython/viper_large_jump.py @@ -0,0 +1,20 @@ +COUNT = 600 + + +try: + code = """ +@micropython.viper +def f() -> int: + x = 0 + while x < 10: +""" + for i in range(COUNT): + code += " x += 1\n" + code += " return x" + exec(code) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +print(f()) diff --git a/tests/micropython/viper_large_jump.py.exp b/tests/micropython/viper_large_jump.py.exp new file mode 100644 index 0000000000000..e9f960cf4ac4e --- /dev/null +++ b/tests/micropython/viper_large_jump.py.exp @@ -0,0 +1 @@ +600 diff --git a/tests/micropython/viper_ptr16_load_boundary.py b/tests/micropython/viper_ptr16_load_boundary.py new file mode 100644 index 0000000000000..0d4c3105b685b --- /dev/null +++ b/tests/micropython/viper_ptr16_load_boundary.py @@ -0,0 +1,39 @@ +# Test boundary conditions for various architectures + +GET_TEMPLATE = """ +@micropython.viper +def get{off}(src: ptr16) -> uint: + return uint(src[{off}]) +print(hex(get{off}(buffer))) +""" + + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 2 + + +@micropython.viper +def get_index(src: ptr16, i: int) -> int: + return src[i] + + +def data(start, len): + output = bytearray(len) + for idx in range(len): + output[idx] = (start + idx) & 0xFF + return output + + +buffer = bytearray((((1 << max(BIT_THRESHOLDS)) + 1) // 1024) * 1024) +val = 0 +for bit in BIT_THRESHOLDS: + print("---", bit) + pre, idx, post = ((1 << bit) - (2 * SIZE), (1 << bit) - (1 * SIZE), 1 << bit) + buffer[pre:post] = data(val, 3 * SIZE) + val = val + (3 * SIZE) + + pre, idx, post = pre // SIZE, idx // SIZE, post // SIZE + print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) + exec(GET_TEMPLATE.format(off=pre)) + exec(GET_TEMPLATE.format(off=idx)) + exec(GET_TEMPLATE.format(off=post)) diff --git a/tests/micropython/viper_ptr16_load_boundary.py.exp b/tests/micropython/viper_ptr16_load_boundary.py.exp new file mode 100644 index 0000000000000..56f1d32290468 --- /dev/null +++ b/tests/micropython/viper_ptr16_load_boundary.py.exp @@ -0,0 +1,20 @@ +--- 5 +0x100 0x302 0x504 +0x100 +0x302 +0x504 +--- 8 +0x706 0x908 0xb0a +0x706 +0x908 +0xb0a +--- 11 +0xd0c 0xf0e 0x1110 +0xd0c +0xf0e +0x1110 +--- 12 +0x1312 0x1514 0x1716 +0x1312 +0x1514 +0x1716 diff --git a/tests/micropython/viper_ptr16_store_boundary.py b/tests/micropython/viper_ptr16_store_boundary.py new file mode 100644 index 0000000000000..7c774d4d1ca18 --- /dev/null +++ b/tests/micropython/viper_ptr16_store_boundary.py @@ -0,0 +1,63 @@ +# Test boundary conditions for various architectures + +SET_TEMPLATE = """ +@micropython.viper +def set{off}(dest: ptr16): + saved = dest + dest[{off}] = {val} + assert int(saved) == int(dest) +""" + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 2 +MASK = (1 << (8 * SIZE)) - 1 + +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + + +def get_index(src, i): + return src[i * SIZE] + (src[(i * SIZE) + 1] << 8) + + +@micropython.viper +def set_index(dest: ptr16, i: int, val: uint): + saved = dest + dest[i] = val + assert int(saved) == int(dest) + + +try: + buffer = bytearray((((1 << max(BIT_THRESHOLDS)) // 1024) + 1) * 1024) + + for bit in BIT_THRESHOLDS: + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + exec(SET_TEMPLATE.format(off=(offset + index) // SIZE, val=next_value())) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +for bit in BIT_THRESHOLDS: + print("---", bit) + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + globals()["set{}".format((offset + index) // SIZE)](buffer) + print(hex(get_index(buffer, (offset + index) // SIZE))) + for index in range(0, 3 * SIZE, SIZE): + set_index(buffer, (offset + index) // SIZE, next_value()) + print(hex(get_index(buffer, (offset + index) // SIZE))) diff --git a/tests/micropython/viper_ptr16_store_boundary.py.exp b/tests/micropython/viper_ptr16_store_boundary.py.exp new file mode 100644 index 0000000000000..007a50b3edafb --- /dev/null +++ b/tests/micropython/viper_ptr16_store_boundary.py.exp @@ -0,0 +1,28 @@ +--- 5 +0x1 +0x102 +0x203 +0xc0d +0xd0e +0xe0f +--- 8 +0x304 +0x405 +0x506 +0xf10 +0x1011 +0x1112 +--- 11 +0x607 +0x708 +0x809 +0x1213 +0x1314 +0x1415 +--- 12 +0x90a +0xa0b +0xb0c +0x1516 +0x1617 +0x1718 diff --git a/tests/micropython/viper_ptr32_load_boundary.py b/tests/micropython/viper_ptr32_load_boundary.py new file mode 100644 index 0000000000000..971d1113c49bb --- /dev/null +++ b/tests/micropython/viper_ptr32_load_boundary.py @@ -0,0 +1,39 @@ +# Test boundary conditions for various architectures + +GET_TEMPLATE = """ +@micropython.viper +def get{off}(src: ptr32) -> uint: + return uint(src[{off}]) +print(hex(get{off}(buffer))) +""" + + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 4 + + +@micropython.viper +def get_index(src: ptr32, i: int) -> int: + return src[i] + + +def data(start, len): + output = bytearray(len) + for idx in range(len): + output[idx] = (start + idx) & 0xFF + return output + + +buffer = bytearray((((1 << max(BIT_THRESHOLDS)) + 1) // 1024) * 1024) +val = 0 +for bit in BIT_THRESHOLDS: + print("---", bit) + pre, idx, post = (((1 << bit) - (2 * SIZE)), ((1 << bit) - (1 * SIZE)), (1 << bit)) + buffer[pre:post] = data(val, 3 * SIZE) + val = val + (3 * SIZE) + + pre, idx, post = pre // SIZE, idx // SIZE, post // SIZE + print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) + exec(GET_TEMPLATE.format(off=pre)) + exec(GET_TEMPLATE.format(off=idx)) + exec(GET_TEMPLATE.format(off=post)) diff --git a/tests/micropython/viper_ptr32_load_boundary.py.exp b/tests/micropython/viper_ptr32_load_boundary.py.exp new file mode 100644 index 0000000000000..1e22a8b361333 --- /dev/null +++ b/tests/micropython/viper_ptr32_load_boundary.py.exp @@ -0,0 +1,20 @@ +--- 5 +0x3020100 0x7060504 0xb0a0908 +0x3020100 +0x7060504 +0xb0a0908 +--- 8 +0xf0e0d0c 0x13121110 0x17161514 +0xf0e0d0c +0x13121110 +0x17161514 +--- 11 +0x1b1a1918 0x1f1e1d1c 0x23222120 +0x1b1a1918 +0x1f1e1d1c +0x23222120 +--- 12 +0x27262524 0x2b2a2928 0x2f2e2d2c +0x27262524 +0x2b2a2928 +0x2f2e2d2c diff --git a/tests/micropython/viper_ptr32_store_boundary.py b/tests/micropython/viper_ptr32_store_boundary.py new file mode 100644 index 0000000000000..96ca74ad3ca70 --- /dev/null +++ b/tests/micropython/viper_ptr32_store_boundary.py @@ -0,0 +1,68 @@ +# Test boundary conditions for various architectures + +SET_TEMPLATE = """ +@micropython.viper +def set{off}(dest: ptr32): + saved = dest + dest[{off}] = {val} + assert int(saved) == int(dest) +""" + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 4 +MASK = (1 << (8 * SIZE)) - 1 + +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + + +def get_index(src, i): + return ( + src[i * SIZE] + + (src[(i * SIZE) + 1] << 8) + + (src[(i * SIZE) + 2] << 16) + + (src[(i * SIZE) + 3] << 24) + ) + + +@micropython.viper +def set_index(dest: ptr32, i: int, val: uint): + saved = dest + dest[i] = val + assert int(dest) == int(saved) + + +try: + buffer = bytearray((((1 << max(BIT_THRESHOLDS)) // 1024) + 1) * 1024) + + for bit in BIT_THRESHOLDS: + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + exec(SET_TEMPLATE.format(off=(offset + index) // SIZE, val=next_value())) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +for bit in BIT_THRESHOLDS: + print("---", bit) + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + globals()["set{}".format((offset + index) // SIZE)](buffer) + print(hex(get_index(buffer, (offset + index) // SIZE))) + for index in range(0, 3 * SIZE, SIZE): + set_index(buffer, (offset + index) // SIZE, next_value()) + print(hex(get_index(buffer, (offset + index) // SIZE))) diff --git a/tests/micropython/viper_ptr32_store_boundary.py.exp b/tests/micropython/viper_ptr32_store_boundary.py.exp new file mode 100644 index 0000000000000..7a9a51624743e --- /dev/null +++ b/tests/micropython/viper_ptr32_store_boundary.py.exp @@ -0,0 +1,28 @@ +--- 5 +0x1 +0x102 +0x10203 +0xa0b0c0d +0xb0c0d0e +0xc0d0e0f +--- 8 +0x1020304 +0x2030405 +0x3040506 +0xd0e0f10 +0xe0f1011 +0xf101112 +--- 11 +0x4050607 +0x5060708 +0x6070809 +0x10111213 +0x11121314 +0x12131415 +--- 12 +0x708090a +0x8090a0b +0x90a0b0c +0x13141516 +0x14151617 +0x15161718 diff --git a/tests/micropython/viper_ptr8_load_boundary.py b/tests/micropython/viper_ptr8_load_boundary.py new file mode 100644 index 0000000000000..57e06da5709ab --- /dev/null +++ b/tests/micropython/viper_ptr8_load_boundary.py @@ -0,0 +1,38 @@ +# Test boundary conditions for various architectures + +GET_TEMPLATE = """ +@micropython.viper +def get{off}(src: ptr8) -> uint: + return uint(src[{off}]) +print(hex(get{off}(buffer))) +""" + + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 1 + + +@micropython.viper +def get_index(src: ptr8, i: int) -> int: + return src[i] + + +def data(start, len): + output = bytearray(len) + for idx in range(len): + output[idx] = (start + idx) & 0xFF + return output + + +buffer = bytearray((((1 << max(BIT_THRESHOLDS)) + 1) // 1024) * 1024) +val = 0 +for bit in BIT_THRESHOLDS: + print("---", bit) + pre, idx, post = (((1 << bit) - (2 * SIZE)), ((1 << bit) - (1 * SIZE)), (1 << bit)) + buffer[pre:post] = data(val, 3 * SIZE) + val = val + (3 * SIZE) + + print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) + exec(GET_TEMPLATE.format(off=pre)) + exec(GET_TEMPLATE.format(off=idx)) + exec(GET_TEMPLATE.format(off=post)) diff --git a/tests/micropython/viper_ptr8_load_boundary.py.exp b/tests/micropython/viper_ptr8_load_boundary.py.exp new file mode 100644 index 0000000000000..a0e423686b8fa --- /dev/null +++ b/tests/micropython/viper_ptr8_load_boundary.py.exp @@ -0,0 +1,20 @@ +--- 5 +0x0 0x1 0x2 +0x0 +0x1 +0x2 +--- 8 +0x3 0x4 0x5 +0x3 +0x4 +0x5 +--- 11 +0x6 0x7 0x8 +0x6 +0x7 +0x8 +--- 12 +0x9 0xa 0xb +0x9 +0xa +0xb diff --git a/tests/micropython/viper_ptr8_store_boundary.py b/tests/micropython/viper_ptr8_store_boundary.py new file mode 100644 index 0000000000000..68b76fd598b88 --- /dev/null +++ b/tests/micropython/viper_ptr8_store_boundary.py @@ -0,0 +1,63 @@ +# Test boundary conditions for various architectures + +SET_TEMPLATE = """ +@micropython.viper +def set{off}(dest: ptr8): + saved = dest + dest[{off}] = {val} + assert int(saved) == int(dest) +""" + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 1 +MASK = (1 << (8 * SIZE)) - 1 + +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + + +def get_index(src: ptr8, i: int): + return src[i] + + +@micropython.viper +def set_index(dest: ptr8, i: int, val: uint): + saved = dest + dest[i] = val + assert int(dest) == int(saved) + + +try: + buffer = bytearray((((1 << max(BIT_THRESHOLDS)) // 1024) + 1) * 1024) + + for bit in BIT_THRESHOLDS: + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + exec(SET_TEMPLATE.format(off=(offset + index) // SIZE, val=next_value())) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +for bit in BIT_THRESHOLDS: + print("---", bit) + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + globals()["set{}".format((offset + index) // SIZE)](buffer) + print(hex(get_index(buffer, (offset + index) // SIZE))) + for index in range(0, 3 * SIZE, SIZE): + set_index(buffer, (offset + index) // SIZE, next_value()) + print(hex(get_index(buffer, (offset + index) // SIZE))) diff --git a/tests/micropython/viper_ptr8_store_boundary.py.exp b/tests/micropython/viper_ptr8_store_boundary.py.exp new file mode 100644 index 0000000000000..621295d81a896 --- /dev/null +++ b/tests/micropython/viper_ptr8_store_boundary.py.exp @@ -0,0 +1,28 @@ +--- 5 +0x1 +0x2 +0x3 +0xd +0xe +0xf +--- 8 +0x4 +0x5 +0x6 +0x10 +0x11 +0x12 +--- 11 +0x7 +0x8 +0x9 +0x13 +0x14 +0x15 +--- 12 +0xa +0xb +0xc +0x16 +0x17 +0x18 diff --git a/tests/misc/non_compliant.py b/tests/misc/non_compliant.py index 31c9fa17c3bed..8608f2322f8e8 100644 --- a/tests/misc/non_compliant.py +++ b/tests/misc/non_compliant.py @@ -39,7 +39,7 @@ except NotImplementedError: print("NotImplementedError") -# uPy raises TypeError, should be ValueError +# MicroPython raises TypeError, should be ValueError try: "%c" % b"\x01\x02" except (TypeError, ValueError): @@ -100,10 +100,10 @@ print("NotImplementedError") # CIRCUITPY-CHANGE: We do check these. -# struct pack with too many args, not checked by uPy +# struct pack with too many args, not checked by MicroPython # print(struct.pack("bb", 1, 2, 3)) -# struct pack with too few args, not checked by uPy +# struct pack with too few args, not checked by MicroPython # print(struct.pack("bb", 1)) # array slice assignment with unsupported RHS diff --git a/tests/misc/non_compliant_lexer.py b/tests/misc/non_compliant_lexer.py index e1c21f3d713c4..04c605953e705 100644 --- a/tests/misc/non_compliant_lexer.py +++ b/tests/misc/non_compliant_lexer.py @@ -11,7 +11,7 @@ def test(code): print("NotImplementedError") -# uPy requires spaces between literal numbers and keywords, CPy doesn't +# MPy requires spaces between literal numbers and keywords, CPy doesn't try: eval("1and 0") except SyntaxError: diff --git a/tests/misc/print_exception.py.native.exp b/tests/misc/print_exception.py.native.exp new file mode 100644 index 0000000000000..59e856ae3c44a --- /dev/null +++ b/tests/misc/print_exception.py.native.exp @@ -0,0 +1,18 @@ +caught +Exception: msg + +caught +Exception: fail + +finally +caught +Exception: fail + +reraise +Exception: fail + +caught +Exception: fail + +AttributeError: 'function' object has no attribute 'X' + diff --git a/tests/misc/rge_sm.py b/tests/misc/rge_sm.py index 5e071687c495d..56dad5749776e 100644 --- a/tests/misc/rge_sm.py +++ b/tests/misc/rge_sm.py @@ -39,14 +39,6 @@ def solve(self, finishtime): if not self.iterate(): break - def solveNSteps(self, nSteps): - for i in range(nSteps): - if not self.iterate(): - break - - def series(self): - return zip(*self.Trajectory) - # 1-loop RGES for the main parameters of the SM # couplings are: g1, g2, g3 of U(1), SU(2), SU(3); yt (top Yukawa), lambda (Higgs quartic) @@ -79,46 +71,8 @@ def series(self): ) -def drange(start, stop, step): - r = start - while r < stop: - yield r - r += step - - -def phaseDiagram(system, trajStart, trajPlot, h=0.1, tend=1.0, range=1.0): - tstart = 0.0 - for i in drange(0, range, 0.1 * range): - for j in drange(0, range, 0.1 * range): - rk = RungeKutta(system, trajStart(i, j), tstart, h) - rk.solve(tend) - # draw the line - for tr in rk.Trajectory: - x, y = trajPlot(tr) - print(x, y) - print() - # draw the arrow - continue - l = (len(rk.Trajectory) - 1) / 3 - if l > 0 and 2 * l < len(rk.Trajectory): - p1 = rk.Trajectory[l] - p2 = rk.Trajectory[2 * l] - x1, y1 = trajPlot(p1) - x2, y2 = trajPlot(p2) - dx = -0.5 * (y2 - y1) # orthogonal to line - dy = 0.5 * (x2 - x1) # orthogonal to line - # l = math.sqrt(dx*dx + dy*dy) - # if abs(l) > 1e-3: - # l = 0.1 / l - # dx *= l - # dy *= l - print(x1 + dx, y1 + dy) - print(x2, y2) - print(x1 - dx, y1 - dy) - print() - - def singleTraj(system, trajStart, h=0.02, tend=1.0): + is_REPR_C = float("1.0000001") == float("1.0") tstart = 0.0 # compute the trajectory @@ -130,10 +84,15 @@ def singleTraj(system, trajStart, h=0.02, tend=1.0): for i in range(len(rk.Trajectory)): tr = rk.Trajectory[i] - print(" ".join(["{:.4f}".format(t) for t in tr])) - + tr_str = " ".join(["{:.4f}".format(t) for t in tr]) + if is_REPR_C: + # allow two small deviations for REPR_C + if tr_str == "1.0000 0.3559 0.6485 1.1944 0.9271 0.1083": + tr_str = "1.0000 0.3559 0.6485 1.1944 0.9272 0.1083" + if tr_str == "16.0000 0.3894 0.5793 0.7017 0.5686 -0.0168": + tr_str = "16.0000 0.3894 0.5793 0.7017 0.5686 -0.0167" + print(tr_str) -# phaseDiagram(sysSM, (lambda i, j: [0.354, 0.654, 1.278, 0.8 + 0.2 * i, 0.1 + 0.1 * j]), (lambda a: (a[4], a[5])), h=0.1, tend=math.log(10**17)) # initial conditions at M_Z singleTraj(sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.5, tend=math.log(10**17)) # true values diff --git a/tests/misc/sys_exc_info.py b/tests/misc/sys_exc_info.py index d7e8a2d943b5e..c076dd572b07b 100644 --- a/tests/misc/sys_exc_info.py +++ b/tests/misc/sys_exc_info.py @@ -8,13 +8,14 @@ def f(): - print(sys.exc_info()[0:2]) + e = sys.exc_info() + print(e[0], e[1]) try: raise ValueError("value", 123) except: - print(sys.exc_info()[0:2]) + print(sys.exc_info()[0], sys.exc_info()[1]) f() # Outside except block, sys.exc_info() should be back to None's diff --git a/tests/misc/sys_settrace_cov.py b/tests/misc/sys_settrace_cov.py new file mode 100644 index 0000000000000..579c8a4a25e50 --- /dev/null +++ b/tests/misc/sys_settrace_cov.py @@ -0,0 +1,23 @@ +import sys + +try: + sys.settrace +except AttributeError: + print("SKIP") + raise SystemExit + + +def trace_tick_handler(frame, event, arg): + print("FRAME", frame) + print("LASTI", frame.f_lasti) + return None + + +def f(): + x = 3 + return x + + +sys.settrace(trace_tick_handler) +f() +sys.settrace(None) diff --git a/tests/misc/sys_settrace_cov.py.exp b/tests/misc/sys_settrace_cov.py.exp new file mode 100644 index 0000000000000..423d78ec42b89 --- /dev/null +++ b/tests/misc/sys_settrace_cov.py.exp @@ -0,0 +1,2 @@ +FRAME +LASTI \\d\+ diff --git a/tests/multi_extmod/machine_i2c_target_irq.py b/tests/multi_extmod/machine_i2c_target_irq.py new file mode 100644 index 0000000000000..eafd9dfdca838 --- /dev/null +++ b/tests/multi_extmod/machine_i2c_target_irq.py @@ -0,0 +1,137 @@ +# Test I2CTarget IRQs and clock stretching. +# +# Requires two instances with their SCL and SDA lines connected together. +# Any combination of the below supported boards can be used. +# +# Notes: +# - pull-up resistors may be needed +# - alif use 1.8V signalling + +import sys +import time +from machine import I2C, I2CTarget + +if not hasattr(I2CTarget, "IRQ_ADDR_MATCH_READ"): + print("SKIP") + raise SystemExit + +ADDR = 67 +clock_stretch_us = 200 + +# Configure pins based on the target. +if sys.platform == "alif": + i2c_args = (1,) # pins P3_7/P3_6 + i2c_kwargs = {} +elif sys.platform == "mimxrt": + i2c_args = (0,) # pins 19/18 on Teensy 4.x + i2c_kwargs = {} + clock_stretch_us = 50 # mimxrt cannot delay too long in the IRQ handler +elif sys.platform == "rp2": + i2c_args = (0,) + i2c_kwargs = {"scl": 9, "sda": 8} +elif sys.platform == "pyboard": + i2c_args = ("Y",) + i2c_kwargs = {} +elif sys.platform == "samd": + i2c_args = () # pins SCL/SDA + i2c_kwargs = {} +elif "zephyr-rpi_pico" in sys.implementation._machine: + i2c_args = ("i2c1",) # on gpio7/gpio6 + i2c_kwargs = {} +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def simple_irq(i2c_target): + flags = i2c_target.irq().flags() + if flags & I2CTarget.IRQ_ADDR_MATCH_READ: + print("IRQ_ADDR_MATCH_READ") + if flags & I2CTarget.IRQ_ADDR_MATCH_WRITE: + print("IRQ_ADDR_MATCH_WRITE") + + # Force clock stretching. + time.sleep_us(clock_stretch_us) + + +class I2CTargetMemory: + def __init__(self, i2c_target, mem): + self.buf1 = bytearray(1) + self.mem = mem + self.memaddr = 0 + self.state = 0 + i2c_target.irq( + self.irq, + I2CTarget.IRQ_ADDR_MATCH_WRITE | I2CTarget.IRQ_READ_REQ | I2CTarget.IRQ_WRITE_REQ, + hard=True, + ) + + def irq(self, i2c_target): + # Force clock stretching. + time.sleep_us(clock_stretch_us) + + flags = i2c_target.irq().flags() + if flags & I2CTarget.IRQ_ADDR_MATCH_WRITE: + self.state = 0 + if flags & I2CTarget.IRQ_READ_REQ: + self.buf1[0] = self.mem[self.memaddr] + self.memaddr += 1 + i2c_target.write(self.buf1) + if flags & I2CTarget.IRQ_WRITE_REQ: + i2c_target.readinto(self.buf1) + if self.state == 0: + self.state = 1 + self.memaddr = self.buf1[0] + else: + self.mem[self.memaddr] = self.buf1[0] + self.memaddr += 1 + self.memaddr %= len(self.mem) + + # Force clock stretching. + time.sleep_us(clock_stretch_us) + + +# I2C controller +def instance0(): + i2c = I2C(*i2c_args, **i2c_kwargs) + multitest.next() + for iteration in range(2): + print("controller iteration", iteration) + multitest.wait("target stage 1") + i2c.writeto_mem(ADDR, 2, "0123") + multitest.broadcast("controller stage 2") + multitest.wait("target stage 3") + print(i2c.readfrom_mem(ADDR, 2, 4)) + multitest.broadcast("controller stage 4") + print("done") + + +# I2C target +def instance1(): + multitest.next() + + for iteration in range(2): + print("target iteration", iteration) + buf = bytearray(b"--------") + if iteration == 0: + # Use built-in memory capability of I2CTarget. + i2c_target = I2CTarget(*i2c_args, **i2c_kwargs, addr=ADDR, mem=buf) + i2c_target.irq( + simple_irq, + I2CTarget.IRQ_ADDR_MATCH_READ | I2CTarget.IRQ_ADDR_MATCH_WRITE, + hard=True, + ) + else: + # Implement a memory device by hand. + i2c_target = I2CTarget(*i2c_args, **i2c_kwargs, addr=ADDR) + I2CTargetMemory(i2c_target, buf) + + multitest.broadcast("target stage 1") + multitest.wait("controller stage 2") + print(buf) + multitest.broadcast("target stage 3") + multitest.wait("controller stage 4") + + i2c_target.deinit() + + print("done") diff --git a/tests/multi_extmod/machine_i2c_target_irq.py.exp b/tests/multi_extmod/machine_i2c_target_irq.py.exp new file mode 100644 index 0000000000000..a17c8f43858b3 --- /dev/null +++ b/tests/multi_extmod/machine_i2c_target_irq.py.exp @@ -0,0 +1,15 @@ +--- instance0 --- +controller iteration 0 +b'0123' +controller iteration 1 +b'0123' +done +--- instance1 --- +target iteration 0 +IRQ_ADDR_MATCH_WRITE +bytearray(b'--0123--') +IRQ_ADDR_MATCH_WRITE +IRQ_ADDR_MATCH_READ +target iteration 1 +bytearray(b'--0123--') +done diff --git a/tests/multi_extmod/machine_i2c_target_memory.py b/tests/multi_extmod/machine_i2c_target_memory.py new file mode 100644 index 0000000000000..6b3f0d03eb7fe --- /dev/null +++ b/tests/multi_extmod/machine_i2c_target_memory.py @@ -0,0 +1,79 @@ +# Test basic use of I2CTarget and a memory buffer. +# +# Requires two instances with their SCL and SDA lines connected together. +# Any combination of the below supported boards can be used. +# +# Notes: +# - pull-up resistors may be needed +# - alif use 1.8V signalling + +import sys +from machine import I2C, I2CTarget + +ADDR = 67 + +# Configure pins based on the target. +if sys.platform == "alif": + i2c_args = (1,) # pins P3_7/P3_6 + i2c_kwargs = {} +elif sys.platform == "esp32": + i2c_args = (1,) # on pins 9/8 + i2c_kwargs = {} +elif sys.platform == "mimxrt": + i2c_args = (0,) # pins 19/18 on Teensy 4.x + i2c_kwargs = {} +elif sys.platform == "rp2": + i2c_args = (0,) + i2c_kwargs = {"scl": 9, "sda": 8} +elif sys.platform == "pyboard": + i2c_args = ("Y",) + i2c_kwargs = {} +elif sys.platform == "samd": + i2c_args = () # pins SCL/SDA + i2c_kwargs = {} +elif "zephyr-rpi_pico" in sys.implementation._machine: + i2c_args = ("i2c1",) # on gpio7/gpio6 + i2c_kwargs = {} +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def simple_irq(i2c_target): + flags = i2c_target.irq().flags() + if flags & I2CTarget.IRQ_END_READ: + print("IRQ_END_READ", i2c_target.memaddr) + if flags & I2CTarget.IRQ_END_WRITE: + print("IRQ_END_WRITE", i2c_target.memaddr) + + +# I2C controller +def instance0(): + i2c = I2C(*i2c_args, **i2c_kwargs) + multitest.next() + for iteration in range(2): + print("controller iteration", iteration) + multitest.wait("target stage 1") + i2c.writeto_mem(ADDR, 2 + iteration, "0123") + multitest.broadcast("controller stage 2") + multitest.wait("target stage 3") + print(i2c.readfrom_mem(ADDR, 2 + iteration, 4)) + multitest.broadcast("controller stage 4") + print("done") + + +# I2C target +def instance1(): + buf = bytearray(b"--------") + i2c_target = I2CTarget(*i2c_args, **i2c_kwargs, addr=ADDR, mem=buf) + i2c_target.irq(simple_irq) + multitest.next() + for iteration in range(2): + print("target iteration", iteration) + multitest.broadcast("target stage 1") + multitest.wait("controller stage 2") + print(buf) + multitest.broadcast("target stage 3") + multitest.wait("controller stage 4") + i2c_target.deinit() + print("done") diff --git a/tests/multi_extmod/machine_i2c_target_memory.py.exp b/tests/multi_extmod/machine_i2c_target_memory.py.exp new file mode 100644 index 0000000000000..71386cfe769d9 --- /dev/null +++ b/tests/multi_extmod/machine_i2c_target_memory.py.exp @@ -0,0 +1,16 @@ +--- instance0 --- +controller iteration 0 +b'0123' +controller iteration 1 +b'0123' +done +--- instance1 --- +target iteration 0 +IRQ_END_WRITE 2 +bytearray(b'--0123--') +IRQ_END_READ 2 +target iteration 1 +IRQ_END_WRITE 3 +bytearray(b'--00123-') +IRQ_END_READ 3 +done diff --git a/tests/perf_bench/bm_fft.py b/tests/perf_bench/bm_fft.py index 9a2d03d11b97c..e35c1216c139f 100644 --- a/tests/perf_bench/bm_fft.py +++ b/tests/perf_bench/bm_fft.py @@ -15,7 +15,7 @@ def reverse(x, bits): # Initialization n = len(vector) - levels = int(math.log(n) / math.log(2)) + levels = int(round(math.log(n) / math.log(2))) coef = (2 if inverse else -2) * cmath.pi / n exptable = [cmath.rect(1, i * coef) for i in range(n // 2)] vector = [vector[reverse(i, levels)] for i in range(n)] # Copy with bit-reversed permutation diff --git a/tests/perf_bench/bm_pidigits.py b/tests/perf_bench/bm_pidigits.py index bdaa73cec7e9f..c935f103c5b78 100644 --- a/tests/perf_bench/bm_pidigits.py +++ b/tests/perf_bench/bm_pidigits.py @@ -5,6 +5,12 @@ # This benchmark stresses big integer arithmetic. # Adapted from code on: http://benchmarksgame.alioth.debian.org/ +try: + int("0x10000000000000000", 16) +except: + print("SKIP") # No support for >64-bit integers + raise SystemExit + def compose(a, b): aq, ar, as_, at = a diff --git a/tests/perf_bench/core_import_mpy_multi.py b/tests/perf_bench/core_import_mpy_multi.py index 8affa157fa0c5..67deec0508801 100644 --- a/tests/perf_bench/core_import_mpy_multi.py +++ b/tests/perf_bench/core_import_mpy_multi.py @@ -6,7 +6,7 @@ print("SKIP") raise SystemExit -# This is the test.py file that is compiled to test.mpy below. +# This is the test.py file that is compiled to test.mpy below. mpy-cross must be invoked with `-msmall-int-bits=30`. """ class A: def __init__(self, arg): @@ -23,7 +23,7 @@ def f(): x = ("const tuple", None, False, True, 1, 2, 3) result = 123 """ -file_data = b'M\x06\x00\x1f\x14\x03\x0etest.py\x00\x0f\x02A\x00\x02f\x00\x0cresult\x00/-5#\x82I\x81{\x81w\x82/\x81\x05\x81\x17Iom\x82\x13\x06arg\x00\x05\x1cthis will be a string object\x00\x06\x1bthis will be a bytes object\x00\n\x07\x05\x0bconst tuple\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x81\\\x10\n\x01\x89\x07d`T2\x00\x10\x024\x02\x16\x022\x01\x16\x03"\x80{\x16\x04Qc\x02\x81d\x00\x08\x02(DD\x11\x05\x16\x06\x10\x02\x16\x072\x00\x16\x082\x01\x16\t2\x02\x16\nQc\x03`\x1a\x08\x08\x12\x13@\xb1\xb0\x18\x13Qc@\t\x08\t\x12` Qc@\t\x08\n\x12``Qc\x82@ \x0e\x03\x80\x08+)##\x12\x0b\x12\x0c\x12\r\x12\x0e*\x04Y\x12\x0f\x12\x10\x12\x11*\x03Y#\x00\xc0#\x01\xc0#\x02\xc0Qc' +file_data = b'M\x06\x00\x1e\x14\x03\x0etest.py\x00\x0f\x02A\x00\x02f\x00\x0cresult\x00/-5#\x82I\x81{\x81w\x82/\x81\x05\x81\x17Iom\x82\x13\x06arg\x00\x05\x1cthis will be a string object\x00\x06\x1bthis will be a bytes object\x00\n\x07\x05\x0bconst tuple\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x81\\\x10\n\x01\x89\x07d`T2\x00\x10\x024\x02\x16\x022\x01\x16\x03"\x80{\x16\x04Qc\x02\x81d\x00\x08\x02(DD\x11\x05\x16\x06\x10\x02\x16\x072\x00\x16\x082\x01\x16\t2\x02\x16\nQc\x03`\x1a\x08\x08\x12\x13@\xb1\xb0\x18\x13Qc@\t\x08\t\x12` Qc@\t\x08\n\x12``Qc\x82@ \x0e\x03\x80\x08+)##\x12\x0b\x12\x0c\x12\r\x12\x0e*\x04Y\x12\x0f\x12\x10\x12\x11*\x03Y#\x00\xc0#\x01\xc0#\x02\xc0Qc' class File(io.IOBase): diff --git a/tests/perf_bench/core_import_mpy_single.py b/tests/perf_bench/core_import_mpy_single.py index 4d9aa67bf2f0e..f472bb6476278 100644 --- a/tests/perf_bench/core_import_mpy_single.py +++ b/tests/perf_bench/core_import_mpy_single.py @@ -8,7 +8,7 @@ print("SKIP") raise SystemExit -# This is the test.py file that is compiled to test.mpy below. +# This is the test.py file that is compiled to test.mpy below. mpy-cross must be invoked with `-msmall-int-bits=30`. # Many known and unknown names/strings are included to test the linking process. """ class A0: @@ -78,7 +78,7 @@ def f1(): x = ("const tuple 9", None, False, True, 1, 2, 3) result = 123 """ -file_data = b"M\x06\x00\x1f\x81=\x1e\x0etest.py\x00\x0f\x04A0\x00\x04A1\x00\x04f0\x00\x04f1\x00\x0cresult\x00/-5\x04a0\x00\x04a1\x00\x04a2\x00\x04a3\x00\x13\x15\x17\x19\x1b\x1d\x1f!#%')+1379;=?ACEGIKMOQSUWY[]_acegikmoqsuwy{}\x7f\x81\x01\x81\x03\x81\x05\x81\x07\x81\t\x81\x0b\x81\r\x81\x0f\x81\x11\x81\x13\x81\x15\x81\x17\x81\x19\x81\x1b\x81\x1d\x81\x1f\x81!\x81#\x81%\x81'\x81)\x81+\x81-\x81/\x811\x813\x815\x817\x819\x81;\x81=\x81?\x81A\x81C\x81E\x81G\x81I\x81K\x81M\x81O\x81Q\x81S\x81U\x81W\x81Y\x81[\x81]\x81_\x81a\x81c\x81e\x81g\x81i\x81k\x81m\x81o\x81q\x81s\x81u\x81w\x81y\x81{\x81}\x81\x7f\x82\x01\x82\x03\x82\x05\x82\x07\x82\t\x82\x0b\x82\r\x82\x0f\x82\x11\x82\x13\x82\x15\x82\x17\x82\x19\x82\x1b\x82\x1d\x82\x1f\x82!\x82#\x82%\x82'\x82)\x82+\x82-\x82/\x821\x823\x825\x827\x829\x82;\x82=\x82?\x82A\x82E\x82G\x82I\x82K\nname0\x00\nname1\x00\nname2\x00\nname3\x00\nname4\x00\nname5\x00\nname6\x00\nname7\x00\nname8\x00\nname9\x00$quite_a_long_name0\x00$quite_a_long_name1\x00$quite_a_long_name2\x00$quite_a_long_name3\x00$quite_a_long_name4\x00$quite_a_long_name5\x00$quite_a_long_name6\x00$quite_a_long_name7\x00$quite_a_long_name8\x00$quite_a_long_name9\x00&quite_a_long_name10\x00&quite_a_long_name11\x00\x05\x1ethis will be a string object 0\x00\x05\x1ethis will be a string object 1\x00\x05\x1ethis will be a string object 2\x00\x05\x1ethis will be a string object 3\x00\x05\x1ethis will be a string object 4\x00\x05\x1ethis will be a string object 5\x00\x05\x1ethis will be a string object 6\x00\x05\x1ethis will be a string object 7\x00\x05\x1ethis will be a string object 8\x00\x05\x1ethis will be a string object 9\x00\x06\x1dthis will be a bytes object 0\x00\x06\x1dthis will be a bytes object 1\x00\x06\x1dthis will be a bytes object 2\x00\x06\x1dthis will be a bytes object 3\x00\x06\x1dthis will be a bytes object 4\x00\x06\x1dthis will be a bytes object 5\x00\x06\x1dthis will be a bytes object 6\x00\x06\x1dthis will be a bytes object 7\x00\x06\x1dthis will be a bytes object 8\x00\x06\x1dthis will be a bytes object 9\x00\n\x07\x05\rconst tuple 0\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 1\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 2\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 3\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 4\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 5\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 6\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 7\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 8\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 9\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x82d\x10\x12\x01i@i@\x84\x18\x84\x1fT2\x00\x10\x024\x02\x16\x02T2\x01\x10\x034\x02\x16\x032\x02\x16\x042\x03\x16\x05\"\x80{\x16\x06Qc\x04\x82\x0c\x00\n\x02($$$\x11\x07\x16\x08\x10\x02\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04@\t\x08\n\x81\x0b Qc@\t\x08\x0b\x81\x0b@Qc@\t\x08\x0c\x81\x0b`QcH\t\n\r\x81\x0b` Qc\x82\x14\x00\x0c\x03h`$$$\x11\x07\x16\x08\x10\x03\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04H\t\n\n\x81\x0b``QcH\t\n\x0b\x81\x0b\x80\x07QcH\t\n\x0c\x81\x0b\x80\x08QcH\t\n\r\x81\x0b\x80\tQc\xa08P:\x04\x80\x0b13///---997799<\x1f%\x1f\"\x1f%)\x1f\"//\x12\x0e\x12\x0f\x12\x10\x12\x11\x12\x12\x12\x13\x12\x14*\x07Y\x12\x15\x12\x16\x12\x17\x12\x18\x12\x19\x12\x1a\x12\x08\x12\x07*\x08Y\x12\x1b\x12\x1c\x12\t\x12\x1d\x12\x1e\x12\x1f*\x06Y\x12 \x12!\x12\"\x12#\x12$\x12%*\x06Y\x12&\x12'\x12(\x12)\x12*\x12+*\x06Y\x12,\x12-\x12.\x12/\x120*\x05Y\x121\x122\x123\x124\x125*\x05Y\x126\x127\x128\x129\x12:*\x05Y\x12;\x12<\x12=\x12>\x12?\x12@\x12A\x12B\x12C\x12D\x12E*\x0bY\x12F\x12G\x12H\x12I\x12J\x12K\x12L\x12M\x12N\x12O\x12P*\x0bY\x12Q\x12R\x12S\x12T\x12U\x12V\x12W\x12X\x12Y\x12Z*\nY\x12[\x12\\\x12]\x12^\x12_\x12`\x12a\x12b\x12c\x12d*\nY\x12e\x12f\x12g\x12h\x12i\x12j\x12k\x12l\x12m\x12n\x12o*\x0bY\x12p\x12q\x12r\x12s\x12t\x12u\x12v\x12w\x12x\x12y\x12z*\x0bY\x12{\x12|\x12}\x12~\x12\x7f\x12\x81\x00\x12\x81\x01\x12\x81\x02\x12\x81\x03\x12\x81\x04*\nY\x12\x81\x05\x12\x81\x06\x12\x81\x07\x12\x81\x08\x12\x81\t\x12\x81\n\x12\x81\x0b\x12\x81\x0c\x12\x81\r\x12\x81\x0e\x12\x81\x0f*\x0bY\x12\x81\x10\x12\x81\x11\x12\x81\x12\x12\x81\x13\x12\x81\x14\x12\x81\x15\x12\x81\x16\x12\x81\x17\x12\x81\x18\x12\x81\x19*\nY\x12\x81\x1a\x12\x81\x1b\x12\x81\x1c\x12\x81\x1d\x12\x81\x1e\x12\x81\x1f\x12\x81 \x12\x81!\x12\x81\"\x12\x81#\x12\x81$*\x0bY\x12\x81%\x12\x81&*\x02Y\x12\x81'\x12\x81(\x12\x81)\x12\x81*\x12\x81+\x12\x81,\x12\x81-\x12\x81.\x12\x81/\x12\x810*\nY\x12\x811\x12\x812\x12\x813\x12\x814*\x04Y\x12\x815\x12\x816\x12\x817\x12\x818*\x04Y\x12\x819\x12\x81:\x12\x81;\x12\x81<*\x04YQc\x87p\x08@\x05\x80###############################\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14\xc0#\x15\xc0#\x16\xc0#\x17\xc0#\x18\xc0#\x19\xc0#\x1a\xc0#\x1b\xc0#\x1c\xc0#\x1d\xc0Qc" +file_data = b"M\x06\x00\x1e\x81=\x1e\x0etest.py\x00\x0f\x04A0\x00\x04A1\x00\x04f0\x00\x04f1\x00\x0cresult\x00/-5\x04a0\x00\x04a1\x00\x04a2\x00\x04a3\x00\x13\x15\x17\x19\x1b\x1d\x1f!#%')+1379;=?ACEGIKMOQSUWY[]_acegikmoqsuwy{}\x7f\x81\x01\x81\x03\x81\x05\x81\x07\x81\t\x81\x0b\x81\r\x81\x0f\x81\x11\x81\x13\x81\x15\x81\x17\x81\x19\x81\x1b\x81\x1d\x81\x1f\x81!\x81#\x81%\x81'\x81)\x81+\x81-\x81/\x811\x813\x815\x817\x819\x81;\x81=\x81?\x81A\x81C\x81E\x81G\x81I\x81K\x81M\x81O\x81Q\x81S\x81U\x81W\x81Y\x81[\x81]\x81_\x81a\x81c\x81e\x81g\x81i\x81k\x81m\x81o\x81q\x81s\x81u\x81w\x81y\x81{\x81}\x81\x7f\x82\x01\x82\x03\x82\x05\x82\x07\x82\t\x82\x0b\x82\r\x82\x0f\x82\x11\x82\x13\x82\x15\x82\x17\x82\x19\x82\x1b\x82\x1d\x82\x1f\x82!\x82#\x82%\x82'\x82)\x82+\x82-\x82/\x821\x823\x825\x827\x829\x82;\x82=\x82?\x82A\x82E\x82G\x82I\x82K\nname0\x00\nname1\x00\nname2\x00\nname3\x00\nname4\x00\nname5\x00\nname6\x00\nname7\x00\nname8\x00\nname9\x00$quite_a_long_name0\x00$quite_a_long_name1\x00$quite_a_long_name2\x00$quite_a_long_name3\x00$quite_a_long_name4\x00$quite_a_long_name5\x00$quite_a_long_name6\x00$quite_a_long_name7\x00$quite_a_long_name8\x00$quite_a_long_name9\x00&quite_a_long_name10\x00&quite_a_long_name11\x00\x05\x1ethis will be a string object 0\x00\x05\x1ethis will be a string object 1\x00\x05\x1ethis will be a string object 2\x00\x05\x1ethis will be a string object 3\x00\x05\x1ethis will be a string object 4\x00\x05\x1ethis will be a string object 5\x00\x05\x1ethis will be a string object 6\x00\x05\x1ethis will be a string object 7\x00\x05\x1ethis will be a string object 8\x00\x05\x1ethis will be a string object 9\x00\x06\x1dthis will be a bytes object 0\x00\x06\x1dthis will be a bytes object 1\x00\x06\x1dthis will be a bytes object 2\x00\x06\x1dthis will be a bytes object 3\x00\x06\x1dthis will be a bytes object 4\x00\x06\x1dthis will be a bytes object 5\x00\x06\x1dthis will be a bytes object 6\x00\x06\x1dthis will be a bytes object 7\x00\x06\x1dthis will be a bytes object 8\x00\x06\x1dthis will be a bytes object 9\x00\n\x07\x05\rconst tuple 0\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 1\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 2\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 3\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 4\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 5\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 6\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 7\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 8\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 9\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x82d\x10\x12\x01i@i@\x84\x18\x84\x1fT2\x00\x10\x024\x02\x16\x02T2\x01\x10\x034\x02\x16\x032\x02\x16\x042\x03\x16\x05\"\x80{\x16\x06Qc\x04\x82\x0c\x00\n\x02($$$\x11\x07\x16\x08\x10\x02\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04@\t\x08\n\x81\x0b Qc@\t\x08\x0b\x81\x0b@Qc@\t\x08\x0c\x81\x0b`QcH\t\n\r\x81\x0b` Qc\x82\x14\x00\x0c\x03h`$$$\x11\x07\x16\x08\x10\x03\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04H\t\n\n\x81\x0b``QcH\t\n\x0b\x81\x0b\x80\x07QcH\t\n\x0c\x81\x0b\x80\x08QcH\t\n\r\x81\x0b\x80\tQc\xa08P:\x04\x80\x0b13///---997799<\x1f%\x1f\"\x1f%)\x1f\"//\x12\x0e\x12\x0f\x12\x10\x12\x11\x12\x12\x12\x13\x12\x14*\x07Y\x12\x15\x12\x16\x12\x17\x12\x18\x12\x19\x12\x1a\x12\x08\x12\x07*\x08Y\x12\x1b\x12\x1c\x12\t\x12\x1d\x12\x1e\x12\x1f*\x06Y\x12 \x12!\x12\"\x12#\x12$\x12%*\x06Y\x12&\x12'\x12(\x12)\x12*\x12+*\x06Y\x12,\x12-\x12.\x12/\x120*\x05Y\x121\x122\x123\x124\x125*\x05Y\x126\x127\x128\x129\x12:*\x05Y\x12;\x12<\x12=\x12>\x12?\x12@\x12A\x12B\x12C\x12D\x12E*\x0bY\x12F\x12G\x12H\x12I\x12J\x12K\x12L\x12M\x12N\x12O\x12P*\x0bY\x12Q\x12R\x12S\x12T\x12U\x12V\x12W\x12X\x12Y\x12Z*\nY\x12[\x12\\\x12]\x12^\x12_\x12`\x12a\x12b\x12c\x12d*\nY\x12e\x12f\x12g\x12h\x12i\x12j\x12k\x12l\x12m\x12n\x12o*\x0bY\x12p\x12q\x12r\x12s\x12t\x12u\x12v\x12w\x12x\x12y\x12z*\x0bY\x12{\x12|\x12}\x12~\x12\x7f\x12\x81\x00\x12\x81\x01\x12\x81\x02\x12\x81\x03\x12\x81\x04*\nY\x12\x81\x05\x12\x81\x06\x12\x81\x07\x12\x81\x08\x12\x81\t\x12\x81\n\x12\x81\x0b\x12\x81\x0c\x12\x81\r\x12\x81\x0e\x12\x81\x0f*\x0bY\x12\x81\x10\x12\x81\x11\x12\x81\x12\x12\x81\x13\x12\x81\x14\x12\x81\x15\x12\x81\x16\x12\x81\x17\x12\x81\x18\x12\x81\x19*\nY\x12\x81\x1a\x12\x81\x1b\x12\x81\x1c\x12\x81\x1d\x12\x81\x1e\x12\x81\x1f\x12\x81 \x12\x81!\x12\x81\"\x12\x81#\x12\x81$*\x0bY\x12\x81%\x12\x81&*\x02Y\x12\x81'\x12\x81(\x12\x81)\x12\x81*\x12\x81+\x12\x81,\x12\x81-\x12\x81.\x12\x81/\x12\x810*\nY\x12\x811\x12\x812\x12\x813\x12\x814*\x04Y\x12\x815\x12\x816\x12\x817\x12\x818*\x04Y\x12\x819\x12\x81:\x12\x81;\x12\x81<*\x04YQc\x87p\x08@\x05\x80###############################\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14\xc0#\x15\xc0#\x16\xc0#\x17\xc0#\x18\xc0#\x19\xc0#\x1a\xc0#\x1b\xc0#\x1c\xc0#\x1d\xc0Qc" class File(io.IOBase): diff --git a/tests/run-internalbench.py b/tests/run-internalbench.py index c9f783e474c9c..99c6304afe9d6 100755 --- a/tests/run-internalbench.py +++ b/tests/run-internalbench.py @@ -8,6 +8,10 @@ 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 + if os.name == "nt": MICROPYTHON = os.getenv( "MICROPY_MICROPYTHON", "../ports/windows/build-standard/micropython.exe" @@ -15,13 +19,39 @@ else: MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/build-standard/micropython") +injected_bench_code = b""" +import time + +class bench_class: + ITERS = 20000000 + + @staticmethod + def run(test): + t = time.ticks_us() + test(bench_class.ITERS) + t = time.ticks_diff(time.ticks_us(), t) + s, us = divmod(t, 1_000_000) + print("{}.{:06}".format(s, us)) + +import sys +sys.modules['bench'] = bench_class +""" + -def run_tests(pyb, test_dict): +def execbench(pyb, filename, iters): + with open(filename, "rb") as f: + pyfile = f.read() + code = (injected_bench_code + pyfile).replace(b"20000000", str(iters).encode("utf-8")) + return pyb.exec(code).replace(b"\r\n", b"\n") + + +def run_tests(pyb, test_dict, iters): test_count = 0 testcase_count = 0 for base_test, tests in sorted(test_dict.items()): print(base_test + ":") + baseline = None for test_file in tests: # run MicroPython if pyb is None: @@ -36,20 +66,25 @@ def run_tests(pyb, test_dict): # run on pyboard pyb.enter_raw_repl() try: - output_mupy = pyb.execfile(test_file).replace(b"\r\n", b"\n") + output_mupy = execbench(pyb, test_file[0], iters) except pyboard.PyboardError: output_mupy = b"CRASH" - output_mupy = float(output_mupy.strip()) + try: + output_mupy = float(output_mupy.strip()) + except ValueError: + output_mupy = -1 test_file[1] = output_mupy testcase_count += 1 - test_count += 1 - baseline = None - for t in tests: if baseline is None: - baseline = t[1] - print(" %.3fs (%+06.2f%%) %s" % (t[1], (t[1] * 100 / baseline) - 100, t[0])) + baseline = test_file[1] + print( + " %.3fs (%+06.2f%%) %s" + % (test_file[1], (test_file[1] * 100 / baseline) - 100, test_file[0]) + ) + + test_count += 1 print("{} tests performed ({} individual testcases)".format(test_count, testcase_count)) @@ -58,27 +93,47 @@ def run_tests(pyb, test_dict): def main(): - cmd_parser = argparse.ArgumentParser(description="Run tests for MicroPython.") - cmd_parser.add_argument("--pyboard", action="store_true", help="run the tests on the pyboard") + cmd_parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=f"""Run and manage tests for MicroPython. + +{run_tests_module.test_instance_description} +{run_tests_module.test_directory_description} +""", + epilog=run_tests_module.test_instance_epilog, + ) + cmd_parser.add_argument( + "-t", "--test-instance", default="unix", help="the MicroPython instance to test" + ) + cmd_parser.add_argument( + "-b", "--baudrate", default=115200, help="the baud rate of the serial device" + ) + cmd_parser.add_argument("-u", "--user", default="micro", help="the telnet login username") + cmd_parser.add_argument("-p", "--password", default="python", help="the telnet login password") + cmd_parser.add_argument( + "-d", "--test-dirs", nargs="*", help="input test directories (if no files given)" + ) + cmd_parser.add_argument( + "-I", + "--iters", + type=int, + default=200_000, + help="number of test iterations, only for remote instances (default 200,000)", + ) cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() # Note pyboard support is copied over from run-tests.py, not tests, and likely needs revamping - if args.pyboard: - import pyboard - - pyb = pyboard.Pyboard("/dev/ttyACM0") - pyb.enter_raw_repl() - else: - pyb = None + pyb = run_tests_module.get_test_instance( + args.test_instance, args.baudrate, args.user, args.password + ) if len(args.files) == 0: - if pyb is None: - # run PC tests - test_dirs = ("internal_bench",) + if args.test_dirs: + test_dirs = tuple(args.test_dirs) else: - # run pyboard tests - test_dirs = ("basics", "float", "pyb") + test_dirs = ("internal_bench",) + tests = sorted( test_file for test_files in (glob("{}/*.py".format(dir)) for dir in test_dirs) @@ -95,7 +150,7 @@ def main(): continue test_dict[m.group(1)].append([t, None]) - if not run_tests(pyb, test_dict): + if not run_tests(pyb, test_dict, args.iters): sys.exit(1) diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 387eec7018bb3..e5458ffe0d00c 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -15,6 +15,8 @@ import subprocess import tempfile +run_tests_module = __import__("run-tests") + test_dir = os.path.abspath(os.path.dirname(__file__)) if os.path.abspath(sys.path[0]) == test_dir: @@ -130,6 +132,11 @@ def get_host_ip(_ip_cache=[]): return _ip_cache[0] +def decode(output): + # Convenience function to convert raw process or serial output to ASCII + return str(output, "ascii", "backslashreplace") + + class PyInstance: def __init__(self): pass @@ -188,7 +195,7 @@ def run_script(self, script): output = p.stdout except subprocess.CalledProcessError as er: err = er - return str(output.strip(), "ascii"), err + return decode(output.strip()), err def start_script(self, script): self.popen = subprocess.Popen( @@ -215,7 +222,7 @@ def readline(self): self.finished = self.popen.poll() is not None return None, None else: - return str(out.rstrip(), "ascii"), None + return decode(out.rstrip()), None def write(self, data): self.popen.stdin.write(data) @@ -227,21 +234,12 @@ def is_finished(self): def wait_finished(self): self.popen.wait() out = self.popen.stdout.read() - return str(out, "ascii"), "" + return decode(out), "" class PyInstancePyboard(PyInstance): - @staticmethod - def map_device_shortcut(device): - if device[0] == "a" and device[1:].isdigit(): - return "/dev/ttyACM" + device[1:] - elif device[0] == "u" and device[1:].isdigit(): - return "/dev/ttyUSB" + device[1:] - else: - return device - def __init__(self, device): - device = self.map_device_shortcut(device) + device = device self.device = device self.pyb = pyboard.Pyboard(device) self.pyb.enter_raw_repl() @@ -262,7 +260,7 @@ def run_script(self, script): output = self.pyb.exec_(script) except pyboard.PyboardError as er: err = er - return str(output.strip(), "ascii"), err + return decode(output.strip()), err def start_script(self, script): self.pyb.enter_raw_repl() @@ -281,13 +279,13 @@ def readline(self): if out.endswith(b"\x04"): self.finished = True out = out[:-1] - err = str(self.pyb.read_until(1, b"\x04"), "ascii") + err = decode(self.pyb.read_until(1, b"\x04")) err = err[:-1] if not out and not err: return None, None else: err = None - return str(out.rstrip(), "ascii"), err + return decode(out.rstrip()), err def write(self, data): self.pyb.serial.write(data) @@ -297,7 +295,7 @@ def is_finished(self): def wait_finished(self): out, err = self.pyb.follow(10, None) - return str(out, "ascii"), str(err, "ascii") + return decode(out), decode(err) def prepare_test_file_list(test_files): @@ -488,9 +486,7 @@ def print_diff(a, b): def run_tests(test_files, instances_truth, instances_test): - skipped_tests = [] - passed_tests = [] - failed_tests = [] + test_results = [] for test_file, num_instances in test_files: instances_str = "|".join(str(instances_test[i]) for i in range(num_instances)) @@ -526,13 +522,13 @@ def run_tests(test_files, instances_truth, instances_test): # Print result of test if skip: print("skip") - skipped_tests.append(test_file) + test_results.append((test_file, "skip", "")) elif output_test == output_truth: print("pass") - passed_tests.append(test_file) + test_results.append((test_file, "pass", "")) else: print("FAIL") - failed_tests.append(test_file) + test_results.append((test_file, "fail", "")) if not cmd_args.show_output: print("### TEST ###") print(output_test, end="") @@ -549,15 +545,7 @@ def run_tests(test_files, instances_truth, instances_test): if cmd_args.show_output: print() - print("{} tests performed".format(len(skipped_tests) + len(passed_tests) + len(failed_tests))) - print("{} tests passed".format(len(passed_tests))) - - if skipped_tests: - print("{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests))) - if failed_tests: - print("{} tests failed: {}".format(len(failed_tests), " ".join(failed_tests))) - - return not failed_tests + return test_results def main(): @@ -565,16 +553,24 @@ def main(): cmd_parser = argparse.ArgumentParser( description="Run network tests for MicroPython", + epilog=( + run_tests_module.test_instance_epilog + + "Each instance arg can optionally have custom env provided, eg. ,ENV=VAR,ENV=VAR...\n" + ), formatter_class=argparse.RawTextHelpFormatter, ) cmd_parser.add_argument( "-s", "--show-output", action="store_true", help="show test output after running" ) cmd_parser.add_argument( - "-t", "--trace-output", action="store_true", help="trace test output while running" + "-c", "--trace-output", action="store_true", help="trace test output while running" ) cmd_parser.add_argument( - "-i", "--instance", action="append", default=[], help="instance(s) to run the tests on" + "-t", + "--test-instance", + action="append", + default=[], + help="instance(s) to run the tests on", ) cmd_parser.add_argument( "-p", @@ -583,13 +579,11 @@ def main(): default=1, help="repeat the test with this many permutations of the instance order", ) - cmd_parser.epilog = ( - "Supported instance types:\r\n" - " -i pyb: physical device (eg. pyboard) on provided repl port.\n" - " -i micropython unix micropython instance, path customised with MICROPY_MICROPYTHON env.\n" - " -i cpython desktop python3 instance, path customised with MICROPY_CPYTHON3 env.\n" - " -i exec: custom program run on provided path.\n" - "Each instance arg can optionally have custom env provided, eg. ,ENV=VAR,ENV=VAR...\n" + cmd_parser.add_argument( + "-r", + "--result-dir", + default=run_tests_module.base_path("results"), + help="directory for test results", ) cmd_parser.add_argument("files", nargs="+", help="input test files") cmd_args = cmd_parser.parse_args() @@ -603,33 +597,36 @@ def main(): instances_truth = [PyInstanceSubProcess([PYTHON_TRUTH]) for _ in range(max_instances)] instances_test = [] - for i in cmd_args.instance: + for i in cmd_args.test_instance: # Each instance arg is ,ENV=VAR,ENV=VAR... i = i.split(",") cmd = i[0] env = i[1:] if cmd.startswith("exec:"): instances_test.append(PyInstanceSubProcess([cmd[len("exec:") :]], env)) - elif cmd == "micropython": + elif cmd == "unix": instances_test.append(PyInstanceSubProcess([MICROPYTHON], env)) elif cmd == "cpython": instances_test.append(PyInstanceSubProcess([CPYTHON3], env)) - elif cmd.startswith("pyb:"): - instances_test.append(PyInstancePyboard(cmd[len("pyb:") :])) + elif cmd == "webassembly" or cmd.startswith("execpty:"): + print("unsupported instance string: {}".format(cmd), file=sys.stderr) + sys.exit(2) else: - print("unknown instance string: {}".format(cmd), file=sys.stderr) - sys.exit(1) + device = run_tests_module.convert_device_shortcut_to_real_device(cmd) + instances_test.append(PyInstancePyboard(device)) for _ in range(max_instances - len(instances_test)): instances_test.append(PyInstanceSubProcess([MICROPYTHON])) + os.makedirs(cmd_args.result_dir, exist_ok=True) all_pass = True try: for i, instances_test_permutation in enumerate(itertools.permutations(instances_test)): if i >= cmd_args.permutations: break - all_pass &= run_tests(test_files, instances_truth, instances_test_permutation) + test_results = run_tests(test_files, instances_truth, instances_test_permutation) + all_pass &= run_tests_module.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 340a7f004b061..6d2a975b82a0b 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -9,7 +9,7 @@ import sys import argparse -# CIRCUITPY-CHANGE: no pyboard +run_tests_module = __import__("run-tests") # Paths for host executables CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") @@ -72,6 +72,7 @@ def open(self, path, mode): # CIRCUITPY-CHANGE: no vfs, but still have os os.mount(__FS(), '/__remote') sys.path.insert(0, '/__remote') +{import_prelude} sys.modules['{}'] = __import__('__injected') """ @@ -108,7 +109,7 @@ def run_script(self, script): output = self.pyb.exec_(script) output = output.replace(b"\r\n", b"\n") return output, None - except pyboard.PyboardError as er: + except run_tests_module.pyboard.PyboardError as er: return b"", er @@ -131,7 +132,15 @@ def detect_architecture(target): return platform, arch, None -def run_tests(target_truth, target, args, stats, resolved_arch): +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 test_file_basename = os.path.basename(test_file) @@ -154,9 +163,11 @@ def run_tests(target_truth, target, args, stats, resolved_arch): with open(NATMOD_EXAMPLE_DIR + test_mpy, "rb") as f: test_script += b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n" except OSError: - print("---- {} - mpy file not compiled".format(test_file)) + test_results.append((test_file, "skip", "mpy file not compiled")) + print("skip {} - mpy file not compiled".format(test_file)) continue test_script += bytes(injected_import_hook_code.format(test_module), "ascii") + test_script += b"print('START TEST')\n" test_script += test_file_data # Run test under MicroPython @@ -164,8 +175,18 @@ def run_tests(target_truth, target, args, stats, resolved_arch): # Work out result of test extra = "" + result_out = result_out.removeprefix(b"START TEST\n") if error is None and result_out == b"SKIP\n": result = "SKIP" + elif ( + error is not None + and error.args[0] == "exception" + and error.args[1] == b"" + and b"MemoryError" in error.args[2] + ): + # Test had a MemoryError before anything (should be at least "START TEST") + # was printed, so the test is too big for the target. + result = "LRGE" elif error is not None: result = "FAIL" extra = " - " + str(error) @@ -186,40 +207,63 @@ def run_tests(target_truth, target, args, stats, resolved_arch): result = "pass" # Accumulate statistics - stats["total"] += 1 if result == "pass": - stats["pass"] += 1 + test_results.append((test_file, "pass", "")) elif result == "SKIP": - stats["skip"] += 1 + test_results.append((test_file, "skip", "")) + elif result == "LRGE": + test_results.append((test_file, "skip", "too large")) else: - stats["fail"] += 1 + test_results.append((test_file, "fail", "")) # Print result print("{:4} {}{}".format(result, test_file, extra)) + return test_results + def main(): cmd_parser = argparse.ArgumentParser( - description="Run dynamic-native-module tests under MicroPython" + description="Run dynamic-native-module tests under MicroPython", + epilog=run_tests_module.test_instance_epilog, + formatter_class=argparse.RawDescriptionHelpFormatter, ) cmd_parser.add_argument( - "-p", "--pyboard", action="store_true", help="run tests via pyboard.py" + "-t", "--test-instance", default="unix", help="the MicroPython instance to test" ) + cmd_parser.add_argument("--baudrate", default=115200, help="baud rate of the serial device") + cmd_parser.add_argument("--user", default="micro", help="telnet login username") + cmd_parser.add_argument("--password", default="python", help="telnet login password") cmd_parser.add_argument( - "-d", "--device", default="/dev/ttyACM0", help="the device for pyboard.py" + "-a", "--arch", choices=AVAILABLE_ARCHS, help="override native architecture of the target" ) cmd_parser.add_argument( - "-a", "--arch", choices=AVAILABLE_ARCHS, help="override native architecture of the target" + "-b", + "--begin", + type=argparse.FileType("rt"), + default=None, + help="prologue python file to execute before module import", + ) + cmd_parser.add_argument( + "-r", + "--result-dir", + default=run_tests_module.base_path("results"), + help="directory for test results", ) cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() target_truth = TargetSubprocess([CPYTHON3]) - if args.pyboard: - target = TargetPyboard(pyboard.Pyboard(args.device)) - else: + target = run_tests_module.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]) + else: + # Use a remote target. + target = TargetPyboard(target) if hasattr(args, "arch") and args.arch is not None: target_arch = args.arch @@ -235,20 +279,14 @@ def main(): print("platform={} ".format(target_platform), end="") print("arch={}".format(target_arch)) - stats = {"total": 0, "pass": 0, "fail": 0, "skip": 0} - run_tests(target_truth, target, args, stats, target_arch) + 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) target.close() target_truth.close() - print("{} tests performed".format(stats["total"])) - print("{} tests passed".format(stats["pass"])) - if stats["fail"]: - print("{} tests failed".format(stats["fail"])) - if stats["skip"]: - print("{} tests skipped".format(stats["skip"])) - - if stats["fail"]: + if not res: sys.exit(1) diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index 81d873c45997d..039d11a36111e 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -10,10 +10,9 @@ import argparse from glob import glob -sys.path.append("../tools") -import pyboard +run_tests_module = __import__("run-tests") -prepare_script_for_target = __import__("run-tests").prepare_script_for_target +prepare_script_for_target = run_tests_module.prepare_script_for_target # Paths for host executables if os.name == "nt": @@ -45,12 +44,12 @@ def run_script_on_target(target, script): output = b"" err = None - if isinstance(target, pyboard.Pyboard): + if hasattr(target, "enter_raw_repl"): # Run via pyboard interface try: target.enter_raw_repl() output = target.exec_(script) - except pyboard.PyboardError as er: + except run_tests_module.pyboard.PyboardError as er: err = er else: # Run local executable @@ -90,9 +89,9 @@ def run_benchmark_on_target(target, script): def run_benchmarks(args, target, param_n, param_m, n_average, test_list): + test_results = [] skip_complex = run_feature_test(target, "complex") != "complex" skip_native = run_feature_test(target, "native_check") != "native" - target_had_error = False for test_file in sorted(test_list): print(test_file + ": ", end="") @@ -105,6 +104,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): and test_file.find("viper_") != -1 ) if skip: + test_results.append((test_file, "skip", "")) print("SKIP") continue @@ -122,9 +122,10 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): f.write(test_script) # Process script through mpy-cross if needed - if isinstance(target, pyboard.Pyboard) or args.via_mpy: + if hasattr(target, "enter_raw_repl") or args.via_mpy: crash, test_script_target = prepare_script_for_target(args, script_text=test_script) if crash: + test_results.append((test_file, "fail", "preparation")) print("CRASH:", test_script_target) continue else: @@ -162,10 +163,13 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): error = "FAIL truth" if error is not None: - if not error.startswith("SKIP"): - target_had_error = True + if error.startswith("SKIP"): + test_results.append((test_file, "skip", error)) + else: + test_results.append((test_file, "fail", error)) print(error) else: + test_results.append((test_file, "pass", "")) t_avg, t_sd = compute_stats(times) s_avg, s_sd = compute_stats(scores) print( @@ -179,7 +183,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): sys.stdout.flush() - return target_had_error + return test_results def parse_output(filename): @@ -190,7 +194,13 @@ def parse_output(filename): m = int(m.split("=")[1]) data = [] for l in f: - if ": " in l and ": SKIP" not in l and "CRASH: " not in l: + if ( + ": " in l + and ": SKIP" not in l + and "CRASH: " not in l + and "skipped: " not in l + and "failed: " not in l + ): name, values = l.strip().split(": ") values = tuple(float(v) for v in values.split()) data.append((name,) + values) @@ -246,17 +256,17 @@ def compute_diff(file1, file2, diff_score): def main(): cmd_parser = argparse.ArgumentParser(description="Run benchmarks for MicroPython") cmd_parser.add_argument( - "-t", "--diff-time", action="store_true", help="diff time outputs from a previous run" + "-m", "--diff-time", action="store_true", help="diff time outputs from a previous run" ) cmd_parser.add_argument( "-s", "--diff-score", action="store_true", help="diff score outputs from a previous run" ) cmd_parser.add_argument( - "-p", "--pyboard", action="store_true", help="run tests via pyboard.py" - ) - cmd_parser.add_argument( - "-d", "--device", default="/dev/ttyACM0", help="the device for pyboard.py" + "-t", "--test-instance", default="unix", help="the MicroPython instance to test" ) + cmd_parser.add_argument("--baudrate", default=115200, help="baud rate of the serial device") + cmd_parser.add_argument("--user", default="micro", help="telnet login username") + cmd_parser.add_argument("--password", default="python", help="telnet login password") cmd_parser.add_argument("-a", "--average", default="8", help="averaging number") cmd_parser.add_argument( "--emit", default="bytecode", help="MicroPython emitter to use (bytecode or native)" @@ -264,6 +274,12 @@ def main(): cmd_parser.add_argument("--heapsize", help="heapsize to use (use default if not specified)") cmd_parser.add_argument("--via-mpy", action="store_true", help="compile code to .mpy first") cmd_parser.add_argument("--mpy-cross-flags", default="", help="flags to pass to mpy-cross") + cmd_parser.add_argument( + "-r", + "--result-dir", + default=run_tests_module.base_path("results"), + help="directory for test results", + ) cmd_parser.add_argument( "N", nargs=1, help="N parameter (approximate target CPU frequency in MHz)" ) @@ -282,15 +298,18 @@ def main(): M = int(args.M[0]) n_average = int(args.average) - if args.pyboard: - if not args.mpy_cross_flags: - args.mpy_cross_flags = "-march=armv7m" - target = pyboard.Pyboard(args.device) - target.enter_raw_repl() - else: + target = run_tests_module.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] if args.heapsize is not None: target.extend(["-X", "heapsize=" + args.heapsize]) + else: + # Use a remote target. + if not args.mpy_cross_flags: + args.mpy_cross_flags = "-march=armv7m" if len(args.files) == 0: tests_skip = ("benchrun.py",) @@ -307,13 +326,15 @@ def main(): print("N={} M={} n_average={}".format(N, M, n_average)) - target_had_error = run_benchmarks(args, target, N, M, n_average, tests) + 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) - if isinstance(target, pyboard.Pyboard): + if hasattr(target, "exit_raw_repl"): target.exit_raw_repl() target.close() - if target_had_error: + if not res: sys.exit(1) diff --git a/tests/run-tests.py b/tests/run-tests.py index cb839ae23dcdf..71570292ce7f7 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -15,13 +15,15 @@ import threading import tempfile -# Maximum time to run a PC-based test, in seconds. -TEST_TIMEOUT = 30 +# 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))) +RV32_ARCH_FLAGS = {"zba": 1 << 0} + def base_path(*p): return os.path.abspath(os.path.join(BASEPATH, *p)).replace("\\", "/") @@ -58,6 +60,23 @@ def base_path(*p): # 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. @@ -88,31 +107,82 @@ def getcwd(self): return "" def stat(self, path): if path == '__injected_test.mpy': - return tuple(0 for _ in range(10)) + return (0,0,0,0,0,0,0,0,0,0) else: - raise OSError(-2) # ENOENT + 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") +# Mapping from `sys.platform` to the port name, for special cases. +# See `platform_to_port()` function. +platform_to_port_map = {"pyboard": "stm32", "WiPy": "cc3200"} +platform_to_port_map.update({p: "unix" for p in PC_PLATFORMS}) + +# Tests to skip for values of the `--via-mpy` argument. +via_mpy_tests_to_skip = { + # Skip the following when mpy is enabled. + True: ( + # These print out the filename and that's expected to match the .py name. + "import/import_file.py", + "io/argv.py", + "misc/sys_settrace_features.py", + "misc/sys_settrace_generator.py", + "misc/sys_settrace_loop.py", + ), +} + +# Tests to skip for specific emitters. +emitter_tests_to_skip = { + # Some tests are known to fail with native emitter. + # Remove them from the below when they work. + "native": ( + # These require raise_varargs. + "basics/gen_yield_from_close.py", + "basics/try_finally_return2.py", + "basics/try_reraise.py", + "basics/try_reraise2.py", + "misc/features.py", + # These require checking for unbound local. + "basics/annotate_var.py", + "basics/del_deref.py", + "basics/del_local.py", + "basics/scope_implicit.py", + "basics/unboundlocal.py", + # These require "raise from". + "basics/exception_chain.py", + # These require stack-allocated slice optimisation. + "micropython/heapalloc_slice.py", + # These require running the scheduler. + "micropython/schedule.py", + "extmod/asyncio_event_queue.py", + "extmod/asyncio_iterator_event.py", + # These require sys.exc_info(). + "misc/sys_exc_info.py", + # These require sys.settrace(). + "misc/sys_settrace_cov.py", + "misc/sys_settrace_features.py", + "misc/sys_settrace_generator.py", + "misc/sys_settrace_loop.py", + # These are bytecode-specific tests. + "stress/bytecode_limit.py", + ), +} + # Tests to skip on specific targets. # These are tests that are difficult to detect that they should not be run on the given target. platform_tests_to_skip = { - "esp8266": ( - "micropython/viper_args.py", # too large - "micropython/viper_binop_arith.py", # too large - "misc/rge_sm.py", # too large - ), "minimal": ( "basics/class_inplace_op.py", # all special methods not supported "basics/subclass_native_init.py", # native subclassing corner cases not support - "misc/rge_sm.py", # too large "micropython/opt_level.py", # don't assume line numbers are stored ), "nrf": ( @@ -140,14 +210,6 @@ def open(self, path, mode): "thread/thread_lock3.py", "thread/thread_shared2.py", ), - "qemu": ( - # Skip tests that require Cortex-M4. - "inlineasm/thumb/asmfpaddsub.py", - "inlineasm/thumb/asmfpcmp.py", - "inlineasm/thumb/asmfpldrstr.py", - "inlineasm/thumb/asmfpmuldiv.py", - "inlineasm/thumb/asmfpsqrt.py", - ), "webassembly": ( "basics/string_format_modulo.py", # can't print nulls to stdout "basics/string_strip.py", # can't print nulls to stdout @@ -162,6 +224,9 @@ def open(self, path, mode): "extmod/asyncio_new_event_loop.py", "extmod/asyncio_threadsafeflag.py", "extmod/asyncio_wait_for_fwd.py", + "extmod/asyncio_event_queue.py", + "extmod/asyncio_iterator_event.py", + "extmod/asyncio_wait_for_linked_task.py", "extmod/binascii_a2b_base64.py", "extmod/deflate_compress_memory_error.py", # tries to allocate unlimited memory "extmod/re_stack_overflow.py", @@ -184,6 +249,89 @@ def open(self, path, mode): ), } +# These tests don't test float explicitly but rather use it to perform the test. +tests_requiring_float = ( + "extmod/asyncio_basic.py", + "extmod/asyncio_basic2.py", + "extmod/asyncio_cancel_task.py", + "extmod/asyncio_event.py", + "extmod/asyncio_fair.py", + "extmod/asyncio_gather.py", + "extmod/asyncio_gather_notimpl.py", + "extmod/asyncio_get_event_loop.py", + "extmod/asyncio_iterator_event.py", + "extmod/asyncio_lock.py", + "extmod/asyncio_task_done.py", + "extmod/asyncio_wait_for.py", + "extmod/asyncio_wait_for_fwd.py", + "extmod/asyncio_wait_for_linked_task.py", + "extmod/asyncio_wait_task.py", + "extmod/json_dumps_float.py", + "extmod/json_loads_float.py", + "extmod/random_extra_float.py", + "extmod/select_poll_eintr.py", + "extmod/tls_threads.py", + "extmod/uctypes_le_float.py", + "extmod/uctypes_native_float.py", + "extmod/uctypes_sizeof_float.py", + "misc/rge_sm.py", + "ports/unix/ffi_float.py", + "ports/unix/ffi_float2.py", +) + +# These tests don't test slice explicitly but rather use it to perform the test. +tests_requiring_slice = ( + "basics/builtin_range.py", + "basics/bytearray1.py", + "basics/class_super.py", + "basics/containment.py", + "basics/errno1.py", + "basics/fun_str.py", + "basics/generator1.py", + "basics/globals_del.py", + "basics/memoryview1.py", + "basics/memoryview_gc.py", + "basics/object1.py", + "basics/python34.py", + "basics/struct_endian.py", + "extmod/btree1.py", + "extmod/deflate_decompress.py", + "extmod/framebuf16.py", + "extmod/framebuf4.py", + "extmod/machine1.py", + "extmod/time_mktime.py", + "extmod/time_res.py", + "extmod/tls_sslcontext_ciphers.py", + "extmod/vfs_fat_fileio1.py", + "extmod/vfs_fat_finaliser.py", + "extmod/vfs_fat_more.py", + "extmod/vfs_fat_ramdisk.py", + "extmod/vfs_fat_ramdisklarge.py", + "extmod/vfs_lfs.py", + "extmod/vfs_rom.py", + "float/string_format_modulo.py", + "micropython/builtin_execfile.py", + "micropython/extreme_exc.py", + "micropython/heapalloc_fail_bytearray.py", + "micropython/heapalloc_fail_list.py", + "micropython/heapalloc_fail_memoryview.py", + "micropython/import_mpy_invalid.py", + "micropython/import_mpy_native.py", + "micropython/import_mpy_native_gc.py", + "misc/non_compliant.py", + "misc/rge_sm.py", +) + +# Tests that require `import target_wiring` to work. +tests_requiring_target_wiring = ( + "extmod/machine_uart_irq_txidle.py", + "extmod/machine_uart_tx.py", + "extmod_hardware/machine_encoder.py", + "extmod_hardware/machine_uart_irq_break.py", + "extmod_hardware/machine_uart_irq_rx.py", + "extmod_hardware/machine_uart_irq_rxidle.py", +) + def rm_f(fname): if os.path.exists(fname): @@ -210,22 +358,31 @@ def convert_regex_escapes(line): return bytes("".join(cs), "utf8") +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.startswith("port:"): - _, port = test_instance.split(":", 1) - elif test_instance == "unix": + if test_instance == "unix": return None elif test_instance == "webassembly": return PyboardNodeRunner() - elif test_instance.startswith("a") and test_instance[1:].isdigit(): - port = "/dev/ttyACM" + test_instance[1:] - elif test_instance.startswith("u") and test_instance[1:].isdigit(): - port = "/dev/ttyUSB" + test_instance[1:] - elif test_instance.startswith("c") and test_instance[1:].isdigit(): - port = "COM" + test_instance[1:] else: # Assume it's a device path. - port = test_instance + port = convert_device_shortcut_to_real_device(test_instance) global pyboard sys.path.append(base_path("../tools")) @@ -245,46 +402,114 @@ def detect_inline_asm_arch(pyb, args): return None +def map_rv32_arch_flags(flags): + mapped_flags = [] + for extension, bit in RV32_ARCH_FLAGS.items(): + if flags & bit: + mapped_flags.append(extension) + flags &= ~bit + if flags: + raise Exception("Unexpected flag bits set in value {}".format(flags)) + return mapped_flags + + def detect_test_platform(pyb, args): # Run a script to detect various bits of information about the target test instance. output = run_feature_check(pyb, args, "target_info.py") if output.endswith(b"CRASH"): raise ValueError("cannot detect platform: {}".format(output)) - platform, arch = str(output, "ascii").strip().split() + platform, arch, arch_flags, build, thread, float_prec, unicode = ( + str(output, "ascii").strip().split() + ) if arch == "None": arch = None inlineasm_arch = detect_inline_asm_arch(pyb, args) + if thread == "None": + thread = None + float_prec = int(float_prec) + unicode = unicode == "True" + if arch == "rv32imc": + arch_flags = map_rv32_arch_flags(int(arch_flags)) + else: + arch_flags = None args.platform = platform args.arch = arch + args.arch_flags = arch_flags if arch and not args.mpy_cross_flags: args.mpy_cross_flags = "-march=" + arch + if arch_flags: + args.mpy_cross_flags += " -march-flags=" + ",".join(arch_flags) args.inlineasm_arch = inlineasm_arch + args.build = build + args.thread = thread + args.float_prec = float_prec + args.unicode = unicode + # Print the detected information about the target. print("platform={}".format(platform), end="") if arch: print(" arch={}".format(arch), end="") + if arch_flags: + print(" arch_flags={}".format(",".join(arch_flags)), end="") if inlineasm_arch: print(" inlineasm={}".format(inlineasm_arch), end="") - print() - - -def prepare_script_for_target(args, *, script_filename=None, script_text=None, force_plain=False): + if thread: + print(" thread={}".format(thread), end="") + if float_prec: + print(" float={}-bit".format(float_prec), end="") + if unicode: + print(" unicode", end="") + + +def detect_target_wiring_script(pyb, args): + tw_data = b"" + tw_source = None + if args.target_wiring: + # A target_wiring path is explicitly provided, so use that. + tw_source = args.target_wiring + with open(tw_source, "rb") as f: + tw_data = f.read() + elif hasattr(pyb, "exec_raw") and pyb.exec_raw("import target_wiring") == (b"", b""): + # The board already has a target_wiring module available, so use that. + tw_source = "on-device" + else: + port = platform_to_port(args.platform) + build = args.build + tw_board_exact = None + tw_board_partial = None + tw_port = None + for file in os.listdir("target_wiring"): + file_base = file.removesuffix(".py") + if file_base == build: + # A file matching the target's board/build name. + tw_board_exact = file + elif file_base.endswith("x") and build.startswith(file_base.removesuffix("x")): + # A file with a partial match to the target's board/build name. + tw_board_partial = file + elif file_base == port: + # A file matching the target's port. + tw_port = file + tw_source = tw_board_exact or tw_board_partial or tw_port + if tw_source: + with open("target_wiring/" + tw_source, "rb") as f: + tw_data = f.read() + if tw_source: + print(" target_wiring={}".format(tw_source), end="") + 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"): - if script_filename is not None: - with open(script_filename, "rb") as f: - script_text = f.read() + # A plain test to run as-is, no processing needed. + pass elif args.via_mpy: tempname = tempfile.mktemp(dir="") mpy_filename = tempname + ".mpy" - if script_filename is None: - script_filename = tempname + ".py" - cleanup_script_filename = True - with open(script_filename, "wb") as f: - f.write(script_text) - else: - cleanup_script_filename = False + script_filename = tempname + ".py" + with open(script_filename, "wb") as f: + f.write(script_text) try: subprocess.check_output( @@ -300,8 +525,7 @@ def prepare_script_for_target(args, *, script_filename=None, script_text=None, f script_text = b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n" rm_f(mpy_filename) - if cleanup_script_filename: - rm_f(script_filename) + rm_f(script_filename) script_text += bytes(injected_import_hook_code, "ascii") else: @@ -312,34 +536,61 @@ def prepare_script_for_target(args, *, script_filename=None, script_text=None, f def run_script_on_remote_target(pyb, args, test_file, is_special): - had_crash, script = prepare_script_for_target( - args, script_filename=test_file, force_plain=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() - output_mupy = pyb.exec_(script) + 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": - output_mupy = e.args[1] + e.args[2] + b"CRASH" + 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 -special_tests = [ +tests_with_regex_output = [ base_path(file) for file in ( + # CIRCUITPY-CHANGE: removal and additions "micropython/meminfo.py", "basics/bytes_compare3.py", "basics/builtin_help.py", "thread/thread_exc2.py", - # CIRCUITPY-CHANGE: removal and additions - # REMOVE "esp32/partition_ota.py", "circuitpython/traceback_test.py", "circuitpython/traceback_test_chained.py", ) @@ -350,10 +601,7 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False): had_crash = False if pyb is None: # run on PC - if ( - test_file_abspath.startswith((base_path("cmdline/"), base_path("feature_check/"))) - or test_file_abspath in special_tests - ): + if test_file_abspath.startswith((base_path("cmdline/"), base_path("feature_check/"))): # special handling for tests of the unix cmdline program is_special = True @@ -392,6 +640,10 @@ def get(required=False): return rv def send_get(what): + # Detect {\x00} pattern and convert to ctrl-key codes. + ctrl_code = lambda m: bytes([int(m.group(1))]) + what = re.sub(rb"{\\x(\d\d)}", ctrl_code, what) + os.write(master, what) return get() @@ -471,17 +723,17 @@ def send_get(what): ) # canonical form for all ports/platforms is to use \n for end-of-line - output_mupy = output_mupy.replace(b"\r\n", b"\n") + output_mupy = normalize_newlines(output_mupy) # don't try to convert the output if we should skip this test - if had_crash or output_mupy in (b"SKIP\n", b"CRASH"): + if had_crash or output_mupy in (b"SKIP\n", b"SKIP-TOO-LARGE\n", b"CRASH"): return output_mupy # skipped special tests will output "SKIP" surrounded by other interpreter debug output if is_special and not had_crash and b"\nSKIP\n" in output_mupy: return b"SKIP\n" - if is_special or test_file_abspath in special_tests: + if is_special or test_file_abspath in tests_with_regex_output: # convert parts of the output that are not stable across runs with open(test_file + ".exp", "rb") as f: lines_exp = [] @@ -603,30 +855,25 @@ def run_script_on_remote_target(self, args, test_file, is_special): def run_tests(pyb, tests, args, result_dir, num_threads=1): - test_count = ThreadSafeCounter() testcase_count = ThreadSafeCounter() - passed_count = ThreadSafeCounter() - failed_tests = ThreadSafeCounter([]) - skipped_tests = ThreadSafeCounter([]) + test_results = ThreadSafeCounter([]) skip_tests = set() skip_native = False skip_int_big = False + skip_int_64 = False skip_bytearray = False skip_set_type = False skip_slice = False skip_async = False skip_const = False skip_revops = False - skip_io_module = False skip_fstring = False skip_endian = False skip_inlineasm = False has_complex = True has_coverage = False - upy_float_precision = 32 - if True: # Even if we run completely different tests in a different directory, # we need to access feature_checks from the same directory as the @@ -642,6 +889,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if output != b"1000000000000000000000000000000000000000000000\n": skip_int_big = True + # Check if 'long long' precision integers are supported, even if arbitrary precision is not + output = run_feature_check(pyb, args, "int_64.py") + if output != b"4611686018427387904\n": + skip_int_64 = True + # Check if bytearray is supported, and skip such tests if it's not output = run_feature_check(pyb, args, "bytearray.py") if output != b"bytearray\n": @@ -672,11 +924,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if output == b"TypeError\n": skip_revops = True - # Check if io module exists, and skip such tests if it doesn't - output = run_feature_check(pyb, args, "io_module.py") - if output != b"io\n": - skip_io_module = True - # Check if fstring feature is enabled, and skip such tests if it doesn't output = run_feature_check(pyb, args, "fstring.py") if output != b"a=1\n": @@ -690,13 +937,20 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("inlineasm/thumb/asmbitops.py") skip_tests.add("inlineasm/thumb/asmconst.py") skip_tests.add("inlineasm/thumb/asmdiv.py") + skip_tests.add("inlineasm/thumb/asmit.py") + skip_tests.add("inlineasm/thumb/asmspecialregs.py") + if args.arch not in ("armv7emsp", "armv7emdp"): skip_tests.add("inlineasm/thumb/asmfpaddsub.py") skip_tests.add("inlineasm/thumb/asmfpcmp.py") skip_tests.add("inlineasm/thumb/asmfpldrstr.py") skip_tests.add("inlineasm/thumb/asmfpmuldiv.py") skip_tests.add("inlineasm/thumb/asmfpsqrt.py") - skip_tests.add("inlineasm/thumb/asmit.py") - skip_tests.add("inlineasm/thumb/asmspecialregs.py") + + if args.inlineasm_arch == "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") # Check if emacs repl is supported, and skip such tests if it's not t = run_feature_check(pyb, args, "repl_emacs_check.py") @@ -709,11 +963,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("cmdline/repl_words_move.py") upy_byteorder = run_feature_check(pyb, args, "byteorder.py") - upy_float_precision = run_feature_check(pyb, args, "float.py") - try: - upy_float_precision = int(upy_float_precision) - except ValueError: - upy_float_precision = 0 has_complex = run_feature_check(pyb, args, "complex.py") == b"complex\n" has_coverage = run_feature_check(pyb, args, "coverage.py") == b"coverage\n" cpy_byteorder = subprocess.check_output( @@ -723,24 +972,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_inlineasm = args.inlineasm_arch is None - # These tests don't test slice explicitly but rather use it to perform the test - misc_slice_tests = ( - "builtin_range", - "bytearray1", - "class_super", - "containment", - "errno1", - "fun_str", - "generator1", - "globals_del", - "memoryview1", - "memoryview_gc", - "object1", - "python34", - "string_format_modulo", - "struct_endian", - ) - # 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 @@ -749,30 +980,31 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # fails with stack overflow on Debug builds skip_tests.add("misc/sys_settrace_features.py") - if upy_float_precision == 0: - skip_tests.add("extmod/uctypes_le_float.py") - skip_tests.add("extmod/uctypes_native_float.py") - skip_tests.add("extmod/uctypes_sizeof_float.py") - skip_tests.add("extmod/json_dumps_float.py") - skip_tests.add("extmod/json_loads_float.py") - skip_tests.add("extmod/random_extra_float.py") - skip_tests.add("misc/rge_sm.py") - if upy_float_precision < 32: + if args.float_prec == 0: + skip_tests.update(tests_requiring_float) + if args.float_prec < 32: skip_tests.add( "float/float2int_intbig.py" ) # requires fp32, there's float2int_fp30_intbig.py instead skip_tests.add( - "float/string_format.py" - ) # requires fp32, there's string_format_fp30.py instead + "float/float_struct_e.py" + ) # requires fp32, there's float_struct_e_fp30.py instead skip_tests.add("float/bytes_construct.py") # requires fp32 skip_tests.add("float/bytearray_construct.py") # requires fp32 skip_tests.add("float/float_format_ints_power10.py") # requires fp32 - if upy_float_precision < 64: + if args.float_prec < 64: skip_tests.add("float/float_divmod.py") # tested by float/float_divmod_relaxed.py instead skip_tests.add("float/float2int_doubleprec_intbig.py") + skip_tests.add("float/float_struct_e_doubleprec.py") skip_tests.add("float/float_format_ints_doubleprec.py") skip_tests.add("float/float_parse_doubleprec.py") + if not args.unicode: + skip_tests.add("extmod/json_loads.py") # tests loading a utf-8 character + + if skip_slice: + skip_tests.update(tests_requiring_slice) + if not has_complex: skip_tests.add("float/complex1.py") skip_tests.add("float/complex1_intbig.py") @@ -788,8 +1020,8 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("cmdline/repl_sys_ps1_ps2.py") skip_tests.add("extmod/ssl_poll.py") - # Skip thread mutation tests on targets that don't have the GIL. - if args.platform in PC_PLATFORMS + ("rp2",): + # Skip thread mutation tests on targets that have unsafe threading behaviour. + if args.thread == "unsafe": for t in tests: if t.startswith("thread/mutate_"): skip_tests.add(t) @@ -798,6 +1030,15 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if args.platform not in PC_PLATFORMS: skip_tests.add("basics/exception_chain.py") # warning is not printed skip_tests.add("micropython/meminfo.py") # output is very different to PC output + skip_tests.add("unicode/file1.py") # requires local file access + skip_tests.add("unicode/file2.py") # requires local file access + skip_tests.add("unicode/file_invalid.py") # requires local file access + + # Skip certain tests when going via a .mpy file. + skip_tests.update(via_mpy_tests_to_skip.get(args.via_mpy, ())) + + # Skip emitter-specific tests. + skip_tests.update(emitter_tests_to_skip.get(args.emit, ())) # Skip platform-specific tests. skip_tests.update(platform_tests_to_skip.get(args.platform, ())) @@ -812,49 +1053,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Works but CPython uses '\' path separator skip_tests.add("import/import_file.py") - # Some tests are known to fail with native emitter - # Remove them from the below when they work - if args.emit == "native": - skip_tests.add("basics/gen_yield_from_close.py") # require raise_varargs - skip_tests.update( - {"basics/%s.py" % t for t in "try_reraise try_reraise2".split()} - ) # require raise_varargs - skip_tests.add("basics/annotate_var.py") # requires checking for unbound local - skip_tests.add("basics/del_deref.py") # requires checking for unbound local - skip_tests.add("basics/del_local.py") # requires checking for unbound local - skip_tests.add("basics/exception_chain.py") # raise from is not supported - skip_tests.add("basics/scope_implicit.py") # requires checking for unbound local - skip_tests.add("basics/sys_tracebacklimit.py") # requires traceback info - skip_tests.add("basics/try_finally_return2.py") # requires raise_varargs - skip_tests.add("basics/unboundlocal.py") # requires checking for unbound local - skip_tests.add("misc/features.py") # requires raise_varargs - # CIRCUITPY-CHANGE - skip_tests.update( - ( - "basics/chained_exception.py", - "circuitpython/traceback_test.py", - "circuitpython/traceback_test_chained.py", - ) - ) # because native doesn't have proper traceback info - skip_tests.add( - "misc/print_exception.py" - ) # because native doesn't have proper traceback info - skip_tests.add("misc/sys_exc_info.py") # sys.exc_info() is not supported for native - skip_tests.add("misc/sys_settrace_features.py") # sys.settrace() not supported - skip_tests.add("misc/sys_settrace_generator.py") # sys.settrace() not supported - skip_tests.add("misc/sys_settrace_loop.py") # sys.settrace() not supported - skip_tests.add( - "micropython/emg_exc.py" - ) # because native doesn't have proper traceback info - skip_tests.add( - "micropython/heapalloc_traceback.py" - ) # because native doesn't have proper traceback info - skip_tests.add( - "micropython/opt_level_lineno.py" - ) # native doesn't have proper traceback info - skip_tests.add("micropython/schedule.py") # native code doesn't check pending events - skip_tests.add("stress/bytecode_limit.py") # bytecode specific test - def run_one_test(test_file): test_file = test_file.replace("\\", "/") test_file_abspath = os.path.abspath(test_file).replace("\\", "/") @@ -870,19 +1068,19 @@ def run_one_test(test_file): test_basename = test_file.replace("..", "_").replace("./", "").replace("/", "_") test_name = os.path.splitext(os.path.basename(test_file))[0] - is_native = ( - test_name.startswith("native_") - or test_name.startswith("viper_") - or args.emit == "native" - ) + is_native = test_name.startswith("native_") or test_name.startswith("viper_") is_endian = test_name.endswith("_endian") - is_int_big = test_name.startswith("int_big") or test_name.endswith("_intbig") + is_int_big = ( + test_name.startswith("int_big") + or test_name.endswith("_intbig") + or test_name.startswith("ffi_int") # these tests contain large integer literals + ) + is_int_64 = test_name.startswith("int_64") or test_name.endswith("_int64") is_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray") is_set_type = test_name.startswith(("set_", "frozenset")) or test_name.endswith("_set") - is_slice = test_name.find("slice") != -1 or test_name in misc_slice_tests - is_async = test_name.startswith(("async_", "asyncio_")) + is_slice = test_name.find("slice") != -1 + is_async = test_name.startswith(("async_", "asyncio_")) or test_name.endswith("_async") is_const = test_name.startswith("const") - is_io_module = test_name.startswith("io_") is_fstring = test_name.startswith("string_fstring") is_inlineasm = test_name.startswith("asm") @@ -890,19 +1088,19 @@ def run_one_test(test_file): skip_it |= skip_native and is_native skip_it |= skip_endian and is_endian skip_it |= skip_int_big and is_int_big + skip_it |= skip_int_64 and is_int_64 skip_it |= skip_bytearray and is_bytearray skip_it |= skip_set_type and is_set_type skip_it |= skip_slice and is_slice skip_it |= skip_async and is_async skip_it |= skip_const and is_const skip_it |= skip_revops and "reverse_op" in test_name - skip_it |= skip_io_module and is_io_module skip_it |= skip_fstring and is_fstring skip_it |= skip_inlineasm and is_inlineasm if skip_it: print("skip ", test_file) - skipped_tests.append(test_name) + test_results.append((test_file, "skip", "")) return # Run the test on the MicroPython target. @@ -917,7 +1115,11 @@ def run_one_test(test_file): # start-up code (eg boot.py) when preparing to run the next test. pyb.read_until(1, b"raw REPL; CTRL-B to exit\r\n") print("skip ", test_file) - skipped_tests.append(test_name) + test_results.append((test_file, "skip", "")) + return + elif output_mupy == b"SKIP-TOO-LARGE\n": + print("lrge ", test_file) + test_results.append((test_file, "skip", "too large")) return # Look at the output of the test to see if unittest was used. @@ -954,7 +1156,11 @@ def run_one_test(test_file): # Expected output is result of running unittest. output_expected = None else: - test_file_expected = test_file + ".exp" + # Prefer emitter-specific expected output. + test_file_expected = test_file + "." + args.emit + ".exp" + if not os.path.isfile(test_file_expected): + # Fall back to generic expected output. + test_file_expected = test_file + ".exp" if os.path.isfile(test_file_expected): # Expected output given by a file, so read that in. with open(test_file_expected, "rb") as f: @@ -1012,7 +1218,7 @@ def run_one_test(test_file): # Print test summary, update counters, and save .exp/.out files if needed. if test_passed: print("pass ", test_file, extra_info) - passed_count.increment() + test_results.append((test_file, "pass", "")) rm_f(filename_expected) rm_f(filename_mupy) else: @@ -1024,9 +1230,7 @@ def run_one_test(test_file): rm_f(filename_expected) # in case left over from previous failed run with open(filename_mupy, "wb") as f: f.write(output_mupy) - failed_tests.append((test_name, test_file)) - - test_count.increment() + test_results.append((test_file, "fail", "")) # Print a note if this looks like it might have been a misfired unittest if not uses_unittest and not test_passed: @@ -1053,17 +1257,49 @@ def run_one_test(test_file): print(line) sys.exit(1) - print( - "{} tests performed ({} individual testcases)".format( - test_count.value, testcase_count.value - ) + # Return test results. + 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" ) - print("{} tests passed".format(passed_count.value)) + 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))) - skipped_tests = sorted(skipped_tests.value) if len(skipped_tests) > 0: - print("{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests))) - failed_tests = sorted(failed_tests.value) + 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): @@ -1071,23 +1307,22 @@ def to_json(obj): return obj.pattern return obj - with open(os.path.join(result_dir, RESULTS_FILE), "w") as f: + with open(os.path.join(args.result_dir, RESULTS_FILE), "w") as f: json.dump( - {"args": vars(args), "failed_tests": [test[1] for test in failed_tests]}, + { + # 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, ) - if len(failed_tests) > 0: - print( - "{} tests failed: {}".format( - len(failed_tests), " ".join(test[0] for test in failed_tests) - ) - ) - return False - - # all tests succeeded - return True + # Return True only if all tests succeeded. + return len(failed_tests) == 0 class append_filter(argparse.Action): @@ -1104,27 +1339,11 @@ def __call__(self, parser, args, value, option): args.filters.append((option, re.compile(value))) -def main(): - cmd_parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description="""Run and manage tests for MicroPython. - +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. - -Tests are discovered by scanning test directories for .py files or using the -specified test files. If test files nor directories are specified, the script -expects to be ran in the tests directory (where this file is located) and the -builtin tests suitable for the target platform are ran. - -When running tests, run-tests.py compares the MicroPython output of the test with the output -produced by running the test through CPython unless a .exp file is found, in which -case it is used as comparison. - -If a test fails, run-tests.py produces a pair of .out and .exp files in the result -directory with the MicroPython output and the expectations, respectively. -""", - epilog="""\ +""" +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 @@ -1140,7 +1359,35 @@ def main(): - 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. + +{test_instance_description} +{test_directory_description} + +When running tests, run-tests.py compares the MicroPython output of the test with the output +produced by running the test through CPython unless a .exp file is found (or a +.native.exp file when using the native emitter), in which case it is used as comparison. + +If a test fails, run-tests.py produces a pair of .out and .exp files in the result +directory with the MicroPython output and the expectations, respectively. +""", + epilog=f"""\ +{test_instance_epilog} Options -i and -e can be multiple and processed in the order given. Regex "search" (vs "match") operation is used. An action (include/exclude) of the last matching regex is used: @@ -1214,8 +1461,25 @@ def main(): action="store_true", help="re-run only the failed tests", ) + cmd_parser.add_argument( + "--begin", + metavar="PROLOGUE", + default=None, + help="prologue python file to execute before module import", + ) + cmd_parser.add_argument( + "--target-wiring", + default=None, + help="force the given script to be used as target_wiring.py", + ) 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_prologue}", prologue) + if args.print_failures: for out in glob(os.path.join(args.result_dir, "*.out")): testbase = out[:-4] @@ -1256,7 +1520,7 @@ def main(): results_file = os.path.join(args.result_dir, RESULTS_FILE) if os.path.exists(results_file): with open(results_file, "r") as f: - tests = json.load(f)["failed_tests"] + tests = list(test[0] for test in json.load(f)["results"] if test[1] == "fail") else: tests = [] elif len(args.files) == 0: @@ -1267,47 +1531,27 @@ def main(): if args.test_dirs is None: test_dirs = ( "basics", - "circuitpython", # CIRCUITPY-CHANGE "micropython", "misc", "extmod", + "stress", ) if args.inlineasm_arch is not None: test_dirs += ("inlineasm/{}".format(args.inlineasm_arch),) - if args.platform == "pyboard": - # run pyboard tests - test_dirs += ("float", "stress", "ports/stm32") - elif args.platform == "mimxrt": - test_dirs += ("float", "stress") - elif args.platform == "renesas-ra": - test_dirs += ("float", "ports/renesas-ra") - elif args.platform == "rp2": - test_dirs += ("float", "stress", "thread", "ports/rp2") - elif args.platform == "esp32": - test_dirs += ("float", "stress", "thread") - elif args.platform in ("esp8266", "minimal", "samd", "nrf"): + if args.thread is not None: + test_dirs += ("thread",) + if args.float_prec > 0: test_dirs += ("float",) - elif args.platform == "WiPy": - # run WiPy tests - test_dirs += ("ports/cc3200",) - elif args.platform in PC_PLATFORMS: + if args.unicode: + test_dirs += ("unicode",) + port_specific_test_dir = "ports/{}".format(platform_to_port(args.platform)) + if os.path.isdir(port_specific_test_dir): + test_dirs += (port_specific_test_dir,) + if args.platform in PC_PLATFORMS: # run PC tests - test_dirs += ( - "float", - "import", - "io", - "stress", - "unicode", - "cmdline", - "ports/unix", - ) - elif args.platform == "qemu": - test_dirs += ( - "float", - "ports/qemu", - ) - elif args.platform == "webassembly": - test_dirs += ("float", "ports/webassembly") + test_dirs += ("import",) + if args.build != "minimal": + test_dirs += ("cmdline", "io") else: # run tests from these directories test_dirs = args.test_dirs @@ -1322,6 +1566,13 @@ def main(): # tests explicitly given tests = args.files + # If any tests need it, prepare the target_wiring script for the target. + if pyb and any(test.endswith(tests_requiring_target_wiring) for test in tests): + detect_target_wiring_script(pyb, args) + + # End the target information line. + print() + if not args.keep_path: # Clear search path to make sure tests use only builtin modules, those in # extmod, and a path to unittest in case it's needed. @@ -1342,7 +1593,8 @@ def main(): try: os.makedirs(args.result_dir, exist_ok=True) - res = run_tests(pyb, tests, args, args.result_dir, args.jobs) + test_results, testcase_count = run_tests(pyb, tests, args, args.result_dir, args.jobs) + res = create_test_report(args, test_results, testcase_count) finally: if pyb: pyb.close() diff --git a/tests/serial_test.py b/tests/serial_test.py new file mode 100755 index 0000000000000..3b5940d91a907 --- /dev/null +++ b/tests/serial_test.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python +# +# Performance and reliability test for serial port communication. +# +# Basic usage: +# serial_test.py [-t serial-device] +# +# 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: + wr=__import__("pyb").USB_VCP(0).send +except: + import sys + if hasattr(sys.stdout,'buffer'): + wr=sys.stdout.buffer.write + else: + wr=sys.stdout.write + bin = False +b=bytearray(%u) +if bin: + wr('BIN') + for i in range(len(b)): + b[i] = i & 0xff +else: + wr('TXT') + for i in range(len(b)): + b[i] = 0x20 + (i & 0x3f) +for _ in range(%d): + wr(b) +""" + + +write_test_script_verified = """ +import sys +try: + rd=__import__("pyb").USB_VCP(0).recv +except: + rd=sys.stdin.readinto +b=bytearray(%u) +for _ in range(%u): + n = rd(b) + fail = 0 + for i in range(n): + if b[i] != 32 + (i & 0x3f): + fail += 1 + if fail: + sys.stdout.write(b'ER%%05u' %% fail) + else: + sys.stdout.write(b'OK%%05u' %% n) +""" + +write_test_script_unverified = """ +import sys +try: + rd=__import__("pyb").USB_VCP(0).recv +except: + rd=sys.stdin.readinto +b=bytearray(%u) +for _ in range(%u): + n = rd(b) + if n != len(b): + sys.stdout.write(b'ER%%05u' %% n) + else: + sys.stdout.write(b'OK%%05u' %% n) +""" + + +class TestError(Exception): + pass + + +def drain_input(ser): + time.sleep(0.1) + while ser.inWaiting() > 0: + data = ser.read(ser.inWaiting()) + time.sleep(0.1) + + +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]) + time.sleep(0.01) + ser.write(b"\x04") # eof + ser.flush() + response = ser.read(2) + if response != b"OK": + response += ser.read(ser.inWaiting()) + 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 + + assert bufsize % 256 == 0 # for verify to work + + # how long to wait for data from device + # (if UART TX is also enabled then it can take 1.4s to send + # out a 16KB butter at 115200bps) + READ_TIMEOUT_S = 2 + + # Load and run the read_test_script. + script = bytes(read_test_script % (bufsize, nbuf), "ascii") + send_script(ser_repl, script) + + # Read from the device the type of data that it will send (BIN or TXT). + data_type = ser_data.read(3) + + # Read data from the device, check it is correct, and measure throughput. + n = 0 + last_byte = None + t_start = time.time() + remain = nbuf * bufsize + total_data = bytearray(remain) + while remain: + t0 = time.monotonic_ns() + while ser_data.inWaiting() == 0: + if time.monotonic_ns() - t0 > READ_TIMEOUT_S * 1e9: + # timeout waiting for data from device + break + time.sleep(0.0001) + if not ser_data.inWaiting(): + test_passed = False + print("ERROR: timeout waiting for data") + print(total_data[:n]) + return 0 + to_read = min(ser_data.inWaiting(), remain) + data = ser_data.read(to_read) + remain -= len(data) + print(f"{n} / {nbuf * bufsize}", end="\r") + total_data[n : n + len(data)] = data + n += len(data) + t_end = time.time() + for i in range(0, len(total_data)): + if data_type == b"BIN": + wanted = i & 0xFF + else: + wanted = 0x20 + (i & 0x3F) + if total_data[i] != wanted: + test_passed = False + print("ERROR: data mismatch:", i, wanted, total_data[i]) + ser_repl.write(b"\x03") # break + t = t_end - t_start + + # Print results. + print( + "DATA IN: bufsize=%u, read %u bytes in %.2f msec = %.2f kibytes/sec = %.2f MBits/sec" + % (bufsize, n, t * 1000, n / 1024 / t, n * 8 / 1000000 / t) + ) + + return n / t + + +def write_test(ser_repl, ser_data, bufsize, nbuf, verified): + global test_passed + + # Load and run the write_test_script. + if verified: + script = write_test_script_verified + else: + script = write_test_script_unverified + script = bytes(script % (bufsize, nbuf), "ascii") + send_script(ser_repl, script) + drain_input(ser_repl) + + # Write data to the device, check it is correct, and measure throughput. + n = 0 + t_start = time.time() + buf = bytearray(bufsize) + for i in range(len(buf)): + buf[i] = 32 + (i & 0x3F) # don't want to send ctrl chars! + for i in range(nbuf): + ser_data.write(buf) + n += len(buf) + print(f"{n} / {nbuf * bufsize}", end="\r") + response = ser_repl.read(7) + if response != b"OK%05u" % bufsize: + test_passed = False + print("ERROR: bad response, expecting OK%05u, got %r" % (bufsize, response)) + t_end = time.time() + ser_repl.write(b"\x03") # break + t = t_end - t_start + + # Print results. + print( + "DATA OUT: verify=%d, bufsize=%u, wrote %u bytes in %.2f msec = %.2f kibytes/sec = %.2f MBits/sec" + % (verified, bufsize, n, t * 1000, n / 1024 / t, n * 8 / 1000000 / t) + ) + + return n / t + + +def do_test(dev_repl, dev_data=None, time_per_subtest=1): + if dev_data is None: + print("REPL and data on", dev_repl) + ser_repl = serial.Serial(dev_repl, baudrate=115200, timeout=1) + ser_data = ser_repl + else: + print("REPL on", dev_repl) + print("data on", dev_data) + 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), + (write_test, (False,), 128), + ): + nbuf = 128 + while bufsize <= 16384: + rate = test_func(ser_repl, ser_data, bufsize, nbuf, *test_args) + bufsize *= 2 + if rate: + # Adjust the amount of data based on the rate, to keep each subtest + # at around time_per_subtest seconds long. + nbuf = max(min(128, int(rate * time_per_subtest / bufsize)), 1) + + ser_repl.close() + ser_data.close() + + +def main(): + global test_passed + + cmd_parser = argparse.ArgumentParser( + description="Test performance and reliability of serial port communication.", + epilog=run_tests_module.test_instance_epilog, + formatter_class=argparse.RawTextHelpFormatter, + ) + cmd_parser.add_argument( + "-t", + "--test-instance", + default="a0", + help="MicroPython instance to test", + ) + cmd_parser.add_argument( + "--time-per-subtest", default="1", help="approximate time to take per subtest (in seconds)" + ) + args = cmd_parser.parse_args() + + dev_repl = run_tests_module.convert_device_shortcut_to_real_device(args.test_instance) + + test_passed = True + try: + do_test(dev_repl, None, float(args.time_per_subtest)) + except TestError as er: + test_passed = False + print("ERROR:", er) + + if not test_passed: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/tests/stress/bytecode_limit.py b/tests/stress/bytecode_limit.py index 948d7668da551..0a72b66fa05b6 100644 --- a/tests/stress/bytecode_limit.py +++ b/tests/stress/bytecode_limit.py @@ -1,19 +1,29 @@ # Test the limits of bytecode generation. +import sys + +# Tune the test parameters based on the target's bytecode generator. +if hasattr(sys.implementation, "_mpy"): + # Target can load .mpy files so generated bytecode uses 1 byte per qstr. + number_of_body_copies = (433, 432, 431, 399) +else: + # Target can't load .mpy files so generated bytecode uses 2 bytes per qstr. + number_of_body_copies = (401, 400, 399, 398) + body = " with f()()() as a:\n try:\n f()()()\n except Exception:\n pass\n" # Test overflow of jump offset. # Print results at the end in case an intermediate value of n fails with MemoryError. results = [] -for n in (433, 432, 431, 430): +for n in number_of_body_copies: try: exec("cond = 0\nif cond:\n" + body * n + "else:\n print('cond false')\n") - results.append((n, "ok")) + results.append("ok") except MemoryError: print("SKIP") raise SystemExit - except RuntimeError: - results.append((n, "RuntimeError")) + except RuntimeError as er: + results.append(repr(er)) print(results) # Test changing size of code info (source line/bytecode mapping) due to changing diff --git a/tests/stress/bytecode_limit.py.exp b/tests/stress/bytecode_limit.py.exp index cda52b1b97348..50511665f0076 100644 --- a/tests/stress/bytecode_limit.py.exp +++ b/tests/stress/bytecode_limit.py.exp @@ -1,4 +1,4 @@ cond false cond false -[(433, 'RuntimeError'), (432, 'RuntimeError'), (431, 'ok'), (430, 'ok')] +["RuntimeError('bytecode overflow',)", "RuntimeError('bytecode overflow',)", 'ok', 'ok'] [123] diff --git a/tests/stress/dict_copy.py b/tests/stress/dict_copy.py index 73d3a5b51d601..f9b742e20f716 100644 --- a/tests/stress/dict_copy.py +++ b/tests/stress/dict_copy.py @@ -1,6 +1,11 @@ # copying a large dictionary -a = {i: 2 * i for i in range(1000)} +try: + a = {i: 2 * i for i in range(1000)} +except MemoryError: + print("SKIP") + raise SystemExit + b = a.copy() for i in range(1000): print(i, b[i]) diff --git a/tests/stress/dict_create.py b/tests/stress/dict_create.py index e9db40a8e6c5b..91a83a12f9d85 100644 --- a/tests/stress/dict_create.py +++ b/tests/stress/dict_create.py @@ -3,6 +3,10 @@ d = {} x = 1 while x < 1000: - d[x] = x + try: + d[x] = x + except MemoryError: + print("SKIP") + raise SystemExit x += 1 print(d[500]) diff --git a/tests/stress/fun_call_limit.py b/tests/stress/fun_call_limit.py index b802aadd558c0..69f8aa5aec413 100644 --- a/tests/stress/fun_call_limit.py +++ b/tests/stress/fun_call_limit.py @@ -16,14 +16,16 @@ def test(n): # If the port has at least 32-bits then this test should pass. -print(test(29)) +print(test(28)) # This test should fail on all ports (overflows a small int). print(test(70)) -# Check that there is a correct transition to the limit of too many args before *args. +# 28 is the biggest number that will pass on a 32-bit port using object +# representation B, which has 1<<28 still fitting in a positive small int. reached_limit = False -for i in range(30, 70): +any_test_succeeded = False +for i in range(28, 70): result = test(i) if reached_limit: if result != "SyntaxError": @@ -34,3 +36,5 @@ def test(n): else: if result != i + 4: print("FAIL") + any_test_succeeded = True +assert any_test_succeeded # At least one iteration must have passed diff --git a/tests/stress/fun_call_limit.py.exp b/tests/stress/fun_call_limit.py.exp index 53d2b28043015..491abbfa8be72 100644 --- a/tests/stress/fun_call_limit.py.exp +++ b/tests/stress/fun_call_limit.py.exp @@ -1,2 +1,2 @@ -33 +32 SyntaxError diff --git a/tests/stress/qstr_limit.py b/tests/stress/qstr_limit.py index 08b10a039f509..c7bd437f3adc1 100644 --- a/tests/stress/qstr_limit.py +++ b/tests/stress/qstr_limit.py @@ -12,8 +12,8 @@ def make_id(n, base="a"): var = make_id(l) try: exec(var + "=1", g) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) continue print(var in g) @@ -26,16 +26,16 @@ def f(**k): for l in range(254, 259): try: exec("f({}=1)".format(make_id(l))) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) # type construction for l in range(254, 259): id = make_id(l) try: - print(type(id, (), {}).__name__) - except RuntimeError: - print("RuntimeError", l) + print(type(id, (), {})) + except RuntimeError as er: + print("RuntimeError", er, l) # hasattr, setattr, getattr @@ -48,28 +48,20 @@ class A: a = A() try: setattr(a, id, 123) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) try: print(hasattr(a, id), getattr(a, id)) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) # format with keys for l in range(254, 259): id = make_id(l) try: print(("{" + id + "}").format(**{id: l})) - except RuntimeError: - print("RuntimeError", l) - -# modulo format with keys -for l in range(254, 259): - id = make_id(l) - try: - print(("%(" + id + ")d") % {id: l}) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) # import module # (different OS's have different results so only run those that are consistent) @@ -78,8 +70,8 @@ class A: __import__(make_id(l)) except ImportError: print("ok", l) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) # import package for l in (100, 101, 102, 128, 129): @@ -87,5 +79,5 @@ class A: exec("import " + make_id(l) + "." + make_id(l, "A")) except ImportError: print("ok", l) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) diff --git a/tests/stress/qstr_limit.py.exp b/tests/stress/qstr_limit.py.exp index 455761bc71e4a..2349adf220f98 100644 --- a/tests/stress/qstr_limit.py.exp +++ b/tests/stress/qstr_limit.py.exp @@ -1,43 +1,38 @@ True True -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 {'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst': 1} {'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu': 1} -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 -abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst -abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 + + +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 True 123 True 123 -RuntimeError 256 -RuntimeError 256 -RuntimeError 257 -RuntimeError 257 -RuntimeError 258 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 257 +RuntimeError name too long 258 +RuntimeError name too long 258 254 255 -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 -254 -255 -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 ok 100 ok 101 -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 ok 100 ok 101 ok 102 -RuntimeError 128 -RuntimeError 129 +RuntimeError name too long 128 +RuntimeError name too long 129 diff --git a/tests/stress/qstr_limit_str_modulo.py b/tests/stress/qstr_limit_str_modulo.py new file mode 100644 index 0000000000000..90b9f4364ec6f --- /dev/null +++ b/tests/stress/qstr_limit_str_modulo.py @@ -0,0 +1,21 @@ +# Test interning qstrs that go over the qstr length limit (255 bytes in default configuration). +# The tests here are specifically for str formatting with %. + +try: + "" % () +except TypeError: + print("SKIP") + raise SystemExit + + +def make_id(n, base="a"): + return "".join(chr(ord(base) + i % 26) for i in range(n)) + + +# modulo format with keys +for l in range(254, 259): + id = make_id(l) + try: + print(("%(" + id + ")d") % {id: l}) + except RuntimeError as er: + print("RuntimeError", er, l) diff --git a/tests/stress/qstr_limit_str_modulo.py.exp b/tests/stress/qstr_limit_str_modulo.py.exp new file mode 100644 index 0000000000000..3632c85bffea3 --- /dev/null +++ b/tests/stress/qstr_limit_str_modulo.py.exp @@ -0,0 +1,5 @@ +254 +255 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 diff --git a/tests/stress/recursive_iternext.py b/tests/stress/recursive_iternext.py index bbc389e726237..c737f1e36d70a 100644 --- a/tests/stress/recursive_iternext.py +++ b/tests/stress/recursive_iternext.py @@ -1,4 +1,8 @@ # This tests that recursion with iternext doesn't lead to segfault. +# +# This test segfaults CPython, but that's not a bug as CPython doesn't enforce +# limits on C recursion - see +# https://github.com/python/cpython/issues/58218#issuecomment-1093570209 try: enumerate filter @@ -9,49 +13,25 @@ print("SKIP") raise SystemExit -# We need to pick an N that is large enough to hit the recursion -# limit, but not too large that we run out of heap memory. -try: - # large stack/heap, eg unix - [0] * 80000 - N = 5000 -except: - try: - # medium, eg pyboard - [0] * 10000 - N = 1000 - except: - # small, eg esp8266 - N = 100 - -try: - x = (1, 2) - for i in range(N): - x = enumerate(x) - tuple(x) -except RuntimeError: - print("RuntimeError") -try: +# Progressively build a bigger nested iterator structure (10 at a time for speed), +# and then try to evaluate it via tuple(x) which makes deep recursive function calls. +# +# Eventually this should raise a RuntimeError as MicroPython runs out of stack. +# It shouldn't ever raise a MemoryError, if it does then somehow MicroPython has +# run out of heap (for the nested structure) before running out of stack. +def recurse_iternext(nested_fn): x = (1, 2) - for i in range(N): - x = filter(None, x) - tuple(x) -except RuntimeError: - print("RuntimeError") + while True: + for _ in range(10): + x = nested_fn(x) + try: + tuple(x) + except RuntimeError: + print("RuntimeError") + break -try: - x = (1, 2) - for i in range(N): - x = map(max, x, ()) - tuple(x) -except RuntimeError: - print("RuntimeError") -try: - x = (1, 2) - for i in range(N): - x = zip(x) - tuple(x) -except RuntimeError: - print("RuntimeError") +# Test on various nested iterator structures +for nested_fn in [enumerate, lambda x: filter(None, x), lambda x: map(max, x, ()), zip]: + recurse_iternext(nested_fn) diff --git a/tests/target_wiring/EK_RA6M2.py b/tests/target_wiring/EK_RA6M2.py new file mode 100644 index 0000000000000..7d4a8cbbd6484 --- /dev/null +++ b/tests/target_wiring/EK_RA6M2.py @@ -0,0 +1,8 @@ +# Target wiring for EK_RA6M2. +# +# Connect: +# - P601 to P602 + +# UART(9) is on P602/P601. +uart_loopback_args = (9,) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/NUCLEO_WB55.py b/tests/target_wiring/NUCLEO_WB55.py new file mode 100644 index 0000000000000..ad7c120d3770e --- /dev/null +++ b/tests/target_wiring/NUCLEO_WB55.py @@ -0,0 +1,8 @@ +# Target wiring for NUCLEO_WB55. +# +# Connect: +# - PA2 to PA3 + +# LPUART(1) is on PA2/PA3. +uart_loopback_args = ("LP1",) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/PYBx.py b/tests/target_wiring/PYBx.py new file mode 100644 index 0000000000000..10ce520ef0af4 --- /dev/null +++ b/tests/target_wiring/PYBx.py @@ -0,0 +1,8 @@ +# Target wiring for PYBV10, PYBV11, PYBLITEV10, PYBD_SF2, PYBD_SF3, PYBD_SF6. +# +# Connect: +# - X1 to X2 + +# UART("XA") is on X1/X2 (usually UART(4) on PA0/PA1). +uart_loopback_args = ("XA",) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/ZEPHYR_NUCLEO_WB55RG.py b/tests/target_wiring/ZEPHYR_NUCLEO_WB55RG.py new file mode 100644 index 0000000000000..c7ca2ac26f794 --- /dev/null +++ b/tests/target_wiring/ZEPHYR_NUCLEO_WB55RG.py @@ -0,0 +1,7 @@ +# Target wiring for zephyr nucleo_wb55rg. +# +# Connect: +# - TX=PC0 to RX=PC1 + +uart_loopback_args = ("lpuart1",) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/alif.py b/tests/target_wiring/alif.py new file mode 100644 index 0000000000000..18f3cbe7e5e0d --- /dev/null +++ b/tests/target_wiring/alif.py @@ -0,0 +1,7 @@ +# Target wiring for general alif board. +# +# Connect: +# - UART1 TX and RX, usually P0_5 and P0_4 + +uart_loopback_args = (1,) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/esp32.py b/tests/target_wiring/esp32.py new file mode 100644 index 0000000000000..2767cd5acb2e8 --- /dev/null +++ b/tests/target_wiring/esp32.py @@ -0,0 +1,12 @@ +# Target wiring for general esp32 board. +# +# Connect: +# - GPIO4 to GPIO5 +# - GPIO12 to GPIO13 + +uart_loopback_args = (1,) +uart_loopback_kwargs = {"tx": 4, "rx": 5} + +encoder_loopback_id = 0 +encoder_loopback_out_pins = (4, 12) +encoder_loopback_in_pins = (5, 13) diff --git a/tests/target_wiring/mimxrt.py b/tests/target_wiring/mimxrt.py new file mode 100644 index 0000000000000..669e9095990db --- /dev/null +++ b/tests/target_wiring/mimxrt.py @@ -0,0 +1,7 @@ +# Target wiring for general mimxrt board. +# +# Connect: +# - UART1 TX and RX, usually D0 and D1 + +uart_loopback_args = (1,) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/nrf.py b/tests/target_wiring/nrf.py new file mode 100644 index 0000000000000..6979dd28ee595 --- /dev/null +++ b/tests/target_wiring/nrf.py @@ -0,0 +1,7 @@ +# Target wiring for general nrf board. +# +# Connect: +# - UART0 TX and RX + +uart_loopback_args = (0,) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/rp2.py b/tests/target_wiring/rp2.py new file mode 100644 index 0000000000000..cb0fa0d626339 --- /dev/null +++ b/tests/target_wiring/rp2.py @@ -0,0 +1,7 @@ +# Target wiring for general rp2 board. +# +# Connect: +# - GPIO0 to GPIO1 + +uart_loopback_args = (0,) +uart_loopback_kwargs = {"tx": "GPIO0", "rx": "GPIO1"} diff --git a/tests/target_wiring/samd.py b/tests/target_wiring/samd.py new file mode 100644 index 0000000000000..887c43a242f9b --- /dev/null +++ b/tests/target_wiring/samd.py @@ -0,0 +1,7 @@ +# Target wiring for general samd board. +# +# Connect: +# - D0 to D1 + +uart_loopback_args = () +uart_loopback_kwargs = {"tx": "D1", "rx": "D0"} diff --git a/tests/thread/mutate_bytearray.py b/tests/thread/mutate_bytearray.py index b4664781a1522..7116d291cfeec 100644 --- a/tests/thread/mutate_bytearray.py +++ b/tests/thread/mutate_bytearray.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared bytearray @@ -36,7 +37,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check bytearray has correct contents print(len(ba)) diff --git a/tests/thread/mutate_dict.py b/tests/thread/mutate_dict.py index 3777af66248c6..dd5f69e6c5d66 100644 --- a/tests/thread/mutate_dict.py +++ b/tests/thread/mutate_dict.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared dict @@ -38,7 +39,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check dict has correct contents print(sorted(di.items())) diff --git a/tests/thread/mutate_instance.py b/tests/thread/mutate_instance.py index 306ad91c95cfa..63f7fb1e23df8 100644 --- a/tests/thread/mutate_instance.py +++ b/tests/thread/mutate_instance.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -40,7 +41,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check user instance has correct contents print(user.a, user.b, user.c) diff --git a/tests/thread/mutate_list.py b/tests/thread/mutate_list.py index 6f1e8812541a0..d7398a2f1e0b4 100644 --- a/tests/thread/mutate_list.py +++ b/tests/thread/mutate_list.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared list @@ -39,7 +40,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check list has correct contents li.sort() diff --git a/tests/thread/mutate_set.py b/tests/thread/mutate_set.py index 2d9a3e0ce9efd..7dcefa1d113f9 100644 --- a/tests/thread/mutate_set.py +++ b/tests/thread/mutate_set.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared set @@ -33,7 +34,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check set has correct contents print(sorted(se)) diff --git a/tests/thread/stress_aes.py b/tests/thread/stress_aes.py index d8d0acd568a7a..ca25f8ad2fd57 100644 --- a/tests/thread/stress_aes.py +++ b/tests/thread/stress_aes.py @@ -277,7 +277,7 @@ def thread_entry(n_loop): n_thread = 2 n_loop = 2 else: - n_thread = 20 + n_thread = 10 n_loop = 5 for i in range(n_thread): _thread.start_new_thread(thread_entry, (n_loop,)) diff --git a/tests/thread/stress_recurse.py b/tests/thread/stress_recurse.py index 73b3a40f33daa..ec8b43fe8fc22 100644 --- a/tests/thread/stress_recurse.py +++ b/tests/thread/stress_recurse.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -24,5 +25,5 @@ def thread_entry(): # busy wait for thread to finish while not finished: - pass + time.sleep(0) print("done") diff --git a/tests/thread/stress_schedule.py b/tests/thread/stress_schedule.py index 97876f0f77ca8..362d71aa12e38 100644 --- a/tests/thread/stress_schedule.py +++ b/tests/thread/stress_schedule.py @@ -27,6 +27,8 @@ 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: @@ -46,7 +48,7 @@ def thread(): # Wait up to 10 seconds for 10000 tasks to be scheduled. t = time.ticks_ms() while n < _NUM_TASKS and time.ticks_diff(time.ticks_ms(), t) < _TIMEOUT_MS: - pass + time.sleep(0) # Stop all threads. thread_run = False diff --git a/tests/thread/thread_coop.py b/tests/thread/thread_coop.py index aefc4af074db5..85cda789c936e 100644 --- a/tests/thread/thread_coop.py +++ b/tests/thread/thread_coop.py @@ -7,6 +7,7 @@ import _thread import sys from time import ticks_ms, ticks_diff, sleep_ms +import micropython done = False @@ -21,6 +22,8 @@ MAX_DELTA = 100 +# This function must always use the bytecode emitter so the VM can bounce the GIL when running. +@micropython.bytecode def busy_thread(): while not done: pass diff --git a/tests/thread/thread_exc1.py b/tests/thread/thread_exc1.py index cd87740929103..cd6599983c141 100644 --- a/tests/thread/thread_exc1.py +++ b/tests/thread/thread_exc1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -34,5 +35,5 @@ def thread_entry(): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print("done") diff --git a/tests/thread/thread_exc2.py.native.exp b/tests/thread/thread_exc2.py.native.exp new file mode 100644 index 0000000000000..9b2e715ef8dfc --- /dev/null +++ b/tests/thread/thread_exc2.py.native.exp @@ -0,0 +1,3 @@ +Unhandled exception in thread started by +ValueError: +done diff --git a/tests/thread/thread_gc1.py b/tests/thread/thread_gc1.py index b36ea9d4c8421..45c17cc17bed3 100644 --- a/tests/thread/thread_gc1.py +++ b/tests/thread/thread_gc1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import gc import _thread @@ -44,6 +45,6 @@ def thread_entry(n): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(n_correct == n_finished) diff --git a/tests/thread/thread_ident1.py b/tests/thread/thread_ident1.py index 2a3732eff53dc..08cfd3eb36e8a 100644 --- a/tests/thread/thread_ident1.py +++ b/tests/thread/thread_ident1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -27,6 +28,6 @@ def thread_entry(): new_tid = _thread.start_new_thread(thread_entry, ()) while not finished: - pass + time.sleep(0) print("done", type(new_tid) == int, new_tid == tid) diff --git a/tests/thread/thread_lock3.py b/tests/thread/thread_lock3.py index a927dc6829e15..c5acfa21b7dc6 100644 --- a/tests/thread/thread_lock3.py +++ b/tests/thread/thread_lock3.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread lock = _thread.allocate_lock() @@ -26,4 +27,4 @@ def thread_entry(idx): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) diff --git a/tests/thread/thread_lock4.py b/tests/thread/thread_lock4_intbig.py similarity index 100% rename from tests/thread/thread_lock4.py rename to tests/thread/thread_lock4_intbig.py diff --git a/tests/thread/thread_shared1.py b/tests/thread/thread_shared1.py index 251e26fae6ca3..c2e33abcec7ee 100644 --- a/tests/thread/thread_shared1.py +++ b/tests/thread/thread_shared1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -40,5 +41,5 @@ def thread_entry(n, tup): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(tup) diff --git a/tests/thread/thread_shared2.py b/tests/thread/thread_shared2.py index a1223c2b94f40..4ce9057ca017e 100644 --- a/tests/thread/thread_shared2.py +++ b/tests/thread/thread_shared2.py @@ -3,6 +3,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -31,5 +32,5 @@ def thread_entry(n, lst, idx): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(lst) diff --git a/tests/thread/thread_stacksize1.py b/tests/thread/thread_stacksize1.py index 140d165cb3497..75e1da9642f95 100644 --- a/tests/thread/thread_stacksize1.py +++ b/tests/thread/thread_stacksize1.py @@ -3,6 +3,7 @@ # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import sys +import time import _thread # different implementations have different minimum sizes @@ -51,5 +52,5 @@ def thread_entry(): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print("done") diff --git a/tests/thread/thread_stdin.py b/tests/thread/thread_stdin.py index a469933f19b55..498b0a3a27022 100644 --- a/tests/thread/thread_stdin.py +++ b/tests/thread/thread_stdin.py @@ -5,6 +5,7 @@ # This is a regression test for https://github.com/micropython/micropython/issues/15230 # on rp2, but doubles as a general property to test across all ports. import sys +import time import _thread try: @@ -38,7 +39,7 @@ def is_done(self): # have run yet. The actual delay is <20ms but spinning here instead of # sleep(0.1) means the test can run on MP builds without float support. while not thread_waiter.is_done(): - pass + time.sleep(0) # The background thread should have completed its wait by now. print(thread_waiter.is_done()) diff --git a/tests/unix/extra_coverage.py b/tests/unix/extra_coverage.py index 0ea8f7886bfff..ec10a44ff39b3 100644 --- a/tests/unix/extra_coverage.py +++ b/tests/unix/extra_coverage.py @@ -6,6 +6,16 @@ import errno import io +import uctypes + +# create an int-like variable used for coverage of `mp_obj_get_ll` +buf = bytearray(b"\xde\xad\xbe\xef") +struct = uctypes.struct( + uctypes.addressof(buf), + {"f32": uctypes.UINT32 | 0}, + uctypes.BIG_ENDIAN, +) +deadbeef = struct.f32 data = extra_coverage() @@ -23,6 +33,7 @@ print(stream.read(1)) # read 1 byte encounters non-blocking error print(stream.readline()) # readline encounters non-blocking error print(stream.readinto(bytearray(10))) # readinto encounters non-blocking error +print(stream.readinto1(bytearray(10))) # readinto1 encounters non-blocking error print(stream.write(b"1")) # write encounters non-blocking error print(stream.write1(b"1")) # write1 encounters non-blocking error stream.set_buf(b"123") @@ -38,6 +49,28 @@ stream.set_error(0) print(stream.ioctl(0, bytearray(10))) # successful ioctl call +print("# stream.readinto") + +# stream.readinto will read 3 bytes then try to read more to fill the buffer, +# but on the second attempt will encounter EIO and should raise that error. +stream.set_error(errno.EIO) +stream.set_buf(b"123") +buf = bytearray(4) +try: + stream.readinto(buf) +except OSError as er: + print("OSError", er.errno == errno.EIO) +print(buf) + +# stream.readinto1 will read 3 bytes then should return them immediately, and +# not encounter the EIO. +stream.set_error(errno.EIO) +stream.set_buf(b"123") +buf = bytearray(4) +print(stream.readinto1(buf), buf) + +print("# stream textio") + stream2 = data[3] # is textio print(stream2.read(1)) # read 1 byte encounters non-blocking error with textio stream @@ -92,7 +125,7 @@ print(returns_NULL()) -# test for freeze_mpy +# test for freeze_mpy (importing prints several lines) import frozentest print(frozentest.__file__) diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 4cd8b8666d270..cdb1145569497 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -2,25 +2,41 @@ -123 +123 123 -0123 123 -123 -1ABCDEF +123f +123F +7fffffffffffffff +7FFFFFFFFFFFFFFF +18446744073709551615 +789f +789F ab abc ' abc' ' True' 'Tru' false true (null) -2147483648 2147483648 -80000000 -80000000 +8000000f +8000000F abc % +.a . +<%> + + +<43690> +<43690> +<43690> + +<1000.000000> + +<9223372036854775807> # GC -0x0 -0x0 +0 +0 # GC part 2 pass # tracked allocation -m_tracked_head = 0x0 +m_tracked_head = 0 0 1 1 1 2 1 @@ -37,7 +53,7 @@ m_tracked_head = 0x0 5 1 6 1 7 1 -m_tracked_head = 0x0 +m_tracked_head = 0 # vstr tests sts @@ -91,6 +107,16 @@ data 12345 6 -1 +0 +1 +0 +0.000000 +deadbeef +c0ffee777c0ffee +deadbeef +0deadbeef +c0ffee +000c0ffee # runtime utils TypeError: unsupported type for __abs__: 'str' TypeError: unsupported types for __divmod__: 'str', 'str' @@ -99,12 +125,10 @@ TypeError: unsupported types for __divmod__: 'str', 'str' 2 OverflowError: overflow converting long int to machine word OverflowError: overflow converting long int to machine word +TypeError: can't convert NoneType to int +TypeError: can't convert NoneType to int ValueError: Warning: test -# format float -? -+1e+00 -+1e+00 # binary 123 456 @@ -124,6 +148,13 @@ unlocked KeyboardInterrupt: KeyboardInterrupt: 10 +loop +scheduled function +loop +scheduled function +loop +scheduled function +scheduled function # ringbuf 99 0 98 1 @@ -185,11 +216,17 @@ None None None None +None b'123' b'123' b'123' OSError 0 +# stream.readinto +OSError True +bytearray(b'123\x00') +3 bytearray(b'123\x00') +# stream textio None None cpp None @@ -215,7 +252,7 @@ b'\x00\xff' frzmpy4 1 frzmpy4 2 NULL -uPy +interned a long string that is not interned a string that has unicode αβγ chars b'bytes 1234\x01' diff --git a/tests/unix/ffi_float2.py b/tests/unix/ffi_float2.py index bbed6966627e1..eac6cd106cf43 100644 --- a/tests/unix/ffi_float2.py +++ b/tests/unix/ffi_float2.py @@ -29,4 +29,5 @@ def ffi_open(names): for fun in (tgammaf,): for val in (0.5, 1, 1.0, 1.5, 4, 4.0): - print("%.6f" % fun(val)) + # limit to 5 decimals in order to pass with REPR_C with FORMAT_IMPL_BASIC + print("%.5f" % fun(val)) diff --git a/tests/unix/ffi_float2.py.exp b/tests/unix/ffi_float2.py.exp index 58fc6a01acb07..4c750e223a3a6 100644 --- a/tests/unix/ffi_float2.py.exp +++ b/tests/unix/ffi_float2.py.exp @@ -1,6 +1,6 @@ -1.772454 -1.000000 -1.000000 -0.886227 -6.000000 -6.000000 +1.77245 +1.00000 +1.00000 +0.88623 +6.00000 +6.00000 diff --git a/tests/unix/mod_os.py b/tests/unix/mod_os.py index f69fa45b2b22a..468f6badd1e75 100644 --- a/tests/unix/mod_os.py +++ b/tests/unix/mod_os.py @@ -1,6 +1,9 @@ # This module is not entirely compatible with CPython import os +if not hasattr(os, "getenv"): + print("SKIP") + raise SystemExit os.putenv("TEST_VARIABLE", "TEST_VALUE") diff --git a/tests/unix/time_mktime_localtime.py b/tests/unix/time_mktime_localtime.py index d1c03c103d704..df5d6cda690a2 100644 --- a/tests/unix/time_mktime_localtime.py +++ b/tests/unix/time_mktime_localtime.py @@ -1,4 +1,8 @@ -import time +try: + import time +except ImportError: + print("SKIP") + raise SystemExit DAYS_PER_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] diff --git a/tools/boardgen.py b/tools/boardgen.py index 39bedf71cd0c8..3723e7ce31b83 100644 --- a/tools/boardgen.py +++ b/tools/boardgen.py @@ -108,6 +108,10 @@ def add_board_pin_name(self, board_pin_name, hidden=False): ) ) + # Iterate over board pin names in consistent sorted order. + def board_pin_names(self): + return sorted(self._board_pin_names, key=lambda x: x[0]) + # Override this to handle an af specified in af.csv. def add_af(self, af_idx, af_name, af): raise NotImplementedError @@ -295,7 +299,7 @@ def print_board_locals_dict(self, out_source): file=out_source, ) for pin in self.available_pins(): - for board_pin_name, board_hidden in pin._board_pin_names: + for board_pin_name, board_hidden in pin.board_pin_names(): if board_hidden: # Don't include hidden pins in Pins.board. continue @@ -389,7 +393,7 @@ def print_defines(self, out_header, cpu=True, board=True): # #define pin_BOARDNAME (pin_CPUNAME) if board: - for board_pin_name, _board_hidden in pin._board_pin_names: + for board_pin_name, _board_hidden in pin.board_pin_names(): # Note: Hidden board pins are still available to C via the macro. # Note: The RHS isn't wrapped in (), which is necessary to make the # STATIC_AF_ macro work on STM32. diff --git a/tools/cc1 b/tools/cc1 index 827d5886a04f5..aa2534f01e7bb 100755 --- a/tools/cc1 +++ b/tools/cc1 @@ -23,8 +23,8 @@ import re # TODO somehow make them externally configurable # this is the path to the true C compiler -cc1_path = '/usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/cc1' -#cc1_path = '/usr/lib/gcc/arm-none-eabi/5.3.0/cc1' +cc1_path = "/usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/cc1" +# cc1_path = '/usr/lib/gcc/arm-none-eabi/5.3.0/cc1' # this must be the same as MICROPY_QSTR_BYTES_IN_HASH bytes_in_qstr_hash = 2 @@ -41,11 +41,16 @@ print_debug = False ################################################################################ # precompile regexs -re_preproc_line = re.compile(r'# [0-9]+ ') -re_map_entry = re.compile(r'\{.+?\(MP_QSTR_([A-Za-z0-9_]+)\).+\},') -re_mp_obj_dict_t = re.compile(r'(?P(static )?const mp_obj_dict_t (?P[a-z0-9_]+) = \{ \.base = \{&mp_type_dict\}, \.map = \{ \.all_keys_are_qstrs = 1, \.is_fixed = 1, \.is_ordered = )1(?P, \.used = .+ };)$') -re_mp_map_t = re.compile(r'(?P(static )?const mp_map_t (?P[a-z0-9_]+) = \{ \.all_keys_are_qstrs = 1, \.is_fixed = 1, \.is_ordered = )1(?P, \.used = .+ };)$') -re_mp_rom_map_elem_t = re.compile(r'static const mp_rom_map_elem_t [a-z_0-9]+\[\] = {$') +re_preproc_line = re.compile(r"# [0-9]+ ") +re_map_entry = re.compile(r"\{.+?\(MP_QSTR_([A-Za-z0-9_]+)\).+\},") +re_mp_obj_dict_t = re.compile( + r"(?P(static )?const mp_obj_dict_t (?P[a-z0-9_]+) = \{ \.base = \{&mp_type_dict\}, \.map = \{ \.all_keys_are_qstrs = 1, \.is_fixed = 1, \.is_ordered = )1(?P, \.used = .+ };)$" +) +re_mp_map_t = re.compile( + r"(?P(static )?const mp_map_t (?P[a-z0-9_]+) = \{ \.all_keys_are_qstrs = 1, \.is_fixed = 1, \.is_ordered = )1(?P, \.used = .+ };)$" +) +re_mp_rom_map_elem_t = re.compile(r"static const mp_rom_map_elem_t [a-z_0-9]+\[\] = {$") + # this must match the equivalent function in qstr.c def compute_hash(qstr): @@ -55,18 +60,19 @@ def compute_hash(qstr): # Make sure that valid hash is never zero, zero means "hash not computed" return (hash & ((1 << (8 * bytes_in_qstr_hash)) - 1)) or 1 + # this algo must match the equivalent in map.c def hash_insert(map, key, value): hash = compute_hash(key) pos = hash % len(map) start_pos = pos if print_debug: - print(' insert %s: start at %u/%u -- ' % (key, pos, len(map)), end='') + print(" insert %s: start at %u/%u -- " % (key, pos, len(map)), end="") while True: if map[pos] is None: # found empty slot, so key is not in table if print_debug: - print('put at %u' % pos) + print("put at %u" % pos) map[pos] = (key, value) return else: @@ -76,6 +82,7 @@ def hash_insert(map, key, value): pos = (pos + 1) % len(map) assert pos != start_pos + def hash_find(map, key): hash = compute_hash(key) pos = hash % len(map) @@ -92,6 +99,7 @@ def hash_find(map, key): if pos == start_pos: return attempts, None + def process_map_table(file, line, output): output.append(line) @@ -101,7 +109,7 @@ def process_map_table(file, line, output): while True: line = file.readline() if len(line) == 0: - print('unexpected end of input') + print("unexpected end of input") sys.exit(1) line = line.strip() if len(line) == 0: @@ -110,38 +118,38 @@ def process_map_table(file, line, output): if re_preproc_line.match(line): # preprocessor line number comment continue - if line == '};': + if line == "};": # end of table (we assume it appears on a single line) break table_contents.append(line) # make combined string of entries - entries_str = ''.join(table_contents) + entries_str = "".join(table_contents) # split into individual entries entries = [] while entries_str: # look for single entry, by matching nested braces match = None - if entries_str[0] == '{': + if entries_str[0] == "{": nested_braces = 0 for i in range(len(entries_str)): - if entries_str[i] == '{': + if entries_str[i] == "{": nested_braces += 1 - elif entries_str[i] == '}': + elif entries_str[i] == "}": nested_braces -= 1 if nested_braces == 0: - match = re_map_entry.match(entries_str[:i + 2]) + match = re_map_entry.match(entries_str[: i + 2]) break if not match: - print('unknown line in table:', entries_str) + print("unknown line in table:", entries_str) sys.exit(1) # extract single entry line = match.group(0) qstr = match.group(1) - entries_str = entries_str[len(line):].lstrip() + entries_str = entries_str[len(line) :].lstrip() # add the qstr and the whole line to list of all entries entries.append((qstr, line)) @@ -164,28 +172,28 @@ def process_map_table(file, line, output): attempts, line = hash_find(map, qstr) assert line is not None if print_debug: - print(' %s lookup took %u attempts' % (qstr, attempts)) + print(" %s lookup took %u attempts" % (qstr, attempts)) total_attempts += attempts - if len(entries): + if entries: stats = len(map), len(entries) / len(map), total_attempts / len(entries) else: stats = 0, 0, 0 if print_debug: - print(' table stats: size=%d, load=%.2f, avg_lookups=%.1f' % stats) + print(" table stats: size=%d, load=%.2f, avg_lookups=%.1f" % stats) # output hash table for row in map: if row is None: - output.append('{ 0, 0 },\n') + output.append("{ 0, 0 },\n") else: - output.append(row[1] + '\n') - output.append('};\n') + output.append(row[1] + "\n") + output.append("};\n") # skip to next non-blank line while True: line = file.readline() if len(line) == 0: - print('unexpected end of input') + print("unexpected end of input") sys.exit(1) line = line.strip() if len(line) == 0: @@ -197,19 +205,20 @@ def process_map_table(file, line, output): if match is None: match = re_mp_map_t.match(line) if match is None: - print('expecting mp_obj_dict_t or mp_map_t definition') + print("expecting mp_obj_dict_t or mp_map_t definition") print(output[0]) print(line) sys.exit(1) - line = match.group('head') + '0' + match.group('tail') + '\n' + line = match.group("head") + "0" + match.group("tail") + "\n" output.append(line) - return (match.group('id'),) + stats + return (match.group("id"),) + stats + def process_file(filename): output = [] file_changed = False - with open(filename, 'rt') as f: + with open(filename, "rt") as f: while True: line = f.readline() if not line: @@ -218,39 +227,41 @@ def process_file(filename): file_changed = True stats = process_map_table(f, line, output) if print_stats: - print(' [%s: size=%d, load=%.2f, avg_lookups=%.1f]' % stats) + print(" [%s: size=%d, load=%.2f, avg_lookups=%.1f]" % stats) else: output.append(line) if file_changed: if print_debug: - print(' modifying static maps in', output[0].strip()) - with open(filename, 'wt') as f: + print(" modifying static maps in", output[0].strip()) + with open(filename, "wt") as f: for line in output: f.write(line) + def main(): # run actual C compiler # need to quote args that have special characters in them def quote(s): - if s.find('<') != -1 or s.find('>') != -1: + if s.find("<") != -1 or s.find(">") != -1: return "'" + s + "'" else: return s - ret = os.system(cc1_path + ' ' + ' '.join(quote(s) for s in sys.argv[1:])) + + ret = os.system(cc1_path + " " + " ".join(quote(s) for s in sys.argv[1:])) if ret != 0: - ret = (ret & 0x7f) or 127 # make it in range 0-127, but non-zero + ret = (ret & 0x7F) or 127 # make it in range 0-127, but non-zero sys.exit(ret) - if sys.argv[1] == '-E': + if sys.argv[1] == "-E": # CPP has been run, now do our processing stage for i, arg in enumerate(sys.argv): - if arg == '-o': + if arg == "-o": return process_file(sys.argv[i + 1]) print('%s: could not find "-o" option' % (sys.argv[0],)) sys.exit(1) - elif sys.argv[1] == '-fpreprocessed': + elif sys.argv[1] == "-fpreprocessed": # compiler has been run, nothing more to do return else: @@ -258,5 +269,6 @@ def main(): print('%s: unknown first option "%s"' % (sys.argv[0], sys.argv[1])) sys.exit(1) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/tools/ci.sh b/tools/ci.sh index cfc9754837f76..60e870ce65b74 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -9,15 +9,20 @@ fi # Ensure known OPEN_MAX (NO_FILES) limit. ulimit -n 1024 +# Fail on some things which are warnings otherwise +export MICROPY_MAINTAINER_BUILD=1 + ######################################################################################## # general helper functions function ci_gcc_arm_setup { + sudo apt-get update sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi arm-none-eabi-gcc --version } function ci_gcc_riscv_setup { + sudo apt-get update sudo apt-get install gcc-riscv64-unknown-elf picolibc-riscv64-unknown-elf riscv64-unknown-elf-gcc --version } @@ -35,6 +40,7 @@ function ci_picotool_setup { # c code formatting function ci_c_code_formatting_setup { + sudo apt-get update sudo apt-get install uncrustify uncrustify --version } @@ -74,42 +80,69 @@ function ci_code_size_setup { ci_picotool_setup } +function _ci_is_git_merge { + [[ $(git log -1 --format=%P "$1" | wc -w) > 1 ]] +} + function ci_code_size_build { # check the following ports for the change in their code size - PORTS_TO_CHECK=bmusxpdv + # Override the list by setting PORTS_TO_CHECK in the environment before invoking ci. + : ${PORTS_TO_CHECK:=bmusxpdv} + SUBMODULES="lib/asf4 lib/berkeley-db-1.xx lib/btstack lib/cyw43-driver lib/lwip lib/mbedtls lib/micropython-lib lib/nxp_driver lib/pico-sdk lib/stm32lib lib/tinyusb" # Default GitHub pull request sets HEAD to a generated merge commit # between PR branch (HEAD^2) and base branch (i.e. master) (HEAD^1). # # We want to compare this generated commit with the base branch, to see what - # the code size impact would be if we merged this PR. - REFERENCE=$(git rev-parse --short HEAD^1) - COMPARISON=$(git rev-parse --short HEAD) + # the code size impact would be if we merged this PR. During CI we are at a merge commit, + # so this tests the merged PR against its merge base. + # Override the refs by setting REFERENCE and/or COMPARISON in the environment before invoking ci. + : ${COMPARISON:=$(git rev-parse --short HEAD)} + : ${REFERENCE:=$(git rev-parse --short ${COMPARISON}^1)} echo "Comparing sizes of reference ${REFERENCE} to ${COMPARISON}..." git log --oneline $REFERENCE..$COMPARISON - function code_size_build_step { - COMMIT=$1 - OUTFILE=$2 - IGNORE_ERRORS=$3 - - echo "Building ${COMMIT}..." - git checkout --detach $COMMIT - git submodule update --init $SUBMODULES - git show -s - tools/metrics.py clean $PORTS_TO_CHECK - tools/metrics.py build $PORTS_TO_CHECK | tee $OUTFILE || $IGNORE_ERRORS - } - - # build reference, save to size0 - # ignore any errors with this build, in case master is failing - code_size_build_step $REFERENCE ~/size0 true - # build PR/branch, save to size1 - code_size_build_step $COMPARISON ~/size1 false - - unset -f code_size_build_step + OLD_BRANCH="$(git rev-parse --abbrev-ref HEAD)" + + ( # Execute in a subshell so the trap & code_size_build_step doesn't leak + function code_size_build_step { + if [ ! -z "$OLD_BRANCH" ]; then + trap 'git checkout "$OLD_BRANCH"' RETURN EXIT ERR + fi + + COMMIT=$1 + OUTFILE=$2 + IGNORE_ERRORS=$3 + + git checkout --detach $COMMIT + git submodule update --init $SUBMODULES + git show -s + tools/metrics.py clean "$PORTS_TO_CHECK" + # Allow errors from tools/metrics.py to propagate out of the pipe below. + set -o pipefail + tools/metrics.py build "$PORTS_TO_CHECK" | tee -a $OUTFILE || $IGNORE_ERRORS + return $? + } + + # build reference, save to size0 + # ignore any errors with this build, in case master is failing + echo "BUILDING $(git log --format='%s [%h]' -1 ${REFERENCE})" > ~/size0 + code_size_build_step $REFERENCE ~/size0 true + # build PR/branch, save to size1 + if _ci_is_git_merge "$COMPARISON"; then + echo "BUILDING $(git log --oneline -1 --format='%s [merge of %h]' ${COMPARISON}^2)" + else + echo "BUILDING $(git log --oneline -1 --formta='%s [%h]' ${COMPARISON})" + fi > ~/size1 + code_size_build_step $COMPARISON ~/size1 false + ) +} + +function ci_code_size_report { + # Allow errors from tools/metrics.py to propagate out of the pipe above. + (set -o pipefail; tools/metrics.py diff ~/size0 ~/size1 | tee diff) } ######################################################################################## @@ -117,15 +150,12 @@ function ci_code_size_build { function ci_mpy_format_setup { sudo apt-get update - sudo apt-get install python2.7 sudo pip3 install pyelftools - python2.7 --version python3 --version } function ci_mpy_format_test { # Test mpy-tool.py dump feature on bytecode - python2.7 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy python3 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy # Build MicroPython @@ -143,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 @@ -158,14 +197,17 @@ function ci_cc3200_build { ######################################################################################## # ports/esp32 -# GitHub tag of ESP-IDF to use for CI (note: must be a tag or a branch) -IDF_VER=v5.2.2 +# GitHub tag of ESP-IDF to use for CI, extracted from the esp32 dependency lockfile +# This should end up as a tag name like vX.Y.Z +# (note: This hacky parsing can be replaced with 'yq' once Ubuntu >=24.04 is in use) +IDF_VER=v$(grep -A10 "idf:" ports/esp32/lockfiles/dependencies.lock.esp32 | grep "version:" | head -n1 | sed -E 's/ +version: //') PYTHON=$(command -v python3 2> /dev/null) PYTHON_VER=$(${PYTHON:-python} --version | cut -d' ' -f2) export IDF_CCACHE_ENABLE=1 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 # doing a treeless clone isn't quite as good as --shallow-submodules, but it # is smaller than full clones and works when the submodule commit isn't a head. @@ -204,13 +246,28 @@ function ci_esp32_build_s3_c3 { make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C3 } +function ci_esp32_build_c2_c5_c6 { + ci_esp32_build_common + + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C2 + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C5 + 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 function ci_esp8266_setup { sudo pip3 install pyserial esptool==3.3.1 pyelftools ar - wget https://github.com/jepler/esp-open-sdk/releases/download/2018-06-10/xtensa-lx106-elf-standalone.tar.gz - zcat xtensa-lx106-elf-standalone.tar.gz | tar x + wget https://micropython.org/resources/xtensa-lx106-elf-standalone.tar.gz + (set -o pipefail; zcat xtensa-lx106-elf-standalone.tar.gz | tar x) # Remove this esptool.py so pip version is used instead rm xtensa-lx106-elf/bin/esptool.py } @@ -317,17 +374,52 @@ function ci_qemu_setup_rv32 { qemu-system-riscv32 --version } -function ci_qemu_build_arm { +function ci_qemu_setup_rv64 { + ci_gcc_riscv_setup + sudo apt-get update + sudo apt-get install qemu-system + qemu-system-riscv64 --version +} + +function ci_qemu_build_arm_prepare { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/qemu submodules +} + +function ci_qemu_build_arm_bigendian { + ci_qemu_build_arm_prepare make ${MAKEOPTS} -C ports/qemu CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 - make ${MAKEOPTS} -C ports/qemu clean - make ${MAKEOPTS} -C ports/qemu test_full +} + +function ci_qemu_build_arm_sabrelite { + ci_qemu_build_arm_prepare make ${MAKEOPTS} -C ports/qemu BOARD=SABRELITE test_full +} - # Test building and running native .mpy with armv7m architecture. +function ci_qemu_build_arm_thumb_softfp { + ci_qemu_build_arm_prepare + make BOARD=MPS2_AN385 ${MAKEOPTS} -C ports/qemu test_full + + # Test building native .mpy with ARM-M softfp architectures. + ci_native_mpy_modules_build armv6m ci_native_mpy_modules_build armv7m - make ${MAKEOPTS} -C ports/qemu test_natmod + + # Test running native .mpy with all ARM-M architectures. + make BOARD=MPS2_AN385 ${MAKEOPTS} -C ports/qemu test_natmod RUN_TESTS_EXTRA="--arch armv6m" + make BOARD=MPS2_AN385 ${MAKEOPTS} -C ports/qemu test_natmod RUN_TESTS_EXTRA="--arch armv7m" +} + +function ci_qemu_build_arm_thumb_hardfp { + ci_qemu_build_arm_prepare + make BOARD=MPS2_AN500 ${MAKEOPTS} -C ports/qemu test_full + + # Test building native .mpy with all ARM-M hardfp architectures. + ci_native_mpy_modules_build armv7emsp + ci_native_mpy_modules_build armv7emdp + + # Test running native .mpy with all ARM-M hardfp architectures. + make BOARD=MPS2_AN500 ${MAKEOPTS} -C ports/qemu test_natmod RUN_TESTS_EXTRA="--arch armv7emsp" + make BOARD=MPS2_AN500 ${MAKEOPTS} -C ports/qemu test_natmod RUN_TESTS_EXTRA="--arch armv7emdp" } function ci_qemu_build_rv32 { @@ -340,6 +432,12 @@ function ci_qemu_build_rv32 { make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 test_natmod } +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 +} + ######################################################################################## # ports/renesas-ra @@ -403,13 +501,22 @@ 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 { + # 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 @@ -421,20 +528,18 @@ function ci_stm32_pyb_build { make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBV10 CFLAGS_EXTRA='-DMBOOT_FSLOAD=1 -DMBOOT_VFS_LFS2=1' make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBD_SF6 make ${MAKEOPTS} -C ports/stm32/mboot BOARD=STM32F769DISC CFLAGS_EXTRA='-DMBOOT_ADDRESS_SPACE_64BIT=1 -DMBOOT_SDCARD_ADDR=0x100000000ULL -DMBOOT_SDCARD_BYTE_SIZE=0x400000000ULL -DMBOOT_FSLOAD=1 -DMBOOT_VFS_FAT=1' - - # Test building native .mpy with armv7emsp architecture. - git submodule update --init lib/berkeley-db-1.xx - ci_native_mpy_modules_build armv7emsp } 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 # Test building various MCU families, some with additional options. make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_F091RC - make ${MAKEOPTS} -C ports/stm32 BOARD=STM32H573I_DK + make ${MAKEOPTS} -C ports/stm32 BOARD=STM32H573I_DK CFLAGS_EXTRA='-DMICROPY_HW_TINYUSB_STACK=1' make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_H743ZI COPT=-O2 CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L073RZ make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L476RG DEBUG=1 @@ -454,21 +559,22 @@ 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 } ######################################################################################## # ports/unix -CI_UNIX_OPTS_SYS_SETTRACE=( - MICROPY_PY_BTREE=0 - MICROPY_PY_FFI=0 - MICROPY_PY_SSL=0 - CFLAGS_EXTRA="-DMICROPY_PY_SYS_SETTRACE=1" -) - CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS=( MICROPY_PY_BTREE=0 MICROPY_PY_FFI=0 @@ -494,6 +600,26 @@ CI_UNIX_OPTS_QEMU_RISCV64=( MICROPY_STANDALONE=1 ) +CI_UNIX_OPTS_SANITIZE_ADDRESS=( + # Macro MP_ASAN allows detecting ASan on gcc<=13 + CFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0 -DMP_ASAN=1" + LDFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" +) + +CI_UNIX_OPTS_SANITIZE_UNDEFINED=( + # Macro MP_UBSAN allows detecting UBSan on gcc<=13 + CFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute -DMP_UBSAN=1" + LDFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute" +) + +CI_UNIX_OPTS_REPR_B=( + VARIANT=standard + CFLAGS_EXTRA="-DMICROPY_OBJ_REPR=MICROPY_OBJ_REPR_B -DMICROPY_PY_UCTYPES=0 -Dmp_int_t=int32_t -Dmp_uint_t=uint32_t" + MICROPY_FORCE_32BIT=1 + RUN_TESTS_MPY_CROSS_FLAGS="--mpy-cross-flags=\"-march=x86 -msmall-int-bits=30\"" + +) + function ci_unix_build_helper { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix "$@" submodules @@ -509,13 +635,26 @@ function ci_unix_run_tests_helper { make -C ports/unix "$@" test } +function ci_unix_run_tests_full_extra { + micropython=$1 + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-multitests.py multi_net/*.py) + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-perfbench.py --average 1 1000 1000) +} + +function ci_unix_run_tests_full_no_native_helper { + variant=$1 + shift + micropython=../ports/unix/build-$variant/micropython + make -C ports/unix VARIANT=$variant "$@" test_full_no_native + ci_unix_run_tests_full_extra $micropython +} + function ci_unix_run_tests_full_helper { variant=$1 shift micropython=../ports/unix/build-$variant/micropython make -C ports/unix VARIANT=$variant "$@" test_full - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-multitests.py multi_net/*.py) - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-perfbench.py 1000 1000) + ci_unix_run_tests_full_extra $micropython } function ci_native_mpy_modules_build { @@ -524,40 +663,19 @@ function ci_native_mpy_modules_build { else arch=$1 fi - for natmod in features1 features3 features4 heapq re + for natmod in btree deflate features1 features3 features4 framebuf heapq random re do - make -C examples/natmod/$natmod clean + make -C examples/natmod/$natmod ARCH=$arch clean make -C examples/natmod/$natmod ARCH=$arch done - # deflate, framebuf, and random currently cannot build on xtensa due to - # some symbols that have been removed from the compiler's runtime, in - # favour of being provided from ROM. - if [ $arch != "xtensa" ]; then - for natmod in deflate framebuf random - do - make -C examples/natmod/$natmod clean - make -C examples/natmod/$natmod ARCH=$arch - done - fi - - # features2 requires soft-float on armv7m, rv32imc, and xtensa. On armv6m - # the compiler generates absolute relocations in the object file - # referencing soft-float functions, which is not supported at the moment. - make -C examples/natmod/features2 clean - if [ $arch = "rv32imc" ] || [ $arch = "armv7m" ] || [ $arch = "xtensa" ]; then + # features2 requires soft-float on rv32imc and xtensa. + make -C examples/natmod/features2 ARCH=$arch clean + if [ $arch = "rv32imc" ] || [ $arch = "xtensa" ]; then make -C examples/natmod/features2 ARCH=$arch MICROPY_FLOAT_IMPL=float - elif [ $arch != "armv6m" ]; then + else make -C examples/natmod/features2 ARCH=$arch fi - - # btree requires thread local storage support on rv32imc, whilst on xtensa - # it relies on symbols that are provided from ROM but not exposed to - # natmods at the moment. - if [ $arch != "rv32imc" ] && [ $arch != "xtensa" ]; then - make -C examples/natmod/btree clean - make -C examples/natmod/btree ARCH=$arch - fi } function ci_native_mpy_modules_32bit_build { @@ -569,7 +687,7 @@ function ci_unix_minimal_build { } function ci_unix_minimal_run_tests { - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/build-minimal/micropython ./run-tests.py -e exception_chain -e self_type_check -e subclass_native_init -d basics) + make -C ports/unix VARIANT=minimal test } function ci_unix_standard_build { @@ -591,9 +709,9 @@ function ci_unix_standard_v2_run_tests { } function ci_unix_coverage_setup { - sudo pip3 install setuptools - sudo pip3 install pyelftools - sudo pip3 install ar + pip3 install setuptools + pip3 install pyelftools + pip3 install ar gcc --version python3 --version } @@ -604,7 +722,7 @@ function ci_unix_coverage_build { } function ci_unix_coverage_run_tests { - ci_unix_run_tests_full_helper coverage + MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage } function ci_unix_coverage_run_mpy_merge_tests { @@ -639,12 +757,11 @@ function ci_unix_coverage_run_native_mpy_tests { function ci_unix_32bit_setup { sudo dpkg --add-architecture i386 sudo apt-get update - sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 python2.7 + sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 sudo pip3 install setuptools sudo pip3 install pyelftools sudo pip3 install ar gcc --version - python2.7 --version python3 --version } @@ -662,13 +779,20 @@ function ci_unix_coverage_32bit_run_native_mpy_tests { } function ci_unix_nanbox_build { - # Use Python 2 to check that it can run the build scripts - ci_unix_build_helper PYTHON=python2.7 VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" + ci_unix_build_helper VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" ci_unix_build_ffi_lib_helper gcc -m32 } function ci_unix_nanbox_run_tests { - ci_unix_run_tests_full_helper nanbox PYTHON=python2.7 + ci_unix_run_tests_full_no_native_helper nanbox +} + +function ci_unix_longlong_build { + ci_unix_build_helper VARIANT=longlong "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" +} + +function ci_unix_longlong_run_tests { + ci_unix_run_tests_full_helper longlong } function ci_unix_float_build { @@ -681,7 +805,17 @@ function ci_unix_float_run_tests { ci_unix_run_tests_helper CFLAGS_EXTRA="-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT" } +function ci_unix_gil_enabled_build { + ci_unix_build_helper VARIANT=standard MICROPY_PY_THREAD_GIL=1 + ci_unix_build_ffi_lib_helper gcc +} + +function ci_unix_gil_enabled_run_tests { + ci_unix_run_tests_full_helper standard MICROPY_PY_THREAD_GIL=1 +} + function ci_unix_clang_setup { + sudo apt-get update sudo apt-get install clang clang --version } @@ -693,7 +827,8 @@ function ci_unix_stackless_clang_build { } function ci_unix_stackless_clang_run_tests { - ci_unix_run_tests_helper CC=clang + # Timeout needs to be increased for thread/stress_aes.py test. + MICROPY_TEST_TIMEOUT=90 ci_unix_run_tests_helper CC=clang } function ci_unix_float_clang_build { @@ -706,24 +841,36 @@ function ci_unix_float_clang_run_tests { ci_unix_run_tests_helper CC=clang } -function ci_unix_settrace_build { +function ci_unix_settrace_stackless_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/unix submodules + make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" +} + +function ci_unix_settrace_stackless_run_tests { + ci_unix_run_tests_full_helper standard "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" +} + +function ci_unix_sanitize_undefined_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix submodules - make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE[@]}" + make ${MAKEOPTS} -C ports/unix VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" + ci_unix_build_ffi_lib_helper gcc } -function ci_unix_settrace_run_tests { - ci_unix_run_tests_full_helper standard "${CI_UNIX_OPTS_SYS_SETTRACE[@]}" +function ci_unix_sanitize_undefined_run_tests { + MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" } -function ci_unix_settrace_stackless_build { +function ci_unix_sanitize_address_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix submodules - make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" + make ${MAKEOPTS} -C ports/unix VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}" + ci_unix_build_ffi_lib_helper gcc } -function ci_unix_settrace_stackless_run_tests { - ci_unix_run_tests_full_helper standard "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" +function ci_unix_sanitize_address_run_tests { + MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}" } function ci_unix_macos_build { @@ -740,7 +887,9 @@ function ci_unix_macos_run_tests { # Issues with macOS tests: # - float_parse and float_parse_doubleprec parse/print floats out by a few mantissa bits # - ffi_callback crashes for an unknown reason - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-standard/micropython ./run-tests.py --exclude '(float_parse|float_parse_doubleprec|ffi_callback).py') + # - thread/stress_heap.py is flaky + # - thread/thread_gc1.py is flaky + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-standard/micropython ./run-tests.py --exclude '(float_parse|float_parse_doubleprec|ffi_callback|thread/stress_heap|thread/thread_gc1).py') } function ci_unix_qemu_mips_setup { @@ -758,8 +907,12 @@ 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_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 ./run-tests.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_arm_setup { @@ -779,8 +932,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 ./run-tests.py --exclude 'vfs_posix.*\.py') + (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') } function ci_unix_qemu_riscv64_setup { @@ -798,14 +954,30 @@ function ci_unix_qemu_riscv64_build { } function ci_unix_qemu_riscv64_run_tests { + # Issues with RISCV-64 tests: + # - thread/stress_aes.py takes around 140 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 ./run-tests.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_repr_b_build { + ci_unix_build_helper "${CI_UNIX_OPTS_REPR_B[@]}" + ci_unix_build_ffi_lib_helper gcc -m32 +} + +function ci_unix_repr_b_run_tests { + # ci_unix_run_tests_full_no_native_helper is not used due to + # https://github.com/micropython/micropython/issues/18105 + ci_unix_run_tests_helper "${CI_UNIX_OPTS_REPR_B[@]}" } ######################################################################################## # ports/windows function ci_windows_setup { + sudo apt-get update sudo apt-get install gcc-mingw-w64 } @@ -813,14 +985,15 @@ function ci_windows_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/windows submodules make ${MAKEOPTS} -C ports/windows CROSS_COMPILE=i686-w64-mingw32- + make ${MAKEOPTS} -C ports/windows CROSS_COMPILE=x86_64-w64-mingw32- BUILD=build-standard-w64 } ######################################################################################## # ports/zephyr -ZEPHYR_DOCKER_VERSION=v0.26.13 -ZEPHYR_SDK_VERSION=0.16.8 -ZEPHYR_VERSION=v3.7.0 +ZEPHYR_DOCKER_VERSION=v0.28.1 +ZEPHYR_SDK_VERSION=0.17.2 +ZEPHYR_VERSION=v4.2.0 function ci_zephyr_setup { IMAGE=ghcr.io/zephyrproject-rtos/ci:${ZEPHYR_DOCKER_VERSION} @@ -859,6 +1032,7 @@ function ci_zephyr_install { } function ci_zephyr_build { + git submodule update --init lib/micropython-lib docker exec zephyr-ci west build -p auto -b qemu_x86 -- -DCONF_FILE=prj_minimal.conf docker exec zephyr-ci west build -p auto -b frdm_k64f docker exec zephyr-ci west build -p auto -b mimxrt1050_evk @@ -867,9 +1041,7 @@ function ci_zephyr_build { function ci_zephyr_run_tests { docker exec zephyr-ci west build -p auto -b qemu_cortex_m3 -- -DCONF_FILE=prj_minimal.conf - # Issues with zephyr tests: - # - inf_nan_arith fails pow(-1, nan) test - (cd tests && ./run-tests.py -t execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf" -d basics float --exclude inf_nan_arith) + (cd tests && ./run-tests.py -t execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf") } ######################################################################################## @@ -886,3 +1058,86 @@ function ci_alif_ae3_build { make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_DUAL make ${MAKEOPTS} -C ports/alif BOARD=ALIF_ENSEMBLE MCU_CORE=M55_DUAL } + +function _ci_help { + # Note: these lines must be indented with tab characters (required by bash <<-EOF) + cat <<-EOF + ci.sh: Script fragments used during CI + + When invoked as a script, runs a sequence of ci steps, + stopping after the first error. + + Usage: + ${BASH_SOURCE} step1 step2... + + Steps: + EOF + if type -path column > /dev/null 2>&1; then + grep '^function ci_' $0 | awk '{print $2}' | sed 's/^ci_//' | column + else + grep '^function ci_' $0 | awk '{print $2}' | sed 's/^ci_//' + fi + exit +} + +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) + _ci_help + ;; + (--bash-completion) + _ci_bash_completion + ;; + (--zsh-completion) + _ci_zsh_completion + ;; + (--fish-completion) + _ci_fish_completion + ;; + (-*) + echo "Unknown option: $1" 1>&2 + exit 1 + ;; + (*) + set -e + cd $(dirname "$0")/.. + while [ $# -ne 0 ]; do + ci_$1 + shift + done + ;; + esac +} + +# https://stackoverflow.com/questions/2683279/how-to-detect-if-a-script-is-being-sourced +sourced=0 +if [ -n "$ZSH_VERSION" ]; then + case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac +elif [ -n "$KSH_VERSION" ]; then + [ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && sourced=1 +elif [ -n "$BASH_VERSION" ]; then + (return 0 2>/dev/null) && sourced=1 +else # All other shells: examine $0 for known shell binary filenames. + # Detects `sh` and `dash`; add additional shell filenames as needed. + case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac +fi + +if [ $sourced -eq 0 ]; then + # invoked as a command + if [ "$#" -eq 0 ]; then + set -- --help + fi + _ci_main "$@" +fi diff --git a/tools/codeformat.py b/tools/codeformat.py index a648d401ec312..cf91049a73162 100644 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -231,7 +231,7 @@ def batch(cmd, files, N=200, check=False): command = ["python3", "tools/ruff_bindings.py"] batch(command, bindings_files(), check=True) - # Format Python files with black. + # Format Python files with "ruff format" (using config in pyproject.toml). if format_py: command = ["ruff", "format"] if args.v: diff --git a/tools/file2h.py b/tools/file2h.py index df9cc02fdabba..2707d4a16e5cf 100644 --- a/tools/file2h.py +++ b/tools/file2h.py @@ -9,8 +9,6 @@ # ; # This script simply prints the escaped string straight to stdout -from __future__ import print_function - import sys # Can either be set explicitly, or left blank to auto-detect diff --git a/tools/insert-usb-ids.py b/tools/insert-usb-ids.py index 4691d5a710c1e..1b043e1fef04f 100644 --- a/tools/insert-usb-ids.py +++ b/tools/insert-usb-ids.py @@ -6,8 +6,6 @@ # inserts those values into the template file specified by sys.argv[2], # printing the result to stdout -from __future__ import print_function - import sys import re import string diff --git a/tools/makemanifest.py b/tools/makemanifest.py index e076a03e0be3c..860935397af14 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -24,7 +24,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import print_function import sys import os import subprocess diff --git a/tools/manifestfile.py b/tools/manifestfile.py index beaa36d0f5fc2..9c7a6e140f968 100644 --- a/tools/manifestfile.py +++ b/tools/manifestfile.py @@ -25,7 +25,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import print_function import contextlib import os import sys diff --git a/tools/metrics.py b/tools/metrics.py index f6189e65abb4d..8bb96ba119a23 100755 --- a/tools/metrics.py +++ b/tools/metrics.py @@ -43,20 +43,23 @@ """ -import collections, sys, re, subprocess +import collections, os, sys, re, shlex, subprocess, multiprocessing -MAKE_FLAGS = ["-j3", "CFLAGS_EXTRA=-DNDEBUG"] +MAKE_FLAGS = ["-j{}".format(multiprocessing.cpu_count()), "CFLAGS_EXTRA=-DNDEBUG"] class PortData: - def __init__(self, name, dir, output, make_flags=None): + def __init__(self, name, dir, output, make_flags=None, pre_cmd=None): self.name = name self.dir = dir self.output = output self.make_flags = make_flags self.needs_mpy_cross = dir not in ("bare-arm", "minimal") + self.pre_cmd = pre_cmd +mpy_cross_output = "mpy-cross/build/mpy-cross" + port_data = { "b": PortData("bare-arm", "bare-arm", "build/firmware.elf"), "m": PortData("minimal x86", "minimal", "build/firmware.elf"), @@ -65,7 +68,12 @@ def __init__(self, name, dir, output, make_flags=None): "s": PortData("stm32", "stm32", "build-PYBV10/firmware.elf", "BOARD=PYBV10"), "c": PortData("cc3200", "cc3200", "build/WIPY/release/application.axf", "BTARGET=application"), "8": PortData("esp8266", "esp8266", "build-ESP8266_GENERIC/firmware.elf"), - "3": PortData("esp32", "esp32", "build-ESP32_GENERIC/micropython.elf"), + "3": PortData( + "esp32", + "esp32", + "build-ESP32_GENERIC/micropython.elf", + pre_cmd=". esp-idf/export.sh", + ), "x": PortData("mimxrt", "mimxrt", "build-TEENSY40/firmware.elf"), "e": PortData("renesas-ra", "renesas-ra", "build-EK_RA6M2/firmware.elf"), "r": PortData("nrf", "nrf", "build-PCA10040/firmware.elf"), @@ -74,8 +82,15 @@ def __init__(self, name, dir, output, make_flags=None): "v": PortData("qemu rv32", "qemu", "build-VIRT_RV32/firmware.elf", "BOARD=VIRT_RV32"), } +for port_letter, port in port_data.items(): + port.pre_cmd = os.environ.get(f"PRE_CMD_{port_letter}", port.pre_cmd) + + +def quoted(args): + return " ".join(shlex.quote(word) for word in args) -def syscmd(*args): + +def syscmd(*args, pre_cmd=None): sys.stdout.flush() a2 = [] for a in args: @@ -83,6 +98,10 @@ def syscmd(*args): a2.append(a) elif a: a2.extend(a) + if pre_cmd is not None: + a2_quoted = quoted(a2) + a2 = ["bash", "-c", "{} && {}".format(pre_cmd, a2_quoted)] + print(a2) subprocess.check_call(a2) @@ -108,6 +127,8 @@ def read_build_log(filename): with open(filename) as f: for line in f: line = line.strip() + if line.startswith("BUILDING ") and "_ref" not in data: + data["_ref"] = line.removeprefix("BUILDING ") if line.strip() == "COMPUTING SIZES": found_sizes = True elif found_sizes: @@ -139,9 +160,15 @@ def do_diff(args): data1 = read_build_log(args[0]) data2 = read_build_log(args[1]) + ref1 = data1.pop("_ref", "(unknown ref)") + ref2 = data2.pop("_ref", "(unknown ref)") + print(f"Reference: {ref1}") + print(f"Comparison: {ref2}") max_delta = None for key, value1 in data1.items(): value2 = data2[key] + if key == mpy_cross_output: + name = "mpy-cross" for port in port_data.values(): if key == "ports/{}/{}".format(port.dir, port.output): name = port.name @@ -181,8 +208,19 @@ def do_clean(args): ports = parse_port_list(args) print("CLEANING") + + if any(port.needs_mpy_cross for port in ports): + syscmd("make", "-C", "mpy-cross", "clean") + for port in ports: - syscmd("make", "-C", "ports/{}".format(port.dir), port.make_flags, "clean") + syscmd( + "make", + "-C", + "ports/{}".format(port.dir), + port.make_flags, + "clean", + pre_cmd=port.pre_cmd, + ) def do_build(args): @@ -196,7 +234,14 @@ def do_build(args): print("BUILDING PORTS") for port in ports: - syscmd("make", "-C", "ports/{}".format(port.dir), MAKE_FLAGS, port.make_flags) + syscmd( + "make", + "-C", + "ports/{}".format(port.dir), + MAKE_FLAGS, + port.make_flags, + pre_cmd=port.pre_cmd, + ) do_sizes(args) @@ -207,6 +252,10 @@ def do_sizes(args): ports = parse_port_list(args) print("COMPUTING SIZES") + + if any(port.needs_mpy_cross for port in ports): + syscmd("size", mpy_cross_output) + for port in ports: syscmd("size", "ports/{}/{}".format(port.dir, port.output)) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 8849f2f1e5956..bf0b89018e4e7 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -24,40 +24,21 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# Python 2/3/MicroPython compatibility code -from __future__ import print_function +import io +import struct import sys +from binascii import hexlify -if sys.version_info[0] == 2: - from binascii import hexlify as hexlify_py2 - - str_cons = lambda val, enc=None: str(val) - bytes_cons = lambda val, enc=None: bytearray(val) - is_str_type = lambda o: isinstance(o, str) - is_bytes_type = lambda o: type(o) is bytearray - is_int_type = lambda o: isinstance(o, int) or isinstance(o, long) # noqa: F821 - - def hexlify_to_str(b): - x = hexlify_py2(b) - return ":".join(x[i : i + 2] for i in range(0, len(x), 2)) - -elif sys.version_info[0] == 3: # Also handles MicroPython - from binascii import hexlify - - str_cons = str - bytes_cons = bytes - is_str_type = lambda o: isinstance(o, str) - is_bytes_type = lambda o: isinstance(o, bytes) - is_int_type = lambda o: isinstance(o, int) - - def hexlify_to_str(b): - return str(hexlify(b, ":"), "ascii") +str_cons = str +bytes_cons = bytes +is_str_type = lambda o: isinstance(o, str) +is_bytes_type = lambda o: isinstance(o, bytes) +is_int_type = lambda o: isinstance(o, int) -# end compatibility code +def hexlify_to_str(b): + return str(hexlify(b, ":"), "ascii") -import sys -import struct sys.path.append(sys.path[0] + "/../py") import makeqstrdata as qstrutil @@ -114,6 +95,23 @@ class Config: MP_NATIVE_ARCH_XTENSA = 9 MP_NATIVE_ARCH_XTENSAWIN = 10 MP_NATIVE_ARCH_RV32IMC = 11 +MP_NATIVE_ARCH_RV64IMC = 12 + +MP_NATIVE_ARCH_NAMES = [ + "NONE", + "X86", + "X64", + "ARMV6", + "ARMV6M", + "ARMV7M", + "ARMV7EM", + "ARMV7EMSP", + "ARMV7EMDP", + "XTENSA", + "XTENSAWIN", + "RV32IMC", + "RV64IMC", +] MP_PERSISTENT_OBJ_FUN_TABLE = 0 MP_PERSISTENT_OBJ_NONE = 1 @@ -142,6 +140,8 @@ class Config: MP_BC_FORMAT_VAR_UINT = 2 MP_BC_FORMAT_OFFSET = 3 +MP_NATIVE_ARCH_FLAGS_PRESENT = 0x40 + mp_unary_op_method_name = ( "__pos__", "__neg__", @@ -306,6 +306,25 @@ class Opcode: MP_BC_POP_JUMP_IF_TRUE, MP_BC_POP_JUMP_IF_FALSE, ) + ALL_OFFSET = ( + MP_BC_UNWIND_JUMP, + MP_BC_JUMP, + MP_BC_POP_JUMP_IF_TRUE, + MP_BC_POP_JUMP_IF_FALSE, + MP_BC_JUMP_IF_TRUE_OR_POP, + MP_BC_JUMP_IF_FALSE_OR_POP, + MP_BC_SETUP_WITH, + MP_BC_SETUP_EXCEPT, + MP_BC_SETUP_FINALLY, + MP_BC_POP_EXCEPT_JUMP, + MP_BC_FOR_ITER, + ) + ALL_WITH_CHILD = ( + MP_BC_MAKE_FUNCTION, + MP_BC_MAKE_FUNCTION_DEFARGS, + MP_BC_MAKE_CLOSURE, + MP_BC_MAKE_CLOSURE_DEFARGS, + ) # Create a dict mapping opcode value to opcode name. mapping = ["unknown" for _ in range(256)] @@ -562,6 +581,7 @@ def __init__( mpy_source_file, mpy_segments, header, + arch_flags, qstr_table, obj_table, raw_code, @@ -574,6 +594,7 @@ def __init__( self.mpy_segments = mpy_segments self.source_file = qstr_table[0] self.header = header + self.arch_flags = arch_flags self.qstr_table = qstr_table self.obj_table = obj_table self.raw_code = raw_code @@ -651,6 +672,14 @@ def disassemble(self): print("mpy_source_file:", self.mpy_source_file) print("source_file:", self.source_file.str) print("header:", hexlify_to_str(self.header)) + arch_index = (self.header[2] >> 2) & 0x2F + if arch_index >= len(MP_NATIVE_ARCH_NAMES): + arch_name = "UNKNOWN" + else: + arch_name = MP_NATIVE_ARCH_NAMES[arch_index] + print("arch:", arch_name) + if self.header[2] & MP_NATIVE_ARCH_FLAGS_PRESENT != 0: + print("arch_flags:", hex(self.arch_flags)) print("qstr_table[%u]:" % len(self.qstr_table)) for q in self.qstr_table: print(" %s" % q.str) @@ -888,7 +917,7 @@ def __init__(self, parent_name, qstr_table, fun_data, prelude_offset, code_kind) self.escaped_name = unique_escaped_name def disassemble_children(self): - print(" children:", [rc.simple_name.str for rc in self.children]) + self.print_children_annotated() for rc in self.children: rc.disassemble() @@ -980,6 +1009,75 @@ def freeze_raw_code(self, prelude_ptr=None, type_sig=0): raw_code_count += 1 raw_code_content += 4 * 4 + @staticmethod + def decode_lineinfo(line_info: memoryview) -> "tuple[int, int, memoryview]": + c = line_info[0] + if (c & 0x80) == 0: + # 0b0LLBBBBB encoding + return (c & 0x1F), (c >> 5), line_info[1:] + else: + # 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + return (c & 0xF), (((c << 4) & 0x700) | line_info[1]), line_info[2:] + + def get_source_annotation(self, ip: int, file=None) -> dict: + bc_offset = ip - self.offset_opcodes + try: + line_info = memoryview(self.fun_data)[self.offset_line_info : self.offset_opcodes] + except AttributeError: + return {"file": file, "line": None} + + source_line = 1 + while line_info: + bc_increment, line_increment, line_info = self.decode_lineinfo(line_info) + if bc_offset >= bc_increment: + bc_offset -= bc_increment + source_line += line_increment + else: + break + + return {"file": file, "line": source_line} + + def get_label(self, ip: "int | None" = None, child_num: "int | None" = None) -> str: + if ip is not None: + assert child_num is None + return "%s.%d" % (self.escaped_name, ip) + elif child_num is not None: + return "%s.child%d" % (self.escaped_name, child_num) + else: + return "%s" % self.escaped_name + + def print_children_annotated(self) -> None: + """ + Equivalent to `print(" children:", [child.simple_name.str for child in self.children])`, + but also includes json markers for the start and end of each one's name in that line. + """ + + labels = ["%s.children" % self.escaped_name] + annotation_labels = [] + output = io.StringIO() + output.write(" children: [") + sep = ", " + for i, child in enumerate(self.children): + if i != 0: + output.write(sep) + start_col = output.tell() + 1 + output.write(child.simple_name.str) + end_col = output.tell() + 1 + labels.append(self.get_label(child_num=i)) + annotation_labels.append( + { + "name": self.get_label(child_num=i), + "target": child.get_label(), + "range": { + "startCol": start_col, + "endCol": end_col, + }, + }, + ) + output.write("]") + + print(output.getvalue(), annotations={"labels": annotation_labels}, labels=labels) + class RawCodeBytecode(RawCode): def __init__(self, parent_name, qstr_table, obj_table, fun_data): @@ -988,9 +1086,58 @@ def __init__(self, parent_name, qstr_table, obj_table, fun_data): parent_name, qstr_table, fun_data, 0, MP_CODE_BYTECODE ) + def get_opcode_annotations_labels( + self, opcode: int, ip: int, arg: int, sz: int, arg_pos: int, arg_len: int + ) -> "tuple[dict, list[str]]": + annotations = { + "source": self.get_source_annotation(ip), + "disassembly": Opcode.mapping[opcode], + } + labels = [self.get_label(ip)] + + if opcode in Opcode.ALL_OFFSET: + annotations["link"] = { + "offset": arg_pos, + "length": arg_len, + "to": ip + arg + sz, + } + annotations["labels"] = [ + { + "name": self.get_label(ip), + "target": self.get_label(ip + arg + sz), + "range": { + "startCol": arg_pos + 1, + "endCol": arg_pos + arg_len + 1, + }, + }, + ] + + elif opcode in Opcode.ALL_WITH_CHILD: + try: + child = self.children[arg] + except IndexError: + # link out-of-range child to the child array itself + target = "%s.children" % self.escaped_name + else: + # link resolvable child to the actual child + target = child.get_label() + + annotations["labels"] = [ + { + "name": self.get_label(ip), + "target": target, + "range": { + "startCol": arg_pos + 1, + "endCol": arg_pos + arg_len + 1, + }, + }, + ] + + return annotations, labels + def disassemble(self): bc = self.fun_data - print("simple_name:", self.simple_name.str) + print("simple_name:", self.simple_name.str, labels=[self.get_label()]) print(" raw bytecode:", len(bc), hexlify_to_str(bc)) print(" prelude:", self.prelude_signature) print(" args:", [self.qstr_table[i].str for i in self.names[1:]]) @@ -1006,9 +1153,22 @@ def disassemble(self): pass else: arg = "" - print( - " %-11s %s %s" % (hexlify_to_str(bc[ip : ip + sz]), Opcode.mapping[bc[ip]], arg) + + pre_arg_part = " %-11s %s" % ( + hexlify_to_str(bc[ip : ip + sz]), + Opcode.mapping[bc[ip]], + ) + arg_part = "%s" % arg + annotations, labels = self.get_opcode_annotations_labels( + opcode=bc[ip], + ip=ip, + arg=arg, + sz=sz, + arg_pos=len(pre_arg_part) + 1, + arg_len=len(arg_part), ) + + print(pre_arg_part, arg_part, annotations=annotations, labels=labels) ip += sz self.disassemble_children() @@ -1085,6 +1245,7 @@ def __init__( MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN, MP_NATIVE_ARCH_RV32IMC, + MP_NATIVE_ARCH_RV64IMC, ): self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",@progbits # ")))' else: @@ -1102,13 +1263,13 @@ def __init__( self.fun_data_attributes += " __attribute__ ((aligned (4)))" elif ( MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP - ) or config.native_arch == MP_NATIVE_ARCH_RV32IMC: - # ARMVxxM or RV32IMC -- two byte align. + ) or MP_NATIVE_ARCH_RV32IMC <= config.native_arch <= MP_NATIVE_ARCH_RV64IMC: + # ARMVxxM or RV{32,64}IMC -- two byte align. self.fun_data_attributes += " __attribute__ ((aligned (2)))" def disassemble(self): fun_data = self.fun_data - print("simple_name:", self.simple_name.str) + print("simple_name:", self.simple_name.str, labels=[self.get_label()]) print( " raw data:", len(fun_data), @@ -1362,7 +1523,7 @@ def read_mpy(filename): if header[1] != config.MPY_VERSION: raise MPYReadError(filename, "incompatible .mpy version") feature_byte = header[2] - mpy_native_arch = feature_byte >> 2 + mpy_native_arch = (feature_byte >> 2) & 0x2F if mpy_native_arch != MP_NATIVE_ARCH_NONE: mpy_sub_version = feature_byte & 3 if mpy_sub_version != config.MPY_SUB_VERSION: @@ -1373,6 +1534,11 @@ def read_mpy(filename): raise MPYReadError(filename, "native architecture mismatch") config.mp_small_int_bits = header[3] + arch_flags = 0 + # Read the architecture-specific flag bits if present. + if (feature_byte & MP_NATIVE_ARCH_FLAGS_PRESENT) != 0: + arch_flags = reader.read_uint() + # Read number of qstrs, and number of objects. n_qstr = reader.read_uint() n_obj = reader.read_uint() @@ -1401,6 +1567,7 @@ def read_mpy(filename): filename, segments, header, + arch_flags, qstr_table, obj_table, raw_code, @@ -1696,26 +1863,40 @@ def merge_mpy(compiled_modules, output_file): merged_mpy.extend(f.read()) else: main_cm_idx = None + arch_flags = 0 for idx, cm in enumerate(compiled_modules): feature_byte = cm.header[2] - mpy_native_arch = feature_byte >> 2 + mpy_native_arch = (feature_byte >> 2) & 0x2F if mpy_native_arch: # Must use qstr_table and obj_table from this raw_code if main_cm_idx is not None: raise Exception("can't merge files when more than one contains native code") main_cm_idx = idx + arch_flags = cm.arch_flags if main_cm_idx is not None: # Shift main_cm to front of list. compiled_modules.insert(0, compiled_modules.pop(main_cm_idx)) + if config.arch_flags is not None: + arch_flags = config.arch_flags + header = bytearray(4) ## CIRCUITPY-CHANGE: "C" is used for CircuitPython header[0] = ord("C") header[1] = config.MPY_VERSION - header[2] = config.native_arch << 2 | config.MPY_SUB_VERSION if config.native_arch else 0 + header[2] = ( + (MP_NATIVE_ARCH_FLAGS_PRESENT if arch_flags != 0 else 0) + | config.native_arch << 2 + | config.MPY_SUB_VERSION + if config.native_arch + else 0 + ) header[3] = config.mp_small_int_bits merged_mpy.extend(header) + if arch_flags != 0: + merged_mpy.extend(mp_encode_uint(arch_flags)) + n_qstr = 0 n_obj = 0 for cm in compiled_modules: @@ -1771,6 +1952,138 @@ def copy_section(file, offset, offset2): f.write(merged_mpy) +def extract_segments(compiled_modules, basename, kinds_arg): + import re + + kind_str = ("META", "QSTR", "OBJ", "CODE") + kinds = set() + if kinds_arg is not None: + for kind in kinds_arg.upper().split(","): + if kind in kind_str: + kinds.add(kind) + else: + raise Exception('unknown segment kind "%s"' % (kind,)) + segments = [] + for module in compiled_modules: + for segment in module.mpy_segments: + if not kinds or kind_str[segment.kind] in kinds: + segments.append((module.mpy_source_file, module.source_file.str, segment)) + count_len = len(str(len(segments))) + sanitiser = re.compile("[^a-zA-Z0-9_.-]") + for counter, entry in enumerate(segments): + file_name, source_file, segment = entry + output_name = ( + basename + + "_" + + str(counter).rjust(count_len, "0") + + "_" + + sanitiser.sub("_", source_file) + + "_" + + kind_str[segment.kind] + + "_" + + sanitiser.sub("_", str(segment.name)) + + ".bin" + ) + with open(file_name, "rb") as source: + with open(output_name, "wb") as output: + source.seek(segment.start) + output.write(source.read(segment.end - segment.start)) + + +class PrintShim: + """Base class for interposing extra functionality onto the global `print` method.""" + + def __init__(self): + self.wrapped_print = None + + def __enter__(self): + global print + + if self.wrapped_print is not None: + raise RecursionError + + self.wrapped_print = print + print = self + + return self + + def __exit__(self, exc_type, exc_value, traceback): + global print + + if self.wrapped_print is None: + return + + print = self.wrapped_print + self.wrapped_print = None + + self.on_exit() + + def on_exit(self): + pass + + def __call__(self, *a, **k): + return self.wrapped_print(*a, **k) + + +class PrintIgnoreExtraArgs(PrintShim): + """Just strip the `annotations` and `labels` kwargs and pass down to the underlying print.""" + + def __call__(self, *a, annotations: dict = {}, labels: "list[str]" = (), **k): + return super().__call__(*a, **k) + + +class PrintJson(PrintShim): + """Output lines as godbolt-compatible JSON with extra annotation info from `annotations` and `labels`, rather than plain text.""" + + def __init__(self, fp=sys.stdout, language_id: str = "mpy"): + super().__init__() + self.fp = fp + self.asm = { + "asm": [], + "labelDefinitions": {}, + "languageId": language_id, + } + self.line_number: int = 0 + self.buf: "io.StringIO | None" = None + + def on_exit(self): + import json + + if self.buf is not None: + # flush last partial line + self.__call__() + + json.dump(self.asm, self.fp) + + def __call__(self, *a, annotations: dict = {}, labels: "list[str]" = (), **k): + # ignore prints directed to an explicit output + if "file" in k: + return super().__call__(*a, **k) + + if self.buf is None: + self.buf = io.StringIO() + + super().__call__(*a, file=sys.stderr, **k) + + if "end" in k: + # buffer partial-line prints to collect into a single AsmResultLine + return super().__call__(*a, file=self.buf, **k) + else: + retval = super().__call__(*a, file=self.buf, end="", **k) + output = self.buf.getvalue() + self.buf = None + + asm_line = {"text": output} + asm_line.update(annotations) + self.asm["asm"].append(asm_line) + + self.line_number += 1 + for label in labels: + self.asm["labelDefinitions"][label] = self.line_number + + return retval + + def main(args=None): global global_qstrs @@ -1784,9 +2097,23 @@ def main(args=None): "-d", "--disassemble", action="store_true", help="output disassembled contents of files" ) cmd_parser.add_argument("-f", "--freeze", action="store_true", help="freeze files") + cmd_parser.add_argument( + "-j", + "--json", + action="store_true", + help="output hexdump, disassembly, and frozen code as JSON with extra metadata", + ) cmd_parser.add_argument( "--merge", action="store_true", help="merge multiple .mpy files into one" ) + cmd_parser.add_argument( + "-e", "--extract", metavar="BASE", type=str, help="write segments into separate files" + ) + cmd_parser.add_argument( + "--extract-only", + metavar="KIND[,...]", + help="extract only segments of the given type (meta, qstr, obj, code)", + ) cmd_parser.add_argument("-q", "--qstr-header", help="qstr header file to freeze against") cmd_parser.add_argument( "-mlongint-impl", @@ -1801,6 +2128,12 @@ def main(args=None): default=16, help="mpz digit size used by target (default 16)", ) + cmd_parser.add_argument( + "-march-flags", + metavar="F", + type=int, + help="architecture flags value to set in the output file (strips existing flags if not present)", + ) cmd_parser.add_argument("-o", "--output", default=None, help="output file") cmd_parser.add_argument("files", nargs="+", help="input .mpy files") args = cmd_parser.parse_args(args) @@ -1813,6 +2146,7 @@ def main(args=None): }[args.mlongint_impl] config.MPZ_DIG_SIZE = args.mmpz_dig_size config.native_arch = MP_NATIVE_ARCH_NONE + config.arch_flags = args.march_flags # set config values for qstrs, and get the existing base set of qstrs # already in the firmware @@ -1836,24 +2170,40 @@ def main(args=None): print(er, file=sys.stderr) sys.exit(1) - if args.hexdump: - hexdump_mpy(compiled_modules) + if args.json: + if args.freeze: + print_shim = PrintJson(sys.stdout, language_id="c") + elif args.hexdump: + print_shim = PrintJson(sys.stdout, language_id="stderr") + elif args.disassemble: + print_shim = PrintJson(sys.stdout, language_id="mpy") + else: + print_shim = PrintJson(sys.stdout) + else: + print_shim = PrintIgnoreExtraArgs() - if args.disassemble: + with print_shim: if args.hexdump: - print() - disassemble_mpy(compiled_modules) + hexdump_mpy(compiled_modules) - if args.freeze: - try: - freeze_mpy(firmware_qstr_idents, compiled_modules) - except FreezeError as er: - print(er, file=sys.stderr) - sys.exit(1) + if args.disassemble: + if args.hexdump: + print() + disassemble_mpy(compiled_modules) + + if args.freeze: + try: + freeze_mpy(firmware_qstr_idents, compiled_modules) + except FreezeError as er: + print(er, file=sys.stderr) + sys.exit(1) if args.merge: merge_mpy(compiled_modules, args.output) + if args.extract: + extract_segments(compiled_modules, args.extract, args.extract_only) + if __name__ == "__main__": main() diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 219cd1a7468bc..26db07261631c 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -402,6 +402,7 @@ def __init__(self, arch): self.known_syms = {} # dict of symbols that are defined self.unresolved_syms = [] # list of unresolved symbols self.mpy_relocs = [] # list of relocations needed in the output .mpy file + self.externs = {} # dict of externally-defined symbols def check_arch(self, arch_name): if arch_name != self.arch.name: @@ -491,10 +492,14 @@ def populate_got(env): sym = got_entry.sym if hasattr(sym, "resolved"): sym = sym.resolved - sec = sym.section - addr = sym["st_value"] - got_entry.sec_name = sec.name - got_entry.link_addr += sec.addr + addr + if sym.name in env.externs: + got_entry.sec_name = ".external.fixed_addr" + got_entry.link_addr = env.externs[sym.name] + else: + sec = sym.section + addr = sym["st_value"] + got_entry.sec_name = sec.name + got_entry.link_addr += sec.addr + addr # Get sorted GOT, sorted by external, text, rodata, bss so relocations can be combined got_list = sorted( @@ -520,6 +525,9 @@ def populate_got(env): dest = int(got_entry.name.split("+")[1], 16) // env.arch.word_size elif got_entry.sec_name == ".external.mp_fun_table": dest = got_entry.sym.mp_fun_table_offset + elif got_entry.sec_name == ".external.fixed_addr": + # Fixed-address symbols should not be relocated. + continue elif got_entry.sec_name.startswith(".text"): dest = ".text" elif got_entry.sec_name.startswith(".rodata"): @@ -703,8 +711,9 @@ def do_relocation_text(env, text_addr, r): (addr, value) = process_riscv32_relocation(env, text_addr, r) elif env.arch.name == "EM_ARM" and r_info_type == R_ARM_ABS32: - # happens for soft-float on armv6m - raise ValueError("Absolute relocations not supported on ARM") + # Absolute relocation, handled as a data relocation. + do_relocation_data(env, text_addr, r) + return else: # Unknown/unsupported relocation @@ -773,9 +782,9 @@ def do_relocation_data(env, text_addr, r): ): # Relocation in data.rel.ro to internal/external symbol if env.arch.word_size == 4: - struct_type = "/). + + symbols = {} + + LINE_REGEX = re.compile( + r"^(?PPROVIDE\()?" # optional weak marker start + r"(?P[a-zA-Z_]\w*)" # symbol name + r"=0x(?P
[\da-fA-F]{1,8})*" # symbol address + r"(?(weak)\));$", # optional weak marker end and line terminator + re.ASCII, + ) + + inside_comment = False + for line in (line.strip() for line in source.readlines()): + if line.startswith("/*") and not inside_comment: + if not line.endswith("*/"): + inside_comment = True + continue + if inside_comment: + if line.endswith("*/"): + inside_comment = False + continue + if line.startswith("//"): + continue + match = LINE_REGEX.match("".join(line.split())) + if not match: + continue + tokens = match.groupdict() + symbol = tokens["symbol"] + address = int(tokens["address"], 16) + if symbol in symbols: + raise ValueError(f"Symbol {symbol} already defined") + symbols[symbol] = address + return symbols + + def main(): import argparse @@ -1501,6 +1569,13 @@ def main(): cmd_parser.add_argument( "--output", "-o", default=None, help="output .mpy file (default to input with .o->.mpy)" ) + cmd_parser.add_argument( + "--externs", + "-e", + type=argparse.FileType("rt"), + default=None, + help="linkerscript providing fixed-address symbols to augment symbol resolution", + ) cmd_parser.add_argument("files", nargs="+", help="input files") args = cmd_parser.parse_args() diff --git a/tools/pyboard.py b/tools/pyboard.py index 20310ba7081c0..c9f65d5d873c2 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -248,14 +248,22 @@ def inWaiting(self): class Pyboard: def __init__( - self, device, baudrate=115200, user="micro", password="python", wait=0, exclusive=True + self, + device, + baudrate=115200, + user="micro", + password="python", + wait=0, + exclusive=True, + timeout=None, + write_timeout=5, ): self.in_raw_repl = False self.use_raw_paste = True if device.startswith("exec:"): self.serial = ProcessToSerial(device[len("exec:") :]) elif device.startswith("execpty:"): - self.serial = ProcessPtyToTerminal(device[len("qemupty:") :]) + self.serial = ProcessPtyToTerminal(device[len("execpty:") :]) elif device and device[0].isdigit() and device[-1].isdigit() and device.count(".") == 3: # device looks like an IP address self.serial = TelnetToSerial(device, user, password, read_timeout=10) @@ -264,7 +272,12 @@ def __init__( import serial.tools.list_ports # Set options, and exclusive if pyserial supports it - serial_kwargs = {"baudrate": baudrate, "interCharTimeout": 1} + serial_kwargs = { + "baudrate": baudrate, + "timeout": timeout, + "write_timeout": write_timeout, + "interCharTimeout": 1, + } if serial.__version__ >= "3.3": serial_kwargs["exclusive"] = exclusive @@ -304,14 +317,25 @@ def __init__( def close(self): self.serial.close() - def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): - # if data_consumer is used then data is not accumulated and the ending must be 1 byte long + def read_until( + self, min_num_bytes, ending, timeout=10, data_consumer=None, timeout_overall=None + ): + """ + min_num_bytes: Obsolete. + ending: Return if 'ending' matches. + timeout [s]: Return if timeout between characters. None: Infinite timeout. + timeout_overall [s]: Return not later than timeout_overall. None: Infinite timeout. + data_consumer: Use callback for incoming characters. + If data_consumer is used then data is not accumulated and the ending must be 1 byte long + + It is not visible to the caller why the function returned. It could be ending or timeout. + """ assert data_consumer is None or len(ending) == 1 + assert isinstance(timeout, (type(None), int, float)) + assert isinstance(timeout_overall, (type(None), int, float)) - data = self.serial.read(min_num_bytes) - if data_consumer: - data_consumer(data) - timeout_count = 0 + data = b"" + begin_overall_s = begin_char_s = time.monotonic() while True: if data.endswith(ending): break @@ -322,15 +346,25 @@ def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): data = new_data else: data = data + new_data - timeout_count = 0 + begin_char_s = time.monotonic() else: - timeout_count += 1 - if timeout is not None and timeout_count >= 100 * timeout: + if timeout is not None and time.monotonic() >= begin_char_s + timeout: + break + if ( + timeout_overall is not None + and time.monotonic() >= begin_overall_s + timeout_overall + ): break time.sleep(0.01) return data - def enter_raw_repl(self, soft_reset=True): + def enter_raw_repl(self, soft_reset=True, timeout_overall=10): + try: + self._enter_raw_repl_unprotected(soft_reset, timeout_overall) + except OSError as er: + raise PyboardError("could not enter raw repl: {}".format(er)) + + def _enter_raw_repl_unprotected(self, soft_reset, timeout_overall): self.serial.write(b"\r\x03") # ctrl-C: interrupt any running program # flush input (without relying on serial.flushInput()) @@ -342,7 +376,9 @@ def enter_raw_repl(self, soft_reset=True): self.serial.write(b"\r\x01") # ctrl-A: enter raw REPL if soft_reset: - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") + data = self.read_until( + 1, b"raw REPL; CTRL-B to exit\r\n>", timeout_overall=timeout_overall + ) if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): print(data) raise PyboardError("could not enter raw repl") @@ -352,12 +388,12 @@ def enter_raw_repl(self, soft_reset=True): # Waiting for "soft reboot" independently to "raw REPL" (done below) # allows boot.py to print, which will show up after "soft reboot" # and before "raw REPL". - data = self.read_until(1, b"soft reboot\r\n") + data = self.read_until(1, b"soft reboot\r\n", timeout_overall=timeout_overall) if not data.endswith(b"soft reboot\r\n"): print(data) raise PyboardError("could not enter raw repl") - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n") + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n", timeout_overall=timeout_overall) if not data.endswith(b"raw REPL; CTRL-B to exit\r\n"): print(data) raise PyboardError("could not enter raw repl") @@ -475,8 +511,8 @@ def eval(self, expression, parse=False): return ret # In Python3, call as pyboard.exec(), see the setattr call below. - def exec_(self, command, data_consumer=None): - ret, ret_err = self.exec_raw(command, data_consumer=data_consumer) + def exec_(self, command, timeout=10, data_consumer=None): + ret, ret_err = self.exec_raw(command, timeout, data_consumer) if ret_err: raise PyboardError("exception", ret, ret_err) return ret diff --git a/tools/pydfu.py b/tools/pydfu.py index cd7354818cdea..376c697cbd5eb 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -11,8 +11,6 @@ See document UM0391 for a description of the DFuse file. """ -from __future__ import print_function - import argparse import collections import inspect @@ -75,11 +73,7 @@ # USB DFU interface __DFU_INTERFACE = 0 -# Python 3 deprecated getargspec in favour of getfullargspec, but -# Python 2 doesn't have the latter, so detect which one to use -getargspec = getattr(inspect, "getfullargspec", getattr(inspect, "getargspec", None)) - -if "length" in getargspec(usb.util.get_string).args: +if "length" in inspect.getfullargspec(usb.util.get_string).args: # PyUSB 1.0.0.b1 has the length argument def get_string(dev, index): return usb.util.get_string(dev, 255, index) diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py index 67215d5c5d066..dba6ebd6de59a 100755 --- a/tools/verifygitlog.py +++ b/tools/verifygitlog.py @@ -96,20 +96,47 @@ def verify_message_body(raw_body, err): if len(subject_line) >= 73: err.error("Subject line must be 72 or fewer characters: " + subject_line) + # Do additional checks on the prefix of the subject line. + verify_subject_line_prefix(subject_line.split(": ")[0], err) + # Second one divides subject and body. if len(raw_body) > 1 and raw_body[1]: err.error("Second message line must be empty: " + raw_body[1]) # Message body lines. for line in raw_body[2:]: - # Long lines with URLs are exempt from the line length rule. - if len(line) >= 76 and "://" not in line: + # Long lines with URLs or human names are exempt from the line length rule. + if len(line) >= 76 and not ( + "://" in line + or line.startswith("Co-authored-by: ") + or line.startswith("Signed-off-by: ") + ): err.error("Message lines should be 75 or less characters: " + line) if not raw_body[-1].startswith("Signed-off-by: ") or "@" not in raw_body[-1]: err.error('Message must be signed-off. Use "git commit -s".') +def verify_subject_line_prefix(prefix, err): + ext = (".c", ".h", ".cpp", ".js", ".rst", ".md") + + if prefix.startswith((".", "/")): + err.error('Subject prefix cannot begin with "." or "/".') + + if prefix.endswith("/"): + err.error('Subject prefix cannot end with "/".') + + if prefix.startswith("ports/"): + err.error( + 'Subject prefix cannot begin with "ports/", start with the name of the port instead.' + ) + + if prefix.endswith(ext): + err.error( + "Subject prefix cannot end with a file extension, use the main part of the filename without the extension." + ) + + def run(args): verbose("run", *args) From 0208aa96cc7891f43333ab69c8462c6a39590348 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Fri, 27 Mar 2026 16:36:09 -0700 Subject: [PATCH 467/517] add new audiotools.SpeedChanger module for WAV/MP3 speed changing --- ports/raspberrypi/mpconfigport.mk | 1 + py/circuitpy_defns.mk | 5 + shared-bindings/audiotools/SpeedChanger.c | 138 +++++++++++++++++ shared-bindings/audiotools/SpeedChanger.h | 17 +++ shared-bindings/audiotools/__init__.c | 28 ++++ shared-bindings/audiotools/__init__.h | 7 + shared-module/audiotools/SpeedChanger.c | 175 ++++++++++++++++++++++ shared-module/audiotools/SpeedChanger.h | 35 +++++ shared-module/audiotools/__init__.c | 5 + shared-module/audiotools/__init__.h | 7 + 10 files changed, 418 insertions(+) create mode 100644 shared-bindings/audiotools/SpeedChanger.c create mode 100644 shared-bindings/audiotools/SpeedChanger.h create mode 100644 shared-bindings/audiotools/__init__.c create mode 100644 shared-bindings/audiotools/__init__.h create mode 100644 shared-module/audiotools/SpeedChanger.c create mode 100644 shared-module/audiotools/SpeedChanger.h create mode 100644 shared-module/audiotools/__init__.c create mode 100644 shared-module/audiotools/__init__.h diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 8401c5d75453a..b8fc084322d5f 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -11,6 +11,7 @@ CIRCUITPY_FLOPPYIO ?= 1 CIRCUITPY_FRAMEBUFFERIO ?= $(CIRCUITPY_DISPLAYIO) CIRCUITPY_FULL_BUILD ?= 1 CIRCUITPY_AUDIOMP3 ?= 1 +CIRCUITPY_AUDIOTOOLS ?= 1 CIRCUITPY_BITOPS ?= 1 CIRCUITPY_HASHLIB ?= 1 CIRCUITPY_HASHLIB_MBEDTLS ?= 1 diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 886ba96f3e5fa..7e84d528c2613 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -146,6 +146,9 @@ endif ifeq ($(CIRCUITPY_AUDIOMP3),1) SRC_PATTERNS += audiomp3/% endif +ifeq ($(CIRCUITPY_AUDIOTOOLS),1) +SRC_PATTERNS += audiotools/% +endif ifeq ($(CIRCUITPY_AURORA_EPAPER),1) SRC_PATTERNS += aurora_epaper/% endif @@ -688,6 +691,8 @@ SRC_SHARED_MODULE_ALL = \ audiocore/RawSample.c \ audiocore/WaveFile.c \ audiocore/__init__.c \ + audiotools/SpeedChanger.c \ + audiotools/__init__.c \ audiodelays/Echo.c \ audiodelays/Chorus.c \ audiodelays/PitchShift.c \ diff --git a/shared-bindings/audiotools/SpeedChanger.c b/shared-bindings/audiotools/SpeedChanger.c new file mode 100644 index 0000000000000..ac46eb1dbc83e --- /dev/null +++ b/shared-bindings/audiotools/SpeedChanger.c @@ -0,0 +1,138 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/audiotools/SpeedChanger.h" +#include "shared-bindings/audiocore/__init__.h" +#include "shared-bindings/util.h" +#include "shared-module/audiotools/SpeedChanger.h" + +// Convert a Python float to 16.16 fixed-point rate +static uint32_t rate_to_fp(mp_obj_t rate_obj) { + mp_float_t rate = mp_arg_validate_obj_float_range(rate_obj, 0.001, 1000.0, MP_QSTR_rate); + return (uint32_t)(rate * (1 << 16)); +} + +// Convert 16.16 fixed-point rate to Python float +static mp_obj_t fp_to_rate(uint32_t rate_fp) { + return mp_obj_new_float((mp_float_t)rate_fp / (1 << 16)); +} + +//| class SpeedChanger: +//| """Wraps an audio sample to play it back at a different speed. +//| +//| Uses nearest-neighbor resampling with a fixed-point phase accumulator +//| for CPU-efficient variable-speed playback.""" +//| +//| def __init__(self, source: audiosample, rate: float = 1.0) -> None: +//| """Create a SpeedChanger that wraps ``source``. +//| +//| :param audiosample source: The audio source to resample. +//| :param float rate: Playback speed multiplier. 1.0 = normal, 2.0 = double speed, +//| 0.5 = half speed. Must be positive. +//| +//| Playing a wave file at 1.5x speed:: +//| +//| import board +//| import audiocore +//| import audiotools +//| import audioio +//| +//| wav = audiocore.WaveFile("drum.wav") +//| fast = audiotools.SpeedChanger(wav, rate=1.5) +//| audio = audioio.AudioOut(board.A0) +//| audio.play(fast) +//| +//| # Change speed during playback: +//| fast.rate = 2.0 # double speed +//| fast.rate = 0.5 # half speed +//| """ +//| ... +//| +static mp_obj_t audiotools_speedchanger_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_source, ARG_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_source, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rate, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Validate source implements audiosample protocol + mp_obj_t source = args[ARG_source].u_obj; + audiosample_check(source); + + uint32_t rate_fp = 1 << 16; // default 1.0 + if (args[ARG_rate].u_obj != mp_const_none) { + rate_fp = rate_to_fp(args[ARG_rate].u_obj); + } + + audiotools_speedchanger_obj_t *self = mp_obj_malloc(audiotools_speedchanger_obj_t, &audiotools_speedchanger_type); + common_hal_audiotools_speedchanger_construct(self, source, rate_fp); + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the SpeedChanger and releases all memory resources for reuse.""" +//| ... +//| +static mp_obj_t audiotools_speedchanger_deinit(mp_obj_t self_in) { + audiotools_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiotools_speedchanger_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(audiotools_speedchanger_deinit_obj, audiotools_speedchanger_deinit); + +//| rate: float +//| """Playback speed multiplier. Can be changed during playback.""" +//| +static mp_obj_t audiotools_speedchanger_obj_get_rate(mp_obj_t self_in) { + audiotools_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + audiosample_check_for_deinit(&self->base); + return fp_to_rate(common_hal_audiotools_speedchanger_get_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiotools_speedchanger_get_rate_obj, audiotools_speedchanger_obj_get_rate); + +static mp_obj_t audiotools_speedchanger_obj_set_rate(mp_obj_t self_in, mp_obj_t rate_obj) { + audiotools_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + audiosample_check_for_deinit(&self->base); + common_hal_audiotools_speedchanger_set_rate(self, rate_to_fp(rate_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiotools_speedchanger_set_rate_obj, audiotools_speedchanger_obj_set_rate); + +MP_PROPERTY_GETSET(audiotools_speedchanger_rate_obj, + (mp_obj_t)&audiotools_speedchanger_get_rate_obj, + (mp_obj_t)&audiotools_speedchanger_set_rate_obj); + +static const mp_rom_map_elem_t audiotools_speedchanger_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiotools_speedchanger_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_rate), MP_ROM_PTR(&audiotools_speedchanger_rate_obj) }, + AUDIOSAMPLE_FIELDS, +}; +static MP_DEFINE_CONST_DICT(audiotools_speedchanger_locals_dict, audiotools_speedchanger_locals_dict_table); + +static const audiosample_p_t audiotools_speedchanger_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .reset_buffer = (audiosample_reset_buffer_fun)audiotools_speedchanger_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audiotools_speedchanger_get_buffer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + audiotools_speedchanger_type, + MP_QSTR_SpeedChanger, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, audiotools_speedchanger_make_new, + locals_dict, &audiotools_speedchanger_locals_dict, + protocol, &audiotools_speedchanger_proto + ); diff --git a/shared-bindings/audiotools/SpeedChanger.h b/shared-bindings/audiotools/SpeedChanger.h new file mode 100644 index 0000000000000..d31ae6d6bdc9f --- /dev/null +++ b/shared-bindings/audiotools/SpeedChanger.h @@ -0,0 +1,17 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/audiotools/SpeedChanger.h" + +extern const mp_obj_type_t audiotools_speedchanger_type; + +void common_hal_audiotools_speedchanger_construct(audiotools_speedchanger_obj_t *self, + mp_obj_t source, uint32_t rate_fp); +void common_hal_audiotools_speedchanger_deinit(audiotools_speedchanger_obj_t *self); +void common_hal_audiotools_speedchanger_set_rate(audiotools_speedchanger_obj_t *self, uint32_t rate_fp); +uint32_t common_hal_audiotools_speedchanger_get_rate(audiotools_speedchanger_obj_t *self); diff --git a/shared-bindings/audiotools/__init__.c b/shared-bindings/audiotools/__init__.c new file mode 100644 index 0000000000000..3d610aa1dbf88 --- /dev/null +++ b/shared-bindings/audiotools/__init__.c @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/audiotools/SpeedChanger.h" + +//| """Audio processing tools""" + +static const mp_rom_map_elem_t audiotools_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiotools) }, + { MP_ROM_QSTR(MP_QSTR_SpeedChanger), MP_ROM_PTR(&audiotools_speedchanger_type) }, +}; + +static MP_DEFINE_CONST_DICT(audiotools_module_globals, audiotools_module_globals_table); + +const mp_obj_module_t audiotools_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audiotools_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audiotools, audiotools_module); diff --git a/shared-bindings/audiotools/__init__.h b/shared-bindings/audiotools/__init__.h new file mode 100644 index 0000000000000..c4a52e5819d12 --- /dev/null +++ b/shared-bindings/audiotools/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/shared-module/audiotools/SpeedChanger.c b/shared-module/audiotools/SpeedChanger.c new file mode 100644 index 0000000000000..2bf03cf1aa919 --- /dev/null +++ b/shared-module/audiotools/SpeedChanger.c @@ -0,0 +1,175 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/audiotools/SpeedChanger.h" + +#include +#include "py/runtime.h" +#include "py/gc.h" + +#include "shared-module/audiocore/WaveFile.h" +#include "shared-bindings/audiocore/__init__.h" + +#define OUTPUT_BUFFER_FRAMES 128 + +void common_hal_audiotools_speedchanger_construct(audiotools_speedchanger_obj_t *self, + mp_obj_t source, uint32_t rate_fp) { + audiosample_base_t *src_base = audiosample_check(source); + + self->source = source; + self->rate_fp = rate_fp; + self->phase = 0; + self->src_buffer = NULL; + self->src_buffer_length = 0; + self->src_sample_count = 0; + self->source_done = false; + self->source_exhausted = false; + + // Copy format from source + self->base.sample_rate = src_base->sample_rate; + self->base.channel_count = src_base->channel_count; + self->base.bits_per_sample = src_base->bits_per_sample; + self->base.samples_signed = src_base->samples_signed; + self->base.single_buffer = true; + + uint8_t bytes_per_frame = (src_base->bits_per_sample / 8) * src_base->channel_count; + self->output_buffer_length = OUTPUT_BUFFER_FRAMES * bytes_per_frame; + self->base.max_buffer_length = self->output_buffer_length; + + self->output_buffer = m_malloc_without_collect(self->output_buffer_length); + if (self->output_buffer == NULL) { + m_malloc_fail(self->output_buffer_length); + } +} + +void common_hal_audiotools_speedchanger_deinit(audiotools_speedchanger_obj_t *self) { + self->output_buffer = NULL; + self->source = MP_OBJ_NULL; + audiosample_mark_deinit(&self->base); +} + +void common_hal_audiotools_speedchanger_set_rate(audiotools_speedchanger_obj_t *self, uint32_t rate_fp) { + self->rate_fp = rate_fp; +} + +uint32_t common_hal_audiotools_speedchanger_get_rate(audiotools_speedchanger_obj_t *self) { + return self->rate_fp; +} + +// Fetch the next buffer from the source. Returns false if no data available. +static bool fetch_source_buffer(audiotools_speedchanger_obj_t *self) { + if (self->source_exhausted) { + return false; + } + uint8_t *buf = NULL; + uint32_t len = 0; + audioio_get_buffer_result_t result = audiosample_get_buffer(self->source, false, 0, &buf, &len); + if (result == GET_BUFFER_ERROR) { + self->source_exhausted = true; + return false; + } + if (len == 0) { + self->source_exhausted = true; + return false; + } + self->src_buffer = buf; + self->src_buffer_length = len; + uint8_t bytes_per_frame = (self->base.bits_per_sample / 8) * self->base.channel_count; + self->src_sample_count = len / bytes_per_frame; + self->source_done = (result == GET_BUFFER_DONE); + // Reset phase to index within this new buffer + self->phase = 0; + return true; +} + +void audiotools_speedchanger_reset_buffer(audiotools_speedchanger_obj_t *self, + bool single_channel_output, uint8_t channel) { + if (single_channel_output && channel == 1) { + return; + } + audiosample_reset_buffer(self->source, false, 0); + self->phase = 0; + self->src_buffer = NULL; + self->src_buffer_length = 0; + self->src_sample_count = 0; + self->source_done = false; + self->source_exhausted = false; +} + +audioio_get_buffer_result_t audiotools_speedchanger_get_buffer(audiotools_speedchanger_obj_t *self, + bool single_channel_output, uint8_t channel, + uint8_t **buffer, uint32_t *buffer_length) { + + // Ensure we have a source buffer + if (self->src_buffer == NULL) { + if (!fetch_source_buffer(self)) { + *buffer = NULL; + *buffer_length = 0; + return GET_BUFFER_DONE; + } + } + + uint8_t bytes_per_sample = self->base.bits_per_sample / 8; + uint8_t channels = self->base.channel_count; + uint8_t bytes_per_frame = bytes_per_sample * channels; + uint32_t out_frames = 0; + uint32_t max_out_frames = self->output_buffer_length / bytes_per_frame; + + if (bytes_per_sample == 1) { + // 8-bit samples + uint8_t *out = self->output_buffer; + while (out_frames < max_out_frames) { + uint32_t src_index = self->phase >> SPEED_SHIFT; + // Advance to next source buffer if needed + if (src_index >= self->src_sample_count) { + if (self->source_done) { + self->source_exhausted = true; + break; + } + if (!fetch_source_buffer(self)) { + break; + } + src_index = 0; // phase was reset by fetch + } + uint8_t *src = self->src_buffer + src_index * bytes_per_frame; + for (uint8_t c = 0; c < channels; c++) { + *out++ = src[c]; + } + out_frames++; + self->phase += self->rate_fp; + } + } else { + // 16-bit samples + int16_t *out = (int16_t *)self->output_buffer; + while (out_frames < max_out_frames) { + uint32_t src_index = self->phase >> SPEED_SHIFT; + if (src_index >= self->src_sample_count) { + if (self->source_done) { + self->source_exhausted = true; + break; + } + if (!fetch_source_buffer(self)) { + break; + } + src_index = 0; + } + int16_t *src = (int16_t *)(self->src_buffer + src_index * bytes_per_frame); + for (uint8_t c = 0; c < channels; c++) { + *out++ = src[c]; + } + out_frames++; + self->phase += self->rate_fp; + } + } + + *buffer = self->output_buffer; + *buffer_length = out_frames * bytes_per_frame; + + if (out_frames == 0) { + return GET_BUFFER_DONE; + } + return self->source_exhausted ? GET_BUFFER_DONE : GET_BUFFER_MORE_DATA; +} diff --git a/shared-module/audiotools/SpeedChanger.h b/shared-module/audiotools/SpeedChanger.h new file mode 100644 index 0000000000000..05cbec56fdea2 --- /dev/null +++ b/shared-module/audiotools/SpeedChanger.h @@ -0,0 +1,35 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-module/audiocore/__init__.h" + +// Fixed-point 16.16 format +#define SPEED_SHIFT 16 + +typedef struct { + audiosample_base_t base; + mp_obj_t source; + uint8_t *output_buffer; + uint32_t output_buffer_length; // in bytes, allocated size + // Source buffer cache + uint8_t *src_buffer; + uint32_t src_buffer_length; // in bytes + uint32_t src_sample_count; // in frames + // Phase accumulator and rate in 16.16 fixed-point (units: source frames) + uint32_t phase; + uint32_t rate_fp; // 16.16 fixed-point rate + bool source_done; // source returned DONE on last get_buffer + bool source_exhausted; // source DONE and we consumed all of it +} audiotools_speedchanger_obj_t; + +void audiotools_speedchanger_reset_buffer(audiotools_speedchanger_obj_t *self, + bool single_channel_output, uint8_t channel); +audioio_get_buffer_result_t audiotools_speedchanger_get_buffer(audiotools_speedchanger_obj_t *self, + bool single_channel_output, uint8_t channel, + uint8_t **buffer, uint32_t *buffer_length); diff --git a/shared-module/audiotools/__init__.c b/shared-module/audiotools/__init__.c new file mode 100644 index 0000000000000..7c9b271a4b501 --- /dev/null +++ b/shared-module/audiotools/__init__.c @@ -0,0 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT diff --git a/shared-module/audiotools/__init__.h b/shared-module/audiotools/__init__.h new file mode 100644 index 0000000000000..c4a52e5819d12 --- /dev/null +++ b/shared-module/audiotools/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once From 032c129d5188d9cd3eb90ee85a27ab68f73d012b Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Fri, 27 Mar 2026 16:40:56 -0700 Subject: [PATCH 468/517] update_board_info for zephyr --- .../adafruit/feather_nrf52840_zephyr/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml | 1 + .../zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml | 1 + .../zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml | 1 + .../boards/renesas/da14695_dk_usb/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml | 1 + ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml | 1 + .../zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml | 1 + 18 files changed, 18 insertions(+) diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 2c717fc83c169..edfe80d53203f 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 1b82d3ba00e02..2074193d09bb3 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index f995c03c2a26f..fcf372cfa6d3a 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 397d514a0dac5..a65c86ea27290 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index 69a657b8c98be..9a588fbd75a18 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 378cbc07e9a2a..e3f3f4fe6d23d 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 892b3dbbbd447..41dde878d074f 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 718cd0bcfd353..80755cdf421e0 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index 10b0056c81dcc..a2d519ad6116b 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index a3d6acdb79e71..d988846c0c319 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index 3d5fbb08c6543..5e5028e0ac2ee 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index ec96d53072699..4a1186f39bd8e 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 132899d424838..a22f8da260f84 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index bc2a29aea088c..d2a53571d1098 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index aacda400a305e..9a0c56a90238c 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml index a8c03d5a4e8f5..99ad0b01f0a1f 100644 --- a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index b66682c59238b..77173e04378f6 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index cadd8b5ade3a2..c80145f73b21b 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiotools = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio From 7038fe65bec80eeae15069578bc1e9cf58b247b0 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Fri, 27 Mar 2026 17:16:11 -0700 Subject: [PATCH 469/517] fix doc build hopefully --- shared-bindings/audiotools/SpeedChanger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/audiotools/SpeedChanger.c b/shared-bindings/audiotools/SpeedChanger.c index ac46eb1dbc83e..ed7e8121d2dc9 100644 --- a/shared-bindings/audiotools/SpeedChanger.c +++ b/shared-bindings/audiotools/SpeedChanger.c @@ -31,7 +31,7 @@ static mp_obj_t fp_to_rate(uint32_t rate_fp) { //| Uses nearest-neighbor resampling with a fixed-point phase accumulator //| for CPU-efficient variable-speed playback.""" //| -//| def __init__(self, source: audiosample, rate: float = 1.0) -> None: +//| def __init__(self, source: circuitpython_typing.AudioSample, rate: float = 1.0) -> None: //| """Create a SpeedChanger that wraps ``source``. //| //| :param audiosample source: The audio source to resample. From 63460de8cbe700d6af7b6a6690ca96dc4389d3e3 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 29 Mar 2026 20:58:39 -0400 Subject: [PATCH 470/517] second pass over diffs --- ports/unix/Makefile | 17 ++-- py/binary.c | 11 -- py/dynruntime.h | 2 +- py/makeqstrdefs.py | 3 + py/malloc.c | 1 + py/misc.h | 14 +++ py/mpconfig.h | 2 +- py/objtype.c | 2 +- py/py.mk | 1 + py/sequence.c | 7 +- py/vstr.c | 2 +- shared/runtime/gchelper_generic.c | 1 + shared/runtime/interrupt_char.c | 1 + shared/runtime/interrupt_char.h | 1 + shared/runtime/mpirq.c | 160 ------------------------------ shared/runtime/mpirq.h | 83 ---------------- shared/runtime/pyexec.c | 3 +- 17 files changed, 37 insertions(+), 274 deletions(-) delete mode 100644 shared/runtime/mpirq.c delete mode 100644 shared/runtime/mpirq.h diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 8be34feed3c49..fe73ae5278bf8 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -210,6 +210,7 @@ ifeq ($(MICROPY_PY_JNI),1) CFLAGS += -I/usr/lib/jvm/java-7-openjdk-amd64/include -DMICROPY_PY_JNI=1 endif +# CIRCUITPY-CHANGE: CircuitPython-specific files. # source files SRC_C += \ main.c \ @@ -219,15 +220,13 @@ SRC_C += \ input.c \ alloc.c \ fatfs_port.c \ - mpbthciport.c \ - mpbtstackport_common.c \ - mpbtstackport_h4.c \ - mpbtstackport_usb.c \ - mpnimbleport.c \ - modtermios.c \ - modsocket.c \ - modffi.c \ - modjni.c \ + shared-module/os/__init__.c \ + supervisor/shared/settings.c \ + supervisor/stub/filesystem.c \ + supervisor/stub/safe_mode.c \ + supervisor/stub/stack.c \ + supervisor/shared/translate/translate.c \ + $(SRC_MOD) \ $(wildcard $(VARIANT_DIR)/*.c) # CIRCUITPY-CHANGE diff --git a/py/binary.c b/py/binary.c index 59353b918f913..24c3a5d42989c 100644 --- a/py/binary.c +++ b/py/binary.c @@ -287,11 +287,8 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) { #if MICROPY_PY_BUILTINS_FLOAT case 'f': return mp_obj_new_float_from_f(((float *)p)[index]); - // CIRCUITPY-CHANGE: - #if MICROPY_PY_DOUBLE_TYPECODE case 'd': return mp_obj_new_float_from_d(((double *)p)[index]); - #endif #endif // Extension to CPython: array of objects #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES @@ -364,8 +361,6 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * float f; } fpu = {val}; return mp_obj_new_float_from_f(fpu.f); - // CIRCUITPY-CHANGE - #if MICROPY_PY_DOUBLE_TYPECODE } else if (val_type == 'd') { union { uint64_t i; @@ -373,7 +368,6 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * } fpu = {val}; return mp_obj_new_float_from_d(fpu.f); #endif - #endif } else if (is_signed(val_type)) { if ((long long)MP_SMALL_INT_MIN <= val && val <= (long long)MP_SMALL_INT_MAX) { return mp_obj_new_int((mp_int_t)val); @@ -503,12 +497,10 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_ case 'f': ((float *)p)[index] = mp_obj_get_float_to_f(val_in); break; - #if MICROPY_PY_DOUBLE_TYPECODE case 'd': ((double *)p)[index] = mp_obj_get_float_to_d(val_in); break; #endif - #endif #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES // Extension to CPython: array of objects case 'O': @@ -577,12 +569,9 @@ void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_i case 'f': ((float *)p)[index] = (float)val; break; - // CIRCUITPY-CHANGE - #if MICROPY_PY_DOUBLE_TYPECODE case 'd': ((double *)p)[index] = (double)val; break; - #endif #endif // Extension to CPython: array of pointers #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES diff --git a/py/dynruntime.h b/py/dynruntime.h index b41a31f48f7a5..ab155a1387579 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -289,7 +289,7 @@ static inline void *mp_obj_malloc_helper_dyn(size_t num_bytes, const mp_obj_type #define nlr_raise(o) (mp_raise_dyn(o)) #define mp_raise_type_arg(type, arg) (mp_raise_dyn(mp_obj_new_exception_arg1_dyn((type), (arg)))) -// CIRCUITPY-CHANGE: use str +// CIRCUITPY-CHANGE: use mp_raise_msg_str #define mp_raise_msg(type, msg) (mp_fun_table.raise_msg_str((type), (msg))) #define mp_raise_OSError(er) (mp_raise_OSError_dyn(er)) #define mp_raise_NotImplementedError(msg) (mp_raise_msg(&mp_type_NotImplementedError, (msg))) diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index 99ba6e2f9e5a0..8c07899baf843 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -137,6 +137,7 @@ def qstr_unescape(qstr): return qstr +# CIRCUITPY-CHANGE: output_filename as an arg def process_file(f, output_filename=None): # match gcc-like output (# n "file") and msvc-like output (#line n "file") re_line = re.compile(r"^#(?:line)?\s+\d+\s\"([^\"]+)\"") @@ -290,6 +291,7 @@ class Args: args.input_filename = sys.argv[3] # Unused for command=cat args.output_dir = sys.argv[4] args.output_file = None if len(sys.argv) == 5 else sys.argv[5] # Unused for command=split + # CIRCUITPY-CHANGE if args.output_file == "_": args.output_file = None @@ -304,6 +306,7 @@ class Args: if args.command == "split": with io.open(args.input_filename, encoding="utf-8") as infile: + # CIRCUITPY-CHANGE: pass output_file process_file(infile, args.output_file) if args.command == "cat": diff --git a/py/malloc.c b/py/malloc.c index c2a20cb817b85..c11fd5071452f 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include #include #include #include diff --git a/py/misc.h b/py/misc.h index d977ee6a848fa..b1bdb9517a4fa 100644 --- a/py/misc.h +++ b/py/misc.h @@ -35,10 +35,24 @@ #include #include #include +#if __cplusplus // Required on at least one compiler to get ULLONG_MAX +#include +#else +#include +#endif typedef unsigned char byte; typedef unsigned int uint; +#ifndef __has_builtin +#define __has_builtin(x) (0) +#endif +#ifndef __has_feature +// This macro is supported by Clang and gcc>=14 +#define __has_feature(x) (0) +#endif + + /** generic ops *************************************************/ #ifndef MIN diff --git a/py/mpconfig.h b/py/mpconfig.h index 5b09fc97c9230..e970ea3ba7b0e 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2387,7 +2387,7 @@ typedef time_t mp_timestamp_t; #endif // INT_FMT #if !MICROPY_PREVIEW_VERSION_2 -#define MP_NORETURN MP_NORETURN +#define NORETURN MP_NORETURN #endif // Modifier for weak functions diff --git a/py/objtype.c b/py/objtype.c index 83bafa14e8839..b1a984b50e057 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -1086,7 +1086,7 @@ static mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp if (!MP_OBJ_TYPE_HAS_SLOT(self, make_new)) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE - mp_raise_TypeError(MP_ERROR_TEXT("cannot create instance")); + mp_raise_TypeError(MP_ERROR_TEXT("can't create instance")); #else // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg(MP_ERROR_TEXT("can't create '%q' instances"), self->name); diff --git a/py/py.mk b/py/py.mk index 71b44f84d65e2..21be07c794fb1 100644 --- a/py/py.mk +++ b/py/py.mk @@ -191,6 +191,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ objnamedtuple.o \ objrange.o \ objreversed.o \ + objringio.o \ objset.o \ objsingleton.o \ objslice.o \ diff --git a/py/sequence.c b/py/sequence.c index ac7ad5368b91e..490f6d9c67253 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -34,15 +34,10 @@ #define SWAP(type, var1, var2) { type t = var2; var2 = var1; var1 = t; } // CIRCUITPY-CHANGE: detect sequence overflow -#if __GNUC__ < 5 -// n.b. does not actually detect overflow! -#define __builtin_mul_overflow(a, b, x) (*(x) = (a) * (b), false) -#endif - // Detect when a multiply causes an overflow. size_t mp_seq_multiply_len(size_t item_sz, size_t len) { size_t new_len; - if (__builtin_mul_overflow(item_sz, len, &new_len)) { + if (mp_mul_mp_int_t_overflow(item_sz, len, &new_len)) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); } return new_len; diff --git a/py/vstr.c b/py/vstr.c index 00972edf89796..522509d0d05cf 100644 --- a/py/vstr.c +++ b/py/vstr.c @@ -51,7 +51,7 @@ void vstr_init(vstr_t *vstr, size_t alloc) { // Init the vstr so it allocs exactly enough ram to hold a null-terminated // string of the given length, and set the length. void vstr_init_len(vstr_t *vstr, size_t len) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: check for invalid length if (len == SIZE_MAX) { m_malloc_fail(len); } diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index 464aeaa9981de..230a24440050f 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -42,6 +42,7 @@ // stack already by the caller. #if defined(__x86_64__) +// CIRCUITPY-CHANGE: use __asm__ instead of asm static void gc_helper_get_regs(gc_helper_regs_t arr) { register long rbx __asm__ ("rbx"); register long rbp __asm__ ("rbp"); diff --git a/shared/runtime/interrupt_char.c b/shared/runtime/interrupt_char.c index 5cec1988f41b3..1f2702017190f 100644 --- a/shared/runtime/interrupt_char.c +++ b/shared/runtime/interrupt_char.c @@ -31,6 +31,7 @@ #if MICROPY_KBD_EXCEPTION +// CIRCUITPY-CHANGE #ifdef __ZEPHYR__ #include diff --git a/shared/runtime/interrupt_char.h b/shared/runtime/interrupt_char.h index c4a465456a8d1..44fd4b45a6121 100644 --- a/shared/runtime/interrupt_char.h +++ b/shared/runtime/interrupt_char.h @@ -29,6 +29,7 @@ // CIRCUITPY-CHANGE #include +// CIRCUITPY-CHANGE #ifdef __ZEPHYR__ #include diff --git a/shared/runtime/mpirq.c b/shared/runtime/mpirq.c deleted file mode 100644 index 4d848ae7e9102..0000000000000 --- a/shared/runtime/mpirq.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2015 Daniel Campora - * 2018 Tobias Badertscher - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "py/runtime.h" -#include "py/gc.h" -#include "shared/runtime/mpirq.h" - -#if MICROPY_ENABLE_SCHEDULER - -/****************************************************************************** - DECLARE PUBLIC DATA - ******************************************************************************/ - -const mp_arg_t mp_irq_init_args[] = { - { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_trigger, MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, -}; - -/****************************************************************************** - DECLARE PRIVATE DATA - ******************************************************************************/ - -/****************************************************************************** - DEFINE PUBLIC FUNCTIONS - ******************************************************************************/ - -mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent) { - mp_irq_obj_t *self = m_new0(mp_irq_obj_t, 1); - mp_irq_init(self, methods, parent); - return self; -} - -void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent) { - self->base.type = &mp_irq_type; - self->methods = (mp_irq_methods_t *)methods; - self->parent = parent; - self->handler = mp_const_none; - self->ishard = false; -} - -int mp_irq_dispatch(mp_obj_t handler, mp_obj_t parent, bool ishard) { - int result = 0; - if (handler != mp_const_none) { - if (ishard) { - #if MICROPY_STACK_CHECK && MICROPY_STACK_SIZE_HARD_IRQ > 0 - // This callback executes in an ISR context so the stack-limit - // check must be changed to use the ISR stack for the duration - // of this function. - char *orig_stack_top = MP_STATE_THREAD(stack_top); - size_t orig_stack_limit = MP_STATE_THREAD(stack_limit); - mp_cstack_init_with_sp_here(MICROPY_STACK_SIZE_HARD_IRQ); - #endif - - // When executing code within a handler we must lock the scheduler to - // prevent any scheduled callbacks from running, and lock the GC to - // prevent any memory allocations. - mp_sched_lock(); - gc_lock(); - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_call_function_1(handler, parent); - nlr_pop(); - } else { - mp_printf(MICROPY_ERROR_PRINTER, "Uncaught exception in IRQ callback handler\n"); - mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); - result = -1; - } - gc_unlock(); - mp_sched_unlock(); - - #if MICROPY_STACK_CHECK && MICROPY_STACK_SIZE_HARD_IRQ > 0 - // Restore original stack-limit checking values. - MP_STATE_THREAD(stack_top) = orig_stack_top; - MP_STATE_THREAD(stack_limit) = orig_stack_limit; - #endif - } else { - // Schedule call to user function - mp_sched_schedule(handler, parent); - } - } - return result; -} - - -void mp_irq_handler(mp_irq_obj_t *self) { - if (mp_irq_dispatch(self->handler, self->parent, self->ishard) < 0) { - // Uncaught exception; disable the callback so that it doesn't run again - self->methods->trigger(self->parent, 0); - self->handler = mp_const_none; - } -} - -/******************************************************************************/ -// MicroPython bindings - -static mp_obj_t mp_irq_flags(mp_obj_t self_in) { - mp_irq_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_FLAGS)); -} -static MP_DEFINE_CONST_FUN_OBJ_1(mp_irq_flags_obj, mp_irq_flags); - -static mp_obj_t mp_irq_trigger(size_t n_args, const mp_obj_t *args) { - mp_irq_obj_t *self = MP_OBJ_TO_PTR(args[0]); - mp_obj_t ret_obj = mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_TRIGGERS)); - if (n_args == 2) { - // Set trigger - self->methods->trigger(self->parent, mp_obj_get_int(args[1])); - } - return ret_obj; -} -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_irq_trigger_obj, 1, 2, mp_irq_trigger); - -static mp_obj_t mp_irq_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 0, 0, false); - mp_irq_handler(MP_OBJ_TO_PTR(self_in)); - return mp_const_none; -} - -static const mp_rom_map_elem_t mp_irq_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_flags), MP_ROM_PTR(&mp_irq_flags_obj) }, - { MP_ROM_QSTR(MP_QSTR_trigger), MP_ROM_PTR(&mp_irq_trigger_obj) }, -}; -static MP_DEFINE_CONST_DICT(mp_irq_locals_dict, mp_irq_locals_dict_table); - -MP_DEFINE_CONST_OBJ_TYPE( - mp_irq_type, - MP_QSTR_irq, - MP_TYPE_FLAG_NONE, - call, mp_irq_call, - locals_dict, &mp_irq_locals_dict - ); - -#endif // MICROPY_ENABLE_SCHEDULER diff --git a/shared/runtime/mpirq.h b/shared/runtime/mpirq.h deleted file mode 100644 index c65741e0e49d1..0000000000000 --- a/shared/runtime/mpirq.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2015 Daniel Campora - * - * 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_LIB_UTILS_MPIRQ_H -#define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H - -#include "py/runtime.h" - -/****************************************************************************** - DEFINE CONSTANTS - ******************************************************************************/ - -enum { - MP_IRQ_ARG_INIT_handler = 0, - MP_IRQ_ARG_INIT_trigger, - MP_IRQ_ARG_INIT_hard, - MP_IRQ_ARG_INIT_NUM_ARGS, -}; - -/****************************************************************************** - DEFINE TYPES - ******************************************************************************/ - -typedef mp_uint_t (*mp_irq_trigger_fun_t)(mp_obj_t self, mp_uint_t trigger); -typedef mp_uint_t (*mp_irq_info_fun_t)(mp_obj_t self, mp_uint_t info_type); - -enum { - MP_IRQ_INFO_FLAGS, - MP_IRQ_INFO_TRIGGERS, -}; - -typedef struct _mp_irq_methods_t { - mp_irq_trigger_fun_t trigger; - mp_irq_info_fun_t info; -} mp_irq_methods_t; - -typedef struct _mp_irq_obj_t { - mp_obj_base_t base; - mp_irq_methods_t *methods; - mp_obj_t parent; - mp_obj_t handler; - bool ishard; -} mp_irq_obj_t; - -/****************************************************************************** - DECLARE EXPORTED DATA - ******************************************************************************/ - -extern const mp_arg_t mp_irq_init_args[]; -extern const mp_obj_type_t mp_irq_type; - -/****************************************************************************** - DECLARE PUBLIC FUNCTIONS - ******************************************************************************/ - -mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent); -void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent); -int mp_irq_dispatch(mp_obj_t handler, mp_obj_t parent, bool ishard); -void mp_irq_handler(mp_irq_obj_t *self); - -#endif // MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index a44fdf724d1f9..b554199eeec6b 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -482,7 +482,8 @@ static int pyexec_friendly_repl_process_char(int c) { mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); mp_hal_stdout_tx_str("\r\n"); #if MICROPY_PY_BUILTINS_HELP - mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); + // CIRCUITPY-CHANGE: don't print help info + // mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); #endif goto input_restart; } else if (ret == CHAR_CTRL_C) { From e9d1082b0a185a4e583c4105fe4961b70a6c7670 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Mon, 30 Mar 2026 15:15:59 +0200 Subject: [PATCH 471/517] Fix length validation for palette in the stage library bindings Right now it's impossible to use the Stage library, because it always throws a validation error. --- shared-bindings/_stage/Text.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared-bindings/_stage/Text.c b/shared-bindings/_stage/Text.c index f64a1b381901c..0013ffb669797 100644 --- a/shared-bindings/_stage/Text.c +++ b/shared-bindings/_stage/Text.c @@ -47,7 +47,7 @@ static mp_obj_t text_make_new(const mp_obj_type_t *type, size_t n_args, mp_buffer_info_t palette_bufinfo; mp_get_buffer_raise(args[3], &palette_bufinfo, MP_BUFFER_READ); - mp_arg_validate_length(font_bufinfo.len, 32, MP_QSTR_palette); + mp_arg_validate_length(palette_bufinfo.len, 32, MP_QSTR_palette); mp_buffer_info_t chars_bufinfo; mp_get_buffer_raise(args[4], &chars_bufinfo, MP_BUFFER_READ); From a6291470dbad9b1402abc2f99df01111607c426a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 30 Mar 2026 16:35:49 -0400 Subject: [PATCH 472/517] fix warnings during RTD builds --- docs/rstjinja.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/rstjinja.py b/docs/rstjinja.py index e7d8a312f1f9d..04c855a1a4049 100644 --- a/docs/rstjinja.py +++ b/docs/rstjinja.py @@ -38,3 +38,7 @@ def rstjinja(app, docname, source): def setup(app): app.connect("source-read", rstjinja) + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } From c2cf8306b82900256297b433942e2fd3d3ce0791 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Mon, 30 Mar 2026 22:59:16 +0200 Subject: [PATCH 473/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 7 ++++++- locale/el.po | 7 ++++++- locale/hi.po | 7 ++++++- locale/ko.po | 7 ++++++- locale/ru.po | 7 ++++++- locale/tr.po | 7 ++++++- 6 files changed, 36 insertions(+), 6 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index d23583f4e6b3e..0adef977617cd 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -636,6 +636,7 @@ msgstr "Konverze audia není implementována" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -1320,6 +1321,7 @@ msgstr "" msgid "Invalid %q" msgstr "Špatný %s" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1557,6 +1559,7 @@ msgstr "Žádný DAC na čipu" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "Nebyl nalezen žádný kanál DMA" @@ -1671,7 +1674,7 @@ msgid "Not connected" msgstr "Nepřipojený" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "Nehraje" @@ -2191,6 +2194,7 @@ msgid "Too many channels in sample" msgstr "V samplu je příliš mnoho kanálů" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2290,6 +2294,7 @@ msgstr "Nelze přistupovat k nezarovnanému IO registru" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" diff --git a/locale/el.po b/locale/el.po index 06a24a1486f9e..df024f2b1fe71 100644 --- a/locale/el.po +++ b/locale/el.po @@ -640,6 +640,7 @@ msgstr "Η μετατροπή ήχου δεν υποστηρίζεται" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -1326,6 +1327,7 @@ msgstr "" msgid "Invalid %q" msgstr "" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1563,6 +1565,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "" @@ -1677,7 +1680,7 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "" @@ -2196,6 +2199,7 @@ msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2294,6 +2298,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" diff --git a/locale/hi.po b/locale/hi.po index a6d5cf49c0a06..48a8c6c26e16f 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -627,6 +627,7 @@ msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -1302,6 +1303,7 @@ msgstr "" msgid "Invalid %q" msgstr "" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1539,6 +1541,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "" @@ -1653,7 +1656,7 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "" @@ -2170,6 +2173,7 @@ msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2268,6 +2272,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" diff --git a/locale/ko.po b/locale/ko.po index cc65afaa7c060..63c64a6952d69 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -666,6 +666,7 @@ msgstr "오디오 변환이 구현되지 않음" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -1353,6 +1354,7 @@ msgstr "출력 함수로 인해 종료되었다" msgid "Invalid %q" msgstr "잘못된 %q" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1594,6 +1596,7 @@ msgstr "칩에 DAC가 없습니다" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "DMA 채널을 찾을 수 없습니다" @@ -1711,7 +1714,7 @@ msgid "Not connected" msgstr "연결되지 않았습니다" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "재생되지 않았습니다" @@ -2243,6 +2246,7 @@ msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2342,6 +2346,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" diff --git a/locale/ru.po b/locale/ru.po index 7f31c3a73a663..a7f7a35d58b7a 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -640,6 +640,7 @@ msgstr "Преобразование звука не реализовано" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -1341,6 +1342,7 @@ msgstr "Прерывается функцией выхода" msgid "Invalid %q" msgstr "Недопустимый %q" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1580,6 +1582,7 @@ msgstr "DAC отсутствует на чипе" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "Канал DMA не найден" @@ -1694,7 +1697,7 @@ msgid "Not connected" msgstr "Не подключено" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "Не воспроизводится (Not playing)" @@ -2226,6 +2229,7 @@ msgid "Too many channels in sample" msgstr "Слишком много каналов в выборке" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "Слишком много каналов в выборке." @@ -2324,6 +2328,7 @@ msgstr "Невозможно получить доступ к невыровне #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "Не удается выделить буферы для подписанного преобразования" diff --git a/locale/tr.po b/locale/tr.po index 96037c5426fd2..00f1c82065ac5 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -638,6 +638,7 @@ msgstr "Ses dönüşümü implemente edilmedi" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -1320,6 +1321,7 @@ msgstr "" msgid "Invalid %q" msgstr "Geçersiz %q" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1558,6 +1560,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "" @@ -1672,7 +1675,7 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "" @@ -2192,6 +2195,7 @@ msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2290,6 +2294,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" From b4e21d108c687f1f23eb46499592f2e6257189db Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 30 Mar 2026 14:33:35 -0700 Subject: [PATCH 474/517] Relax Zephyr CI autogen check It is more annoying than helpful. We can set up an auto-updater later on when they get out of date. --- ports/zephyr-cp/cptools/build_circuitpython.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 2022d82f1b73f..5f51281870c4d 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -522,16 +522,15 @@ async def build_circuitpython(): hal_source.extend(top.glob(f"shared-bindings/{module.name}/*.c")) if os.environ.get("CI", "false") == "true": - # Fail the build if it isn't up to date. + # Warn if it isn't up to date. if ( not autogen_board_info_fn.exists() or autogen_board_info_fn.read_text() != tomlkit.dumps(autogen_board_info) ): - logger.error("autogen_board_info.toml is out of date.") - raise RuntimeError( + logger.warning( f"autogen_board_info.toml is missing or out of date. Please run `make BOARD={board}` locally and commit {autogen_board_info_fn}." ) - elif autogen_board_info_fn.parent.exists(): + if autogen_board_info_fn.parent.exists(): autogen_board_info_fn.write_text(tomlkit.dumps(autogen_board_info)) for mpflag in MPCONFIG_FLAGS: From 81dfb880f789fb47292de771ab621b3b15764bd6 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 30 Mar 2026 17:39:26 -0400 Subject: [PATCH 475/517] update CI actions to latest versions, to use Node.24 --- .../actions/deps/ports/espressif/action.yml | 4 +-- .github/actions/deps/python/action.yml | 4 +-- .github/actions/deps/submodules/action.yml | 4 +-- .github/actions/mpy_cross/action.yml | 4 +-- .github/workflows/build-board-custom.yml | 4 +-- .github/workflows/build-boards.yml | 6 ++--- .github/workflows/build-mpy-cross.yml | 6 ++--- .github/workflows/build.yml | 26 +++++++++---------- .github/workflows/bundle_cron.yml | 6 ++--- .github/workflows/create-website-pr.yml | 4 +-- .github/workflows/learn_cron.yml | 2 +- .github/workflows/pre-commit.yml | 6 ++--- .github/workflows/reports_cron.yml | 4 +-- .github/workflows/run-tests.yml | 8 +++--- 14 files changed, 44 insertions(+), 44 deletions(-) diff --git a/.github/actions/deps/ports/espressif/action.yml b/.github/actions/deps/ports/espressif/action.yml index 25965eb7ef040..321a0fb2b3023 100644 --- a/.github/actions/deps/ports/espressif/action.yml +++ b/.github/actions/deps/ports/espressif/action.yml @@ -19,7 +19,7 @@ runs: shell: bash - name: Cache IDF submodules - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | .git/modules/ports/espressif/esp-idf @@ -27,7 +27,7 @@ runs: key: submodules-idf-${{ steps.idf-commit.outputs.commit }} - name: Cache IDF tools - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.IDF_TOOLS_PATH }} key: ${{ runner.os }}-${{ env.pythonLocation }}-tools-idf-${{ steps.idf-commit.outputs.commit }} diff --git a/.github/actions/deps/python/action.yml b/.github/actions/deps/python/action.yml index da59b87b17a29..bc8b578c1473d 100644 --- a/.github/actions/deps/python/action.yml +++ b/.github/actions/deps/python/action.yml @@ -16,7 +16,7 @@ runs: - name: Cache python dependencies id: cache-python-deps if: inputs.action == 'cache' - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: .cp_tools key: ${{ runner.os }}-${{ env.pythonLocation }}-tools-cp-${{ hashFiles('requirements-dev.txt') }} @@ -24,7 +24,7 @@ runs: - name: Restore python dependencies id: restore-python-deps if: inputs.action == 'restore' - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: .cp_tools key: ${{ runner.os }}-${{ env.pythonLocation }}-tools-cp-${{ hashFiles('requirements-dev.txt') }} diff --git a/.github/actions/deps/submodules/action.yml b/.github/actions/deps/submodules/action.yml index eed83af41f4eb..5ec57b594c0da 100644 --- a/.github/actions/deps/submodules/action.yml +++ b/.github/actions/deps/submodules/action.yml @@ -48,7 +48,7 @@ runs: - name: Cache submodules if: ${{ inputs.action == 'cache' }} - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ".git/modules/\n${{ join(fromJSON(steps.create-submodule-status.outputs.submodules), '\n') }}" key: submodules-common-${{ hashFiles('submodule_status') }} @@ -56,7 +56,7 @@ runs: - name: Restore submodules if: ${{ inputs.action == 'restore' }} - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: ".git/modules/\n${{ join(fromJSON(steps.create-submodule-status.outputs.submodules), '\n') }}" key: submodules-common-${{ hashFiles('submodule_status') }} diff --git a/.github/actions/mpy_cross/action.yml b/.github/actions/mpy_cross/action.yml index 8839f790915cf..469b8b0763e95 100644 --- a/.github/actions/mpy_cross/action.yml +++ b/.github/actions/mpy_cross/action.yml @@ -16,7 +16,7 @@ runs: id: download-mpy-cross if: inputs.download == 'true' continue-on-error: true - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: mpy-cross path: mpy-cross/build @@ -36,7 +36,7 @@ runs: - name: Upload mpy-cross if: inputs.download == 'false' || steps.download-mpy-cross.outcome == 'failure' continue-on-error: true - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: mpy-cross path: mpy-cross/build/mpy-cross diff --git a/.github/workflows/build-board-custom.yml b/.github/workflows/build-board-custom.yml index bf18d7d725956..c17e1b13f9791 100644 --- a/.github/workflows/build-board-custom.yml +++ b/.github/workflows/build-board-custom.yml @@ -70,7 +70,7 @@ jobs: run: | > custom-build && git add custom-build - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Board to port @@ -124,7 +124,7 @@ jobs: run: make -j4 $FLAGS BOARD="$BOARD" DEBUG=$DEBUG TRANSLATION="$TRANSLATION" working-directory: ports/${{ steps.board-to-port.outputs.port }} - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{ inputs.board }}-${{ inputs.language }}-${{ inputs.version }}${{ inputs.flags != '' && '-custom' || '' }}${{ inputs.debug && '-debug' || '' }} path: ports/${{ steps.board-to-port.outputs.port }}/build-${{ inputs.board }}/firmware.* diff --git a/.github/workflows/build-boards.yml b/.github/workflows/build-boards.yml index 7e5156d40114a..6fbc5b6a08f15 100644 --- a/.github/workflows/build-boards.yml +++ b/.github/workflows/build-boards.yml @@ -29,7 +29,7 @@ jobs: board: ${{ fromJSON(inputs.boards) }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false @@ -37,7 +37,7 @@ jobs: persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x @@ -87,7 +87,7 @@ jobs: HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.board }} path: bin/${{ matrix.board }} diff --git a/.github/workflows/build-mpy-cross.yml b/.github/workflows/build-mpy-cross.yml index 831ad3082275b..9e5c1cdfc4a8d 100644 --- a/.github/workflows/build-mpy-cross.yml +++ b/.github/workflows/build-mpy-cross.yml @@ -28,14 +28,14 @@ jobs: OS_static-raspbian: linux-raspbian steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules @@ -66,7 +66,7 @@ jobs: echo >> $GITHUB_ENV "OS=$OS" - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: mpy-cross.${{ env.EX }} path: mpy-cross/build-${{ matrix.mpy-cross }}/mpy-cross.${{ env.EX }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b755eb398ea6..6a2329a411340 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,14 +28,14 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Duplicate USB VID/PID check @@ -114,14 +114,14 @@ jobs: CP_VERSION: ${{ needs.scheduler.outputs.cp-version }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules @@ -133,7 +133,7 @@ jobs: msgfmt --version - name: Build mpy-cross (arm64) run: make -C mpy-cross -j4 -f Makefile.m1 V=2 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: mpy-cross-macos-arm64 path: mpy-cross/build-arm64/mpy-cross-arm64 @@ -156,14 +156,14 @@ jobs: CP_VERSION: ${{ needs.scheduler.outputs.cp-version }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules @@ -177,20 +177,20 @@ jobs: pip install -r requirements-doc.txt - name: Build and Validate Stubs run: make check-stubs -j4 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: stubs path: circuitpython-stubs/dist/* - name: Test Documentation Build (HTML) run: sphinx-build -E -W -b html -D version="$CP_VERSION" -D release="$CP_VERSION" . _build/html - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: docs-html path: _build/html - name: Test Documentation Build (LaTeX/PDF) run: | make latexpdf - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: docs-latexpdf path: _build/latex @@ -260,7 +260,7 @@ jobs: which python; python --version; python -c "import cascadetoml" which python3; python3 --version; python3 -c "import cascadetoml" - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false @@ -295,13 +295,13 @@ jobs: CP_VERSION: ${{ needs.scheduler.outputs.cp-version }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.13' - name: Set up Zephyr diff --git a/.github/workflows/bundle_cron.yml b/.github/workflows/bundle_cron.yml index 606707d4102f1..eefffaaa5b9ab 100644 --- a/.github/workflows/bundle_cron.yml +++ b/.github/workflows/bundle_cron.yml @@ -29,18 +29,18 @@ jobs: if: startswith(github.repository, 'adafruit/') steps: - name: Set up Python 3.12 - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.12 - name: Load contributor cache - uses: actions/cache@v4 + uses: actions/cache@v5 with: key: "contributor-cache" path: "contributors.json" - name: Versions run: | python3 --version - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: repository: 'adafruit/adabot' submodules: true diff --git a/.github/workflows/create-website-pr.yml b/.github/workflows/create-website-pr.yml index 32c1792fa6c74..559a41e67e793 100644 --- a/.github/workflows/create-website-pr.yml +++ b/.github/workflows/create-website-pr.yml @@ -17,14 +17,14 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules diff --git a/.github/workflows/learn_cron.yml b/.github/workflows/learn_cron.yml index 6100e6637c20e..135089bfd5ef2 100644 --- a/.github/workflows/learn_cron.yml +++ b/.github/workflows/learn_cron.yml @@ -26,7 +26,7 @@ jobs: # default branches). if: ${{ (github.repository_owner == 'adafruit') }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: repository: ${{ github.repository_owner }}/Adafruit_Learning_System_Guides token: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 778270dc08c8f..21ae984e46870 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -17,14 +17,14 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules @@ -42,7 +42,7 @@ jobs: run: git diff > ~/pre-commit.patch - name: Upload patch if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: patch path: ~/pre-commit.patch diff --git a/.github/workflows/reports_cron.yml b/.github/workflows/reports_cron.yml index b4e9a43024e27..476ead95d256a 100644 --- a/.github/workflows/reports_cron.yml +++ b/.github/workflows/reports_cron.yml @@ -38,13 +38,13 @@ jobs: BIGQUERY_CLIENT_EMAIL: ${{ secrets.BIGQUERY_CLIENT_EMAIL }} steps: - name: Set up Python 3.11 - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.11 - name: Versions run: | python3 --version - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: repository: 'adafruit/adabot' submodules: true diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 925ee4698542c..83154bbbd2b66 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,13 +24,13 @@ jobs: TEST_native_mpy: --via-mpy --emit native -d basics float micropython steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.12 - name: Set up submodules @@ -75,13 +75,13 @@ jobs: CP_VERSION: ${{ inputs.cp-version }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.13 - name: Set up Zephyr From 9ce0c5d6010feb3c869892065ffb65d1dee01095 Mon Sep 17 00:00:00 2001 From: Tod Kurt Date: Mon, 30 Mar 2026 16:48:19 -0700 Subject: [PATCH 476/517] rename audiotools to audiospeed --- ports/raspberrypi/mpconfigport.mk | 2 +- .../autogen_board_info.toml | 2 +- .../native/native_sim/autogen_board_info.toml | 2 +- .../nrf5340bsim/autogen_board_info.toml | 2 +- .../nordic/nrf5340dk/autogen_board_info.toml | 2 +- .../nordic/nrf54h20dk/autogen_board_info.toml | 2 +- .../nordic/nrf54l15dk/autogen_board_info.toml | 2 +- .../nordic/nrf7002dk/autogen_board_info.toml | 2 +- .../nxp/frdm_mcxn947/autogen_board_info.toml | 2 +- .../nxp/frdm_rw612/autogen_board_info.toml | 2 +- .../mimxrt1170_evk/autogen_board_info.toml | 2 +- .../da14695_dk_usb/autogen_board_info.toml | 2 +- .../renesas/ek_ra6m5/autogen_board_info.toml | 2 +- .../renesas/ek_ra8d1/autogen_board_info.toml | 2 +- .../nucleo_n657x0_q/autogen_board_info.toml | 2 +- .../nucleo_u575zi_q/autogen_board_info.toml | 2 +- .../st/stm32h750b_dk/autogen_board_info.toml | 2 +- .../st/stm32h7b3i_dk/autogen_board_info.toml | 2 +- .../stm32wba65i_dk1/autogen_board_info.toml | 2 +- py/circuitpy_defns.mk | 8 +-- .../{audiotools => audiospeed}/SpeedChanger.c | 66 +++++++++---------- shared-bindings/audiospeed/SpeedChanger.h | 17 +++++ shared-bindings/audiospeed/__init__.c | 28 ++++++++ .../{audiotools => audiospeed}/__init__.h | 0 shared-bindings/audiotools/SpeedChanger.h | 17 ----- shared-bindings/audiotools/__init__.c | 28 -------- .../{audiotools => audiospeed}/SpeedChanger.c | 16 ++--- .../{audiotools => audiospeed}/SpeedChanger.h | 6 +- .../{audiotools => audiospeed}/__init__.c | 0 .../{audiotools => audiospeed}/__init__.h | 0 30 files changed, 112 insertions(+), 112 deletions(-) rename shared-bindings/{audiotools => audiospeed}/SpeedChanger.c (62%) create mode 100644 shared-bindings/audiospeed/SpeedChanger.h create mode 100644 shared-bindings/audiospeed/__init__.c rename shared-bindings/{audiotools => audiospeed}/__init__.h (100%) delete mode 100644 shared-bindings/audiotools/SpeedChanger.h delete mode 100644 shared-bindings/audiotools/__init__.c rename shared-module/{audiotools => audiospeed}/SpeedChanger.c (90%) rename shared-module/{audiotools => audiospeed}/SpeedChanger.h (84%) rename shared-module/{audiotools => audiospeed}/__init__.c (100%) rename shared-module/{audiotools => audiospeed}/__init__.h (100%) diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index b8fc084322d5f..c670f5aa5dfc0 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -11,7 +11,7 @@ CIRCUITPY_FLOPPYIO ?= 1 CIRCUITPY_FRAMEBUFFERIO ?= $(CIRCUITPY_DISPLAYIO) CIRCUITPY_FULL_BUILD ?= 1 CIRCUITPY_AUDIOMP3 ?= 1 -CIRCUITPY_AUDIOTOOLS ?= 1 +CIRCUITPY_AUDIOSPEED ?= 1 CIRCUITPY_BITOPS ?= 1 CIRCUITPY_HASHLIB ?= 1 CIRCUITPY_HASHLIB_MBEDTLS ?= 1 diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index edfe80d53203f..df33d7a7eb2e5 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 2074193d09bb3..7b7295a90453c 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index fcf372cfa6d3a..75bfafe96d0ab 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index a65c86ea27290..a6d27f14eda39 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index 9a588fbd75a18..ec76cd7c1abdb 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index e3f3f4fe6d23d..aca4d701cf785 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 41dde878d074f..5366f53561262 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 80755cdf421e0..14aef6dfdf7cf 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index a2d519ad6116b..a76ec250ec710 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index d988846c0c319..f8acb43678080 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index 5e5028e0ac2ee..11a693117eccc 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 4a1186f39bd8e..00260356a8e9c 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index a22f8da260f84..1742c507c3db8 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index d2a53571d1098..98ab65e270e59 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 9a0c56a90238c..a40eaac3b9d68 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml index 99ad0b01f0a1f..596391b40ccf5 100644 --- a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index 77173e04378f6..adabe5cceecf9 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index c80145f73b21b..07e9913a41a3a 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -24,7 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false -audiotools = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 7e84d528c2613..f4245b01322bc 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -146,8 +146,8 @@ endif ifeq ($(CIRCUITPY_AUDIOMP3),1) SRC_PATTERNS += audiomp3/% endif -ifeq ($(CIRCUITPY_AUDIOTOOLS),1) -SRC_PATTERNS += audiotools/% +ifeq ($(CIRCUITPY_AUDIOSPEED),1) +SRC_PATTERNS += audiospeed/% endif ifeq ($(CIRCUITPY_AURORA_EPAPER),1) SRC_PATTERNS += aurora_epaper/% @@ -691,8 +691,8 @@ SRC_SHARED_MODULE_ALL = \ audiocore/RawSample.c \ audiocore/WaveFile.c \ audiocore/__init__.c \ - audiotools/SpeedChanger.c \ - audiotools/__init__.c \ + audiospeed/SpeedChanger.c \ + audiospeed/__init__.c \ audiodelays/Echo.c \ audiodelays/Chorus.c \ audiodelays/PitchShift.c \ diff --git a/shared-bindings/audiotools/SpeedChanger.c b/shared-bindings/audiospeed/SpeedChanger.c similarity index 62% rename from shared-bindings/audiotools/SpeedChanger.c rename to shared-bindings/audiospeed/SpeedChanger.c index ed7e8121d2dc9..34efa1516b035 100644 --- a/shared-bindings/audiotools/SpeedChanger.c +++ b/shared-bindings/audiospeed/SpeedChanger.c @@ -9,10 +9,10 @@ #include "shared/runtime/context_manager_helpers.h" #include "py/objproperty.h" #include "py/runtime.h" -#include "shared-bindings/audiotools/SpeedChanger.h" +#include "shared-bindings/audiospeed/SpeedChanger.h" #include "shared-bindings/audiocore/__init__.h" #include "shared-bindings/util.h" -#include "shared-module/audiotools/SpeedChanger.h" +#include "shared-module/audiospeed/SpeedChanger.h" // Convert a Python float to 16.16 fixed-point rate static uint32_t rate_to_fp(mp_obj_t rate_obj) { @@ -42,11 +42,11 @@ static mp_obj_t fp_to_rate(uint32_t rate_fp) { //| //| import board //| import audiocore -//| import audiotools +//| import audiospeed //| import audioio //| //| wav = audiocore.WaveFile("drum.wav") -//| fast = audiotools.SpeedChanger(wav, rate=1.5) +//| fast = audiospeed.SpeedChanger(wav, rate=1.5) //| audio = audioio.AudioOut(board.A0) //| audio.play(fast) //| @@ -56,7 +56,7 @@ static mp_obj_t fp_to_rate(uint32_t rate_fp) { //| """ //| ... //| -static mp_obj_t audiotools_speedchanger_make_new(const mp_obj_type_t *type, +static mp_obj_t audiospeed_speedchanger_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_source, ARG_rate }; static const mp_arg_t allowed_args[] = { @@ -75,8 +75,8 @@ static mp_obj_t audiotools_speedchanger_make_new(const mp_obj_type_t *type, rate_fp = rate_to_fp(args[ARG_rate].u_obj); } - audiotools_speedchanger_obj_t *self = mp_obj_malloc(audiotools_speedchanger_obj_t, &audiotools_speedchanger_type); - common_hal_audiotools_speedchanger_construct(self, source, rate_fp); + audiospeed_speedchanger_obj_t *self = mp_obj_malloc(audiospeed_speedchanger_obj_t, &audiospeed_speedchanger_type); + common_hal_audiospeed_speedchanger_construct(self, source, rate_fp); return MP_OBJ_FROM_PTR(self); } @@ -84,55 +84,55 @@ static mp_obj_t audiotools_speedchanger_make_new(const mp_obj_type_t *type, //| """Deinitialises the SpeedChanger and releases all memory resources for reuse.""" //| ... //| -static mp_obj_t audiotools_speedchanger_deinit(mp_obj_t self_in) { - audiotools_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); - common_hal_audiotools_speedchanger_deinit(self); +static mp_obj_t audiospeed_speedchanger_deinit(mp_obj_t self_in) { + audiospeed_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiospeed_speedchanger_deinit(self); return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_1(audiotools_speedchanger_deinit_obj, audiotools_speedchanger_deinit); +static MP_DEFINE_CONST_FUN_OBJ_1(audiospeed_speedchanger_deinit_obj, audiospeed_speedchanger_deinit); //| rate: float //| """Playback speed multiplier. Can be changed during playback.""" //| -static mp_obj_t audiotools_speedchanger_obj_get_rate(mp_obj_t self_in) { - audiotools_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); +static mp_obj_t audiospeed_speedchanger_obj_get_rate(mp_obj_t self_in) { + audiospeed_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); audiosample_check_for_deinit(&self->base); - return fp_to_rate(common_hal_audiotools_speedchanger_get_rate(self)); + return fp_to_rate(common_hal_audiospeed_speedchanger_get_rate(self)); } -MP_DEFINE_CONST_FUN_OBJ_1(audiotools_speedchanger_get_rate_obj, audiotools_speedchanger_obj_get_rate); +MP_DEFINE_CONST_FUN_OBJ_1(audiospeed_speedchanger_get_rate_obj, audiospeed_speedchanger_obj_get_rate); -static mp_obj_t audiotools_speedchanger_obj_set_rate(mp_obj_t self_in, mp_obj_t rate_obj) { - audiotools_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); +static mp_obj_t audiospeed_speedchanger_obj_set_rate(mp_obj_t self_in, mp_obj_t rate_obj) { + audiospeed_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); audiosample_check_for_deinit(&self->base); - common_hal_audiotools_speedchanger_set_rate(self, rate_to_fp(rate_obj)); + common_hal_audiospeed_speedchanger_set_rate(self, rate_to_fp(rate_obj)); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_2(audiotools_speedchanger_set_rate_obj, audiotools_speedchanger_obj_set_rate); +MP_DEFINE_CONST_FUN_OBJ_2(audiospeed_speedchanger_set_rate_obj, audiospeed_speedchanger_obj_set_rate); -MP_PROPERTY_GETSET(audiotools_speedchanger_rate_obj, - (mp_obj_t)&audiotools_speedchanger_get_rate_obj, - (mp_obj_t)&audiotools_speedchanger_set_rate_obj); +MP_PROPERTY_GETSET(audiospeed_speedchanger_rate_obj, + (mp_obj_t)&audiospeed_speedchanger_get_rate_obj, + (mp_obj_t)&audiospeed_speedchanger_set_rate_obj); -static const mp_rom_map_elem_t audiotools_speedchanger_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiotools_speedchanger_deinit_obj) }, +static const mp_rom_map_elem_t audiospeed_speedchanger_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiospeed_speedchanger_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, - { MP_ROM_QSTR(MP_QSTR_rate), MP_ROM_PTR(&audiotools_speedchanger_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_rate), MP_ROM_PTR(&audiospeed_speedchanger_rate_obj) }, AUDIOSAMPLE_FIELDS, }; -static MP_DEFINE_CONST_DICT(audiotools_speedchanger_locals_dict, audiotools_speedchanger_locals_dict_table); +static MP_DEFINE_CONST_DICT(audiospeed_speedchanger_locals_dict, audiospeed_speedchanger_locals_dict_table); -static const audiosample_p_t audiotools_speedchanger_proto = { +static const audiosample_p_t audiospeed_speedchanger_proto = { MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) - .reset_buffer = (audiosample_reset_buffer_fun)audiotools_speedchanger_reset_buffer, - .get_buffer = (audiosample_get_buffer_fun)audiotools_speedchanger_get_buffer, + .reset_buffer = (audiosample_reset_buffer_fun)audiospeed_speedchanger_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audiospeed_speedchanger_get_buffer, }; MP_DEFINE_CONST_OBJ_TYPE( - audiotools_speedchanger_type, + audiospeed_speedchanger_type, MP_QSTR_SpeedChanger, MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, - make_new, audiotools_speedchanger_make_new, - locals_dict, &audiotools_speedchanger_locals_dict, - protocol, &audiotools_speedchanger_proto + make_new, audiospeed_speedchanger_make_new, + locals_dict, &audiospeed_speedchanger_locals_dict, + protocol, &audiospeed_speedchanger_proto ); diff --git a/shared-bindings/audiospeed/SpeedChanger.h b/shared-bindings/audiospeed/SpeedChanger.h new file mode 100644 index 0000000000000..64a126a7a61fb --- /dev/null +++ b/shared-bindings/audiospeed/SpeedChanger.h @@ -0,0 +1,17 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/audiospeed/SpeedChanger.h" + +extern const mp_obj_type_t audiospeed_speedchanger_type; + +void common_hal_audiospeed_speedchanger_construct(audiospeed_speedchanger_obj_t *self, + mp_obj_t source, uint32_t rate_fp); +void common_hal_audiospeed_speedchanger_deinit(audiospeed_speedchanger_obj_t *self); +void common_hal_audiospeed_speedchanger_set_rate(audiospeed_speedchanger_obj_t *self, uint32_t rate_fp); +uint32_t common_hal_audiospeed_speedchanger_get_rate(audiospeed_speedchanger_obj_t *self); diff --git a/shared-bindings/audiospeed/__init__.c b/shared-bindings/audiospeed/__init__.c new file mode 100644 index 0000000000000..b12e6db7e6bc7 --- /dev/null +++ b/shared-bindings/audiospeed/__init__.c @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/audiospeed/SpeedChanger.h" + +//| """Audio processing tools""" + +static const mp_rom_map_elem_t audiospeed_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiospeed) }, + { MP_ROM_QSTR(MP_QSTR_SpeedChanger), MP_ROM_PTR(&audiospeed_speedchanger_type) }, +}; + +static MP_DEFINE_CONST_DICT(audiospeed_module_globals, audiospeed_module_globals_table); + +const mp_obj_module_t audiospeed_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audiospeed_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audiospeed, audiospeed_module); diff --git a/shared-bindings/audiotools/__init__.h b/shared-bindings/audiospeed/__init__.h similarity index 100% rename from shared-bindings/audiotools/__init__.h rename to shared-bindings/audiospeed/__init__.h diff --git a/shared-bindings/audiotools/SpeedChanger.h b/shared-bindings/audiotools/SpeedChanger.h deleted file mode 100644 index d31ae6d6bdc9f..0000000000000 --- a/shared-bindings/audiotools/SpeedChanger.h +++ /dev/null @@ -1,17 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "shared-module/audiotools/SpeedChanger.h" - -extern const mp_obj_type_t audiotools_speedchanger_type; - -void common_hal_audiotools_speedchanger_construct(audiotools_speedchanger_obj_t *self, - mp_obj_t source, uint32_t rate_fp); -void common_hal_audiotools_speedchanger_deinit(audiotools_speedchanger_obj_t *self); -void common_hal_audiotools_speedchanger_set_rate(audiotools_speedchanger_obj_t *self, uint32_t rate_fp); -uint32_t common_hal_audiotools_speedchanger_get_rate(audiotools_speedchanger_obj_t *self); diff --git a/shared-bindings/audiotools/__init__.c b/shared-bindings/audiotools/__init__.c deleted file mode 100644 index 3d610aa1dbf88..0000000000000 --- a/shared-bindings/audiotools/__init__.c +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt -// -// SPDX-License-Identifier: MIT - -#include - -#include "py/obj.h" -#include "py/runtime.h" - -#include "shared-bindings/audiotools/SpeedChanger.h" - -//| """Audio processing tools""" - -static const mp_rom_map_elem_t audiotools_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiotools) }, - { MP_ROM_QSTR(MP_QSTR_SpeedChanger), MP_ROM_PTR(&audiotools_speedchanger_type) }, -}; - -static MP_DEFINE_CONST_DICT(audiotools_module_globals, audiotools_module_globals_table); - -const mp_obj_module_t audiotools_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&audiotools_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_audiotools, audiotools_module); diff --git a/shared-module/audiotools/SpeedChanger.c b/shared-module/audiospeed/SpeedChanger.c similarity index 90% rename from shared-module/audiotools/SpeedChanger.c rename to shared-module/audiospeed/SpeedChanger.c index 2bf03cf1aa919..e76bed1f17c39 100644 --- a/shared-module/audiotools/SpeedChanger.c +++ b/shared-module/audiospeed/SpeedChanger.c @@ -4,7 +4,7 @@ // // SPDX-License-Identifier: MIT -#include "shared-bindings/audiotools/SpeedChanger.h" +#include "shared-bindings/audiospeed/SpeedChanger.h" #include #include "py/runtime.h" @@ -15,7 +15,7 @@ #define OUTPUT_BUFFER_FRAMES 128 -void common_hal_audiotools_speedchanger_construct(audiotools_speedchanger_obj_t *self, +void common_hal_audiospeed_speedchanger_construct(audiospeed_speedchanger_obj_t *self, mp_obj_t source, uint32_t rate_fp) { audiosample_base_t *src_base = audiosample_check(source); @@ -45,22 +45,22 @@ void common_hal_audiotools_speedchanger_construct(audiotools_speedchanger_obj_t } } -void common_hal_audiotools_speedchanger_deinit(audiotools_speedchanger_obj_t *self) { +void common_hal_audiospeed_speedchanger_deinit(audiospeed_speedchanger_obj_t *self) { self->output_buffer = NULL; self->source = MP_OBJ_NULL; audiosample_mark_deinit(&self->base); } -void common_hal_audiotools_speedchanger_set_rate(audiotools_speedchanger_obj_t *self, uint32_t rate_fp) { +void common_hal_audiospeed_speedchanger_set_rate(audiospeed_speedchanger_obj_t *self, uint32_t rate_fp) { self->rate_fp = rate_fp; } -uint32_t common_hal_audiotools_speedchanger_get_rate(audiotools_speedchanger_obj_t *self) { +uint32_t common_hal_audiospeed_speedchanger_get_rate(audiospeed_speedchanger_obj_t *self) { return self->rate_fp; } // Fetch the next buffer from the source. Returns false if no data available. -static bool fetch_source_buffer(audiotools_speedchanger_obj_t *self) { +static bool fetch_source_buffer(audiospeed_speedchanger_obj_t *self) { if (self->source_exhausted) { return false; } @@ -85,7 +85,7 @@ static bool fetch_source_buffer(audiotools_speedchanger_obj_t *self) { return true; } -void audiotools_speedchanger_reset_buffer(audiotools_speedchanger_obj_t *self, +void audiospeed_speedchanger_reset_buffer(audiospeed_speedchanger_obj_t *self, bool single_channel_output, uint8_t channel) { if (single_channel_output && channel == 1) { return; @@ -99,7 +99,7 @@ void audiotools_speedchanger_reset_buffer(audiotools_speedchanger_obj_t *self, self->source_exhausted = false; } -audioio_get_buffer_result_t audiotools_speedchanger_get_buffer(audiotools_speedchanger_obj_t *self, +audioio_get_buffer_result_t audiospeed_speedchanger_get_buffer(audiospeed_speedchanger_obj_t *self, bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length) { diff --git a/shared-module/audiotools/SpeedChanger.h b/shared-module/audiospeed/SpeedChanger.h similarity index 84% rename from shared-module/audiotools/SpeedChanger.h rename to shared-module/audiospeed/SpeedChanger.h index 05cbec56fdea2..e920c72caf461 100644 --- a/shared-module/audiotools/SpeedChanger.h +++ b/shared-module/audiospeed/SpeedChanger.h @@ -26,10 +26,10 @@ typedef struct { uint32_t rate_fp; // 16.16 fixed-point rate bool source_done; // source returned DONE on last get_buffer bool source_exhausted; // source DONE and we consumed all of it -} audiotools_speedchanger_obj_t; +} audiospeed_speedchanger_obj_t; -void audiotools_speedchanger_reset_buffer(audiotools_speedchanger_obj_t *self, +void audiospeed_speedchanger_reset_buffer(audiospeed_speedchanger_obj_t *self, bool single_channel_output, uint8_t channel); -audioio_get_buffer_result_t audiotools_speedchanger_get_buffer(audiotools_speedchanger_obj_t *self, +audioio_get_buffer_result_t audiospeed_speedchanger_get_buffer(audiospeed_speedchanger_obj_t *self, bool single_channel_output, uint8_t channel, uint8_t **buffer, uint32_t *buffer_length); diff --git a/shared-module/audiotools/__init__.c b/shared-module/audiospeed/__init__.c similarity index 100% rename from shared-module/audiotools/__init__.c rename to shared-module/audiospeed/__init__.c diff --git a/shared-module/audiotools/__init__.h b/shared-module/audiospeed/__init__.h similarity index 100% rename from shared-module/audiotools/__init__.h rename to shared-module/audiospeed/__init__.h From db2d03d643ea1aec45333f2329feb9f26d343c69 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 30 Mar 2026 15:05:40 -0700 Subject: [PATCH 477/517] Zephyr fixes 1. Fix flash writing with more than 32 filesystem blocks per erase page and add a test for it. 2. Fix Feather UF2 to place code in the right spot (the code partition). --- ports/zephyr-cp/Makefile | 9 +- ports/zephyr-cp/cptools/build_all_boards.py | 9 + ports/zephyr-cp/prj.conf | 1 + ports/zephyr-cp/supervisor/flash.c | 215 ++++++++++---------- ports/zephyr-cp/tests/__init__.py | 3 +- ports/zephyr-cp/tests/conftest.py | 25 ++- ports/zephyr-cp/tests/test_flash.py | 171 ++++++++++++++++ ports/zephyr-cp/zephyr-config/west.yml | 2 +- 8 files changed, 324 insertions(+), 111 deletions(-) create mode 100644 ports/zephyr-cp/tests/test_flash.py diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index ae1260c0f4dc1..5c4db4f9065d3 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -21,7 +21,7 @@ ifeq ($(DEBUG),1) WEST_CMAKE_ARGS += -Dzephyr-cp_EXTRA_CONF_FILE=$(DEBUG_CONF_FILE) endif -.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug debug-jlink debugserver attach run run-sim clean menuconfig all clean-all test fetch-port-submodules +.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug debug-jlink debugserver attach run run-sim clean menuconfig all clean-all sim clean-sim test fetch-port-submodules $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) @@ -87,6 +87,13 @@ all: clean-all: rm -rf build build-* +# Build all sim boards concurrently using the same jobserver as `make all`. +sim: + +python cptools/build_all_boards.py --vendor native --continue-on-error + +clean-sim: + rm -rf $(wildcard build-native_*) + test: build-native_native_sim/zephyr-cp/zephyr/zephyr.exe pytest cptools/tests pytest tests/ -v diff --git a/ports/zephyr-cp/cptools/build_all_boards.py b/ports/zephyr-cp/cptools/build_all_boards.py index da9f45ead1e71..8505e732efe71 100755 --- a/ports/zephyr-cp/cptools/build_all_boards.py +++ b/ports/zephyr-cp/cptools/build_all_boards.py @@ -426,6 +426,12 @@ def main(): action="store_true", help="Continue building remaining boards even if one fails", ) + parser.add_argument( + "--vendor", + type=str, + default=None, + help="Only build boards from this vendor (e.g. 'native' for sim boards)", + ) args = parser.parse_args() @@ -439,6 +445,9 @@ def main(): # Discover all boards boards = discover_boards(port_dir) + if args.vendor: + boards = [(v, b) for v, b in boards if v == args.vendor] + if not boards: print("ERROR: No boards found!") return 1 diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 9b4dcccb53e4d..6aa5bd65af58d 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -1,6 +1,7 @@ CONFIG_SYS_HEAP_RUNTIME_STATS=n CONFIG_FLASH=y CONFIG_FLASH_MAP=y +CONFIG_USE_DT_CODE_PARTITION=y CONFIG_DYNAMIC_INTERRUPTS=y CONFIG_UART_INTERRUPT_DRIVEN=y diff --git a/ports/zephyr-cp/supervisor/flash.c b/ports/zephyr-cp/supervisor/flash.c index 38f35a5235afa..a13bb3cffcb20 100644 --- a/ports/zephyr-cp/supervisor/flash.c +++ b/ports/zephyr-cp/supervisor/flash.c @@ -31,15 +31,14 @@ static struct flash_area _dynamic_area; extern const struct device *const flashes[]; extern const int circuitpy_flash_device_count; -// Size of an erase area +// Size of an erase page. static size_t _page_size; -// Size of a write area -static size_t _row_size; +// The value a flash byte has after being erased (usually 0xFF, but can differ). +static uint8_t _erase_value; -// Number of file system blocks in a page. +// Number of FILESYSTEM_BLOCK_SIZE blocks in an erase page. static size_t _blocks_per_page; -static size_t _rows_per_block; static uint32_t _page_mask; #define NO_PAGE_LOADED 0xFFFFFFFF @@ -49,14 +48,41 @@ static uint32_t _current_page_address; static uint32_t _scratch_page_address; -// Track which blocks (up to 32) in the current sector currently live in the -// cache. -static uint32_t _dirty_mask; -static uint32_t _loaded_mask; +// Per-block flags packed into a uint8_t array (one byte per block). +#define ROW_LOADED 0x01 // Block data is present in the cache. +#define ROW_DIRTY 0x02 // Block has been modified since last flush. +#define ROW_ERASED 0x04 // Block is all 0xFF; no need to write after erase. + +static uint8_t *_row_flags = NULL; + +static inline void _clear_row_flags(void) { + if (_row_flags != NULL) { + memset(_row_flags, 0, _blocks_per_page); + } +} + +static inline bool _any_dirty(void) { + for (size_t i = 0; i < _blocks_per_page; i++) { + if (_row_flags[i] & ROW_DIRTY) { + return true; + } + } + return false; +} + +// Check if a buffer is entirely the erase value. +static bool _buffer_is_erased(const uint8_t *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + if (buf[i] != _erase_value) { + return false; + } + } + return true; +} // Table of pointers to each cached block. Should be zero'd after allocation. -#define FLASH_CACHE_TABLE_NUM_ENTRIES (_blocks_per_page * _rows_per_block) -#define FLASH_CACHE_TABLE_SIZE (FLASH_CACHE_TABLE_NUM_ENTRIES * sizeof (uint8_t *)) +#define FLASH_CACHE_TABLE_NUM_ENTRIES (_blocks_per_page) +#define FLASH_CACHE_TABLE_SIZE (FLASH_CACHE_TABLE_NUM_ENTRIES * sizeof(uint8_t *)) static uint8_t **flash_cache_table = NULL; static K_MUTEX_DEFINE(_mutex); @@ -171,52 +197,44 @@ void supervisor_flash_init(void) { return; } - const struct device *d = flash_area_get_device(filesystem_area); - _row_size = flash_get_write_block_size(d); - if (_row_size < 256) { - if (256 % _row_size == 0) { - _row_size = 256; - } else { - size_t new_row_size = _row_size; - while (new_row_size < 256) { - new_row_size += _row_size; - } - _row_size = new_row_size; - } - } struct flash_pages_info first_info; + const struct device *d = flash_area_get_device(filesystem_area); + const struct flash_parameters *fp = flash_get_parameters(d); + _erase_value = fp->erase_value; flash_get_page_info_by_offs(d, filesystem_area->fa_off, &first_info); struct flash_pages_info last_info; - flash_get_page_info_by_offs(d, filesystem_area->fa_off + filesystem_area->fa_size - _row_size, &last_info); + flash_get_page_info_by_offs(d, filesystem_area->fa_off + filesystem_area->fa_size - FILESYSTEM_BLOCK_SIZE, &last_info); _page_size = first_info.size; if (_page_size < FILESYSTEM_BLOCK_SIZE) { _page_size = FILESYSTEM_BLOCK_SIZE; } - printk(" erase page size %d\n", _page_size); - // Makes sure that a cached page has 32 or fewer rows. Our dirty mask is - // only 32 bits. - while (_page_size / _row_size > 32) { - _row_size *= 2; - } - printk(" write row size %d\n", _row_size); _blocks_per_page = _page_size / FILESYSTEM_BLOCK_SIZE; - printk(" blocks per page %d\n", _blocks_per_page); - _rows_per_block = FILESYSTEM_BLOCK_SIZE / _row_size; _page_mask = ~(_page_size - 1); + printk(" erase page size %d\n", _page_size); + printk(" blocks per page %d\n", _blocks_per_page); + + _row_flags = port_malloc(_blocks_per_page, false); + if (_row_flags == NULL) { + printk("Unable to allocate row flags (%d bytes)\n", _blocks_per_page); + filesystem_area = NULL; + return; + } + memset(_row_flags, 0, _blocks_per_page); + // The last page is the scratch sector. - _scratch_page_address = last_info.start_offset; + _scratch_page_address = last_info.start_offset - filesystem_area->fa_off; _current_page_address = NO_PAGE_LOADED; } uint32_t supervisor_flash_get_block_size(void) { - return 512; + return FILESYSTEM_BLOCK_SIZE; } uint32_t supervisor_flash_get_block_count(void) { if (filesystem_area == NULL) { return 0; } - return (_scratch_page_address - filesystem_area->fa_off) / 512; + return _scratch_page_address / FILESYSTEM_BLOCK_SIZE; } @@ -245,10 +263,8 @@ static bool write_flash(uint32_t address, const uint8_t *data, uint32_t data_len static bool block_erased(uint32_t sector_address) { uint8_t short_buffer[4]; if (read_flash(sector_address, short_buffer, 4)) { - for (uint16_t i = 0; i < 4; i++) { - if (short_buffer[i] != 0xff) { - return false; - } + if (!_buffer_is_erased(short_buffer, 4)) { + return false; } } else { return false; @@ -257,10 +273,8 @@ static bool block_erased(uint32_t sector_address) { // Now check the full length. uint8_t full_buffer[FILESYSTEM_BLOCK_SIZE]; if (read_flash(sector_address, full_buffer, FILESYSTEM_BLOCK_SIZE)) { - for (uint16_t i = 0; i < FILESYSTEM_BLOCK_SIZE; i++) { - if (short_buffer[i] != 0xff) { - return false; - } + if (!_buffer_is_erased(full_buffer, FILESYSTEM_BLOCK_SIZE)) { + return false; } } else { return false; @@ -278,17 +292,26 @@ static bool erase_page(uint32_t sector_address) { return res == 0; } -// Sector is really 24 bits. static bool copy_block(uint32_t src_address, uint32_t dest_address) { - // Copy row by row to minimize RAM buffer. - uint8_t buffer[_row_size]; - for (uint32_t i = 0; i < FILESYSTEM_BLOCK_SIZE / _row_size; i++) { - if (!read_flash(src_address + i * _row_size, buffer, _row_size)) { - return false; - } - if (!write_flash(dest_address + i * _row_size, buffer, _row_size)) { - return false; - } + uint8_t buffer[FILESYSTEM_BLOCK_SIZE]; + if (!read_flash(src_address, buffer, FILESYSTEM_BLOCK_SIZE)) { + return false; + } + if (!write_flash(dest_address, buffer, FILESYSTEM_BLOCK_SIZE)) { + return false; + } + return true; +} + +// Load a block into the ram cache and set its flags. Returns false on read error. +static bool _load_block_into_cache(size_t block_index) { + if (!read_flash(_current_page_address + block_index * FILESYSTEM_BLOCK_SIZE, + flash_cache_table[block_index], FILESYSTEM_BLOCK_SIZE)) { + return false; + } + _row_flags[block_index] |= ROW_LOADED; + if (_buffer_is_erased(flash_cache_table[block_index], FILESYSTEM_BLOCK_SIZE)) { + _row_flags[block_index] |= ROW_ERASED; } return true; } @@ -303,12 +326,12 @@ static bool flush_scratch_flash(void) { // cached. bool copy_to_scratch_ok = true; for (size_t i = 0; i < _blocks_per_page; i++) { - if ((_dirty_mask & (1 << i)) == 0) { + if (!(_row_flags[i] & ROW_DIRTY)) { copy_to_scratch_ok = copy_to_scratch_ok && copy_block(_current_page_address + i * FILESYSTEM_BLOCK_SIZE, _scratch_page_address + i * FILESYSTEM_BLOCK_SIZE); } - _loaded_mask |= (1 << i); + _row_flags[i] |= ROW_LOADED; } if (!copy_to_scratch_ok) { // TODO(tannewt): Do more here. We opted to not erase and copy bad data @@ -341,7 +364,7 @@ static void release_ram_cache(void) { port_free(flash_cache_table); flash_cache_table = NULL; _current_page_address = NO_PAGE_LOADED; - _loaded_mask = 0; + _clear_row_flags(); } // Attempts to allocate a new set of page buffers for caching a full sector in @@ -350,7 +373,7 @@ static void release_ram_cache(void) { static bool allocate_ram_cache(void) { flash_cache_table = port_malloc(FLASH_CACHE_TABLE_SIZE, false); if (flash_cache_table == NULL) { - // Not enough space even for the cache table. + printk("Unable to allocate ram cache table\n"); return false; } @@ -359,21 +382,20 @@ static bool allocate_ram_cache(void) { bool success = true; for (size_t i = 0; i < _blocks_per_page && success; i++) { - for (size_t j = 0; j < _rows_per_block && success; j++) { - uint8_t *page_cache = port_malloc(_row_size, false); - if (page_cache == NULL) { - success = false; - break; - } - flash_cache_table[i * _rows_per_block + j] = page_cache; + uint8_t *block_cache = port_malloc(FILESYSTEM_BLOCK_SIZE, false); + if (block_cache == NULL) { + success = false; + break; } + flash_cache_table[i] = block_cache; } // We couldn't allocate enough so give back what we got. if (!success) { + printk("Unable to allocate ram cache pages\n"); release_ram_cache(); } - _loaded_mask = 0; + _clear_row_flags(); _current_page_address = NO_PAGE_LOADED; return success; } @@ -386,7 +408,7 @@ static bool flush_ram_cache(bool keep_cache) { return true; } - if (_current_page_address == NO_PAGE_LOADED || _dirty_mask == 0) { + if (_current_page_address == NO_PAGE_LOADED || !_any_dirty()) { if (!keep_cache) { release_ram_cache(); } @@ -397,21 +419,12 @@ static bool flush_ram_cache(bool keep_cache) { // erase below. bool copy_to_ram_ok = true; for (size_t i = 0; i < _blocks_per_page; i++) { - if ((_loaded_mask & (1 << i)) == 0) { - for (size_t j = 0; j < _rows_per_block; j++) { - copy_to_ram_ok = read_flash( - _current_page_address + (i * _rows_per_block + j) * _row_size, - flash_cache_table[i * _rows_per_block + j], - _row_size); - if (!copy_to_ram_ok) { - break; - } + if (!(_row_flags[i] & ROW_LOADED)) { + if (!_load_block_into_cache(i)) { + copy_to_ram_ok = false; + break; } } - if (!copy_to_ram_ok) { - break; - } - _loaded_mask |= (1 << i); } if (!copy_to_ram_ok) { @@ -421,14 +434,19 @@ static bool flush_ram_cache(bool keep_cache) { erase_page(_current_page_address); // Lastly, write all the data in ram that we've cached. for (size_t i = 0; i < _blocks_per_page; i++) { - for (size_t j = 0; j < _rows_per_block; j++) { - write_flash(_current_page_address + (i * _rows_per_block + j) * _row_size, - flash_cache_table[i * _rows_per_block + j], - _row_size); + // Skip blocks that are entirely erased — the page erase already + // set them to 0xFF so writing would be redundant. + if (_row_flags[i] & ROW_ERASED) { + continue; } + write_flash(_current_page_address + i * FILESYSTEM_BLOCK_SIZE, + flash_cache_table[i], + FILESYSTEM_BLOCK_SIZE); + } + // Nothing is dirty anymore. Clear dirty but keep loaded and erased. + for (size_t i = 0; i < _blocks_per_page; i++) { + _row_flags[i] &= ~ROW_DIRTY; } - // Nothing is dirty anymore. Some may already be in the cache cleanly. - _dirty_mask = 0; // We're done with the cache for now so give it back. if (!keep_cache) { @@ -491,15 +509,10 @@ static bool _flash_read_block(uint8_t *dest, uint32_t block) { // Mask out the lower bits that designate the address within the sector. uint32_t page_address = address & _page_mask; size_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % _blocks_per_page; - uint32_t mask = 1 << (block_index); // We're reading from the currently cached sector. - if (_current_page_address == page_address && (mask & _loaded_mask) > 0) { + if (_current_page_address == page_address && (_row_flags[block_index] & ROW_LOADED)) { if (flash_cache_table != NULL) { - for (int i = 0; i < _rows_per_block; i++) { - memcpy(dest + i * _row_size, - flash_cache_table[block_index * _rows_per_block + i], - _row_size); - } + memcpy(dest, flash_cache_table[block_index], FILESYSTEM_BLOCK_SIZE); return true; } uint32_t scratch_block_address = _scratch_page_address + block_index * FILESYSTEM_BLOCK_SIZE; @@ -518,7 +531,6 @@ static bool _flash_write_block(const uint8_t *data, uint32_t block) { // Mask out the lower bits that designate the address within the sector. uint32_t page_address = address & _page_mask; size_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % _blocks_per_page; - uint32_t mask = 1 << (block_index); // Flush the cache if we're moving onto a different page. if (_current_page_address != page_address) { // Check to see if we'd write to an erased block and aren't writing to @@ -533,19 +545,12 @@ static bool _flash_write_block(const uint8_t *data, uint32_t block) { erase_page(_scratch_page_address); } _current_page_address = page_address; - _dirty_mask = 0; - _loaded_mask = 0; + _clear_row_flags(); } - _dirty_mask |= mask; - _loaded_mask |= mask; - + _row_flags[block_index] = ROW_DIRTY | ROW_LOADED; // Copy the block to the appropriate cache. if (flash_cache_table != NULL) { - for (int i = 0; i < _rows_per_block; i++) { - memcpy(flash_cache_table[block_index * _rows_per_block + i], - data + i * _row_size, - _row_size); - } + memcpy(flash_cache_table[block_index], data, FILESYSTEM_BLOCK_SIZE); return true; } else { uint32_t scratch_block_address = _scratch_page_address + block_index * FILESYSTEM_BLOCK_SIZE; diff --git a/ports/zephyr-cp/tests/__init__.py b/ports/zephyr-cp/tests/__init__.py index 8ab7610ce0f53..e9039dd88b346 100644 --- a/ports/zephyr-cp/tests/__init__.py +++ b/ports/zephyr-cp/tests/__init__.py @@ -108,12 +108,13 @@ def write(self, text): class NativeSimProcess: - def __init__(self, cmd, timeout=5, trace_file=None, env=None): + def __init__(self, cmd, timeout=5, trace_file=None, env=None, flash_file=None): if trace_file: cmd.append(f"--trace-file={trace_file}") self._timeout = timeout self.trace_file = trace_file + self.flash_file = flash_file print("Running", " ".join(cmd)) self._proc = subprocess.Popen( cmd, diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py index b0047f0c94763..cec867a97037c 100644 --- a/ports/zephyr-cp/tests/conftest.py +++ b/ports/zephyr-cp/tests/conftest.py @@ -73,6 +73,10 @@ def pytest_configure(config): "markers", "display_mono_vtiled(value): override the mono vtiled screen_info flag (True or False)", ) + config.addinivalue_line( + "markers", + "flash_config(erase_block_size=N, total_size=N): override flash simulator parameters", + ) ZEPHYR_CP = Path(__file__).parent.parent @@ -264,10 +268,19 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp use_realtime = request.node.get_closest_marker("native_sim_rt") is not None + flash_config_marker = request.node.get_closest_marker("flash_config") + flash_total_size = 2 * 1024 * 1024 # default 2MB + flash_erase_block_size = None + flash_write_block_size = None + if flash_config_marker: + flash_total_size = flash_config_marker.kwargs.get("total_size", flash_total_size) + flash_erase_block_size = flash_config_marker.kwargs.get("erase_block_size", None) + flash_write_block_size = flash_config_marker.kwargs.get("write_block_size", None) + procs = [] for i in range(instance_count): flash = tmp_path / f"flash-{i}.bin" - flash.write_bytes(b"\xff" * (2 * 1024 * 1024)) + flash.write_bytes(b"\xff" * flash_total_size) files = None if len(drives[i][1].args) == 1: files = drives[i][1].args[0] @@ -308,6 +321,13 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp (realtime_flag, "-display_headless", "-wait_uart", f"--vm-runs={code_py_runs + 1}") ) + if flash_erase_block_size is not None: + cmd.append(f"--flash_erase_block_size={flash_erase_block_size}") + if flash_write_block_size is not None: + cmd.append(f"--flash_write_block_size={flash_write_block_size}") + if flash_config_marker and "total_size" in flash_config_marker.kwargs: + cmd.append(f"--flash_total_size={flash_total_size}") + if input_trace_file is not None: cmd.append(f"--input-trace={input_trace_file}") @@ -334,12 +354,11 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp cmd.append(f"--display_capture_png={capture_png_pattern}") logger.info("Running: %s", " ".join(cmd)) - proc = NativeSimProcess(cmd, timeout, trace_file, env) + proc = NativeSimProcess(cmd, timeout, trace_file, env, flash_file=flash) proc.display_dump = None proc._capture_png_pattern = capture_png_pattern proc._capture_count = len(capture_times_ns) if capture_times_ns is not None else 0 procs.append(proc) - if instance_count == 1: yield procs[0] else: diff --git a/ports/zephyr-cp/tests/test_flash.py b/ports/zephyr-cp/tests/test_flash.py new file mode 100644 index 0000000000000..e2e5f4de14f81 --- /dev/null +++ b/ports/zephyr-cp/tests/test_flash.py @@ -0,0 +1,171 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test flash filesystem with various erase block sizes.""" + +import subprocess + +import pytest + + +def read_file_from_flash(flash_file, path): + """Extract a file from the FAT filesystem in the flash image.""" + result = subprocess.run( + ["mcopy", "-i", str(flash_file), f"::{path}", "-"], + capture_output=True, + ) + if result.returncode != 0: + raise FileNotFoundError( + f"Failed to read ::{path} from {flash_file}: {result.stderr.decode()}" + ) + return result.stdout.decode() + + +WRITE_READ_CODE = """\ +import storage +storage.remount("/", readonly=False) +with open("/test.txt", "w") as f: + f.write("hello flash") +with open("/test.txt", "r") as f: + content = f.read() +print(f"content: {content}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_READ_CODE}) +def test_flash_default_erase_size(circuitpython): + """Test filesystem write/read with default 4KB erase blocks.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "content: hello flash" in output + assert "done" in output + + content = read_file_from_flash(circuitpython.flash_file, "test.txt") + assert content == "hello flash" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_READ_CODE}) +@pytest.mark.flash_config(erase_block_size=65536) +def test_flash_64k_erase_blocks(circuitpython): + """Test filesystem write/read with 64KB erase blocks (128 blocks per page).""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "content: hello flash" in output + assert "done" in output + + content = read_file_from_flash(circuitpython.flash_file, "test.txt") + assert content == "hello flash" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_READ_CODE}) +@pytest.mark.flash_config(erase_block_size=262144, total_size=4 * 1024 * 1024) +def test_flash_256k_erase_blocks(circuitpython): + """Test filesystem write/read with 256KB erase blocks (like RA8D1 OSPI).""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "content: hello flash" in output + assert "done" in output + + content = read_file_from_flash(circuitpython.flash_file, "test.txt") + assert content == "hello flash" + + +MULTI_FILE_CODE = """\ +import storage +storage.remount("/", readonly=False) +for i in range(5): + name = f"/file{i}.txt" + with open(name, "w") as f: + f.write(f"data{i}" * 100) +print("multi_file_ok") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": MULTI_FILE_CODE}) +@pytest.mark.flash_config(erase_block_size=262144, total_size=4 * 1024 * 1024) +def test_flash_256k_multi_file(circuitpython): + """Test multiple file writes with 256KB erase blocks to exercise cache flushing.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "multi_file_ok" in output + + for i in range(5): + content = read_file_from_flash(circuitpython.flash_file, f"file{i}.txt") + assert content == f"data{i}" * 100, f"file{i}.txt mismatch" + + +EXISTING_DATA_CODE = """\ +import storage +storage.remount("/", readonly=False) + +# Write a file to populate the erase page. +original_data = "A" * 4000 +with open("/original.txt", "w") as f: + f.write(original_data) + +# Force a flush so the data is written to flash. +storage.remount("/", readonly=True) +storage.remount("/", readonly=False) + +# Now write a small new file. This updates FAT metadata and directory +# entries that share an erase page with original.txt, exercising the +# read-modify-write cycle on the cached page. +with open("/small.txt", "w") as f: + f.write("tiny") + +# Read back original to check it survived. +with open("/original.txt", "r") as f: + readback = f.read() +if readback == original_data: + print("existing_data_ok") +else: + print(f"MISMATCH: got {len(readback)} bytes") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": EXISTING_DATA_CODE}) +@pytest.mark.flash_config(erase_block_size=262144, total_size=4 * 1024 * 1024) +def test_flash_256k_existing_data_survives(circuitpython): + """Test that existing data in an erase page survives when new data is written. + + With 256KB erase blocks (512 blocks per page), writing to any block in + the page triggers an erase-rewrite of the entire page. Existing blocks + must be preserved through the read-modify-write cycle. + """ + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "existing_data_ok" in output + + # Verify both files survived on the actual flash image. + original = read_file_from_flash(circuitpython.flash_file, "original.txt") + assert original == "A" * 4000, f"original.txt corrupted: got {len(original)} bytes" + + small = read_file_from_flash(circuitpython.flash_file, "small.txt") + assert small == "tiny" + + +OVERWRITE_CODE = """\ +import storage +storage.remount("/", readonly=False) +with open("/overwrite.txt", "w") as f: + f.write("first version") +with open("/overwrite.txt", "w") as f: + f.write("second version") +print("overwrite_ok") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": OVERWRITE_CODE}) +@pytest.mark.flash_config(erase_block_size=262144, total_size=4 * 1024 * 1024) +def test_flash_256k_overwrite(circuitpython): + """Test overwriting a file with 256KB erase blocks to exercise erase-rewrite cycle.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "overwrite_ok" in output + + content = read_file_from_flash(circuitpython.flash_file, "overwrite.txt") + assert content == "second version" diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index 5433da0e81ac6..fd3eebc66f172 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -8,6 +8,6 @@ manifest: path: modules/bsim_hw_models/nrf_hw_models - name: zephyr url: https://github.com/adafruit/zephyr - revision: 152e280f154a50035c2f054eceeb107fa3bb474f + revision: a768573cc42b18bc2e8b819d4686e52cdb9c848e clone-depth: 100 import: true From 5007e8b8e2cfd2f2c37e336f2bb9331f0b7eecd7 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Tue, 31 Mar 2026 15:07:16 -0700 Subject: [PATCH 478/517] Only code partition for feather --- ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf | 2 ++ ports/zephyr-cp/prj.conf | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf index 4849c8ce2b4ad..20176b34be052 100644 --- a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf @@ -5,4 +5,6 @@ CONFIG_BT_BROADCASTER=y CONFIG_BT_OBSERVER=y CONFIG_BT_EXT_ADV=y +CONFIG_USE_DT_CODE_PARTITION=y + CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 6aa5bd65af58d..9b4dcccb53e4d 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -1,7 +1,6 @@ CONFIG_SYS_HEAP_RUNTIME_STATS=n CONFIG_FLASH=y CONFIG_FLASH_MAP=y -CONFIG_USE_DT_CODE_PARTITION=y CONFIG_DYNAMIC_INTERRUPTS=y CONFIG_UART_INTERRUPT_DRIVEN=y From abbd8b054a05a53654358b138d705f23bc03d97c Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 30 Mar 2026 15:48:32 -0700 Subject: [PATCH 479/517] Add Zephyr board defs for Raspberry Pi Picos Add board defs for rpi_pico_zephyr, rpi_pico_w_zephyr, rpi_pico2_zephyr, and rpi_pico2_w_zephyr. Flash partitions (NVM at 0x180000, CircuitPy at 0x181000) match the native raspberrypi port so users can switch between ports without reformatting. Also enable MICROPY_NLR_THUMB_USE_LONG_JUMP for the Zephyr port to fix Cortex-M0+ linker relocation errors in nlr_push. Devices should enumerate but may have bugs after that. These board definitions are meant to make it easier to test the current state of the Zephyr port. Co-Authored-By: Claude Opus 4.6 (1M context) --- README.rst | 2 +- ports/zephyr-cp/boards/board_aliases.cmake | 4 + ports/zephyr-cp/boards/frdm_rw612.conf | 1 + .../autogen_board_info.toml | 119 ++++++ .../rpi_pico2_w_zephyr/circuitpython.toml | 2 + .../rpi_pico2_zephyr/autogen_board_info.toml | 119 ++++++ .../rpi_pico2_zephyr/circuitpython.toml | 1 + .../rpi_pico_w_zephyr/autogen_board_info.toml | 119 ++++++ .../rpi_pico_w_zephyr/circuitpython.toml | 2 + .../rpi_pico_zephyr/autogen_board_info.toml | 119 ++++++ .../rpi_pico_zephyr/circuitpython.toml | 1 + .../boards/rpi_pico2_rp2350a_m33.overlay | 25 ++ .../boards/rpi_pico2_rp2350a_m33_w.conf | 24 ++ .../boards/rpi_pico2_rp2350a_m33_w.overlay | 25 ++ .../zephyr-cp/boards/rpi_pico_rp2040.overlay | 34 ++ ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf | 26 ++ .../boards/rpi_pico_rp2040_w.overlay | 34 ++ ports/zephyr-cp/common-hal/wifi/Radio.c | 10 +- ports/zephyr-cp/common-hal/wifi/__init__.c | 10 +- .../zephyr-cp/cptools/build_circuitpython.py | 2 + ports/zephyr-cp/cptools/compat2driver.py | 400 +++++++++++++++--- ports/zephyr-cp/cptools/gen_compat2driver.py | 2 +- ports/zephyr-cp/cptools/zephyr2cp.py | 34 +- ports/zephyr-cp/mpconfigport.h | 2 + ports/zephyr-cp/supervisor/port.c | 4 + 25 files changed, 1047 insertions(+), 74 deletions(-) create mode 100644 ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay create mode 100644 ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf create mode 100644 ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay create mode 100644 ports/zephyr-cp/boards/rpi_pico_rp2040.overlay create mode 100644 ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf create mode 100644 ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay diff --git a/README.rst b/README.rst index 80aa1b2aee282..89c7d9229d8ed 100644 --- a/README.rst +++ b/README.rst @@ -226,7 +226,7 @@ Ports Ports include the code unique to a microcontroller line. -The following ports are available: ``atmel-samd``, ``cxd56``, ``espressif``, ``litex``, ``mimxrt10xx``, ``nordic``, ``raspberrypi``, ``renode``, ``silabs`` (``efr32``), ``stm``, ``unix``. +The following ports are available: ``atmel-samd``, ``cxd56``, ``espressif``, ``litex``, ``mimxrt10xx``, ``nordic``, ``raspberrypi``, ``renode``, ``silabs`` (``efr32``), ``stm``, ``unix``, and ``zephyr-cp``. However, not all ports are fully functional. Some have limited functionality and known serious bugs. For details, refer to the **Port status** section in the `latest release `__ notes. diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index 5914ae61f28e6..e973eac72d4a5 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -44,4 +44,8 @@ cp_board_alias(st_stm32h7b3i_dk stm32h7b3i_dk) cp_board_alias(st_stm32h750b_dk stm32h750b_dk/stm32h750xx/ext_flash_app) cp_board_alias(st_stm32wba65i_dk1 stm32wba65i_dk1) cp_board_alias(st_nucleo_u575zi_q nucleo_u575zi_q/stm32u575xx) +cp_board_alias(raspberrypi_rpi_pico_zephyr rpi_pico/rp2040) +cp_board_alias(raspberrypi_rpi_pico_w_zephyr rpi_pico/rp2040/w) +cp_board_alias(raspberrypi_rpi_pico2_zephyr rpi_pico2/rp2350a/m33) +cp_board_alias(raspberrypi_rpi_pico2_w_zephyr rpi_pico2/rp2350a/m33/w) cp_board_alias(st_nucleo_n657x0_q nucleo_n657x0_q/stm32n657xx) diff --git a/ports/zephyr-cp/boards/frdm_rw612.conf b/ports/zephyr-cp/boards/frdm_rw612.conf index 4fc5c8c5bf0fe..ac9a43646a18c 100644 --- a/ports/zephyr-cp/boards/frdm_rw612.conf +++ b/ports/zephyr-cp/boards/frdm_rw612.conf @@ -18,6 +18,7 @@ CONFIG_MBEDTLS_RSA_C=y CONFIG_MBEDTLS_PKCS1_V15=y CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=y CONFIG_MBEDTLS_ENTROPY_C=y +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y CONFIG_MBEDTLS_CTR_DRBG_C=y CONFIG_MBEDTLS_SHA1=y CONFIG_MBEDTLS_USE_PSA_CRYPTO=n diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..8c1f7018f1242 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml @@ -0,0 +1,119 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Raspberry Pi Foundation Raspberry Pi Pico 2" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true # Zephyr networking enabled +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = true # Zephyr networking enabled +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = true # Zephyr networking enabled +spitarget = false +ssl = true # Zephyr networking enabled +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = true # Zephyr board has wifi +zephyr_display = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..0f901d1149ea3 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/circuitpython.toml @@ -0,0 +1,2 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] +BLOBS=["hal_infineon"] diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..cdffc295701ec --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml @@ -0,0 +1,119 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Raspberry Pi Foundation Raspberry Pi Pico 2" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..9d3c229ed1b45 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..d8cee739d6500 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml @@ -0,0 +1,119 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Raspberry Pi Foundation Raspberry Pi Pico" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true # Zephyr networking enabled +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = true # Zephyr networking enabled +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = true # Zephyr networking enabled +spitarget = false +ssl = true # Zephyr networking enabled +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = true # Zephyr board has wifi +zephyr_display = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..0f901d1149ea3 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/circuitpython.toml @@ -0,0 +1,2 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] +BLOBS=["hal_infineon"] diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..40fb5baf34a90 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml @@ -0,0 +1,119 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Raspberry Pi Foundation Raspberry Pi Pico" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..9d3c229ed1b45 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay new file mode 100644 index 0000000000000..eb94fe7de675a --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay @@ -0,0 +1,25 @@ +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + code_partition: partition@0 { + label = "code-partition"; + reg = <0x0 0x180000>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(4) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf new file mode 100644 index 0000000000000..1a0d0010dca2a --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf @@ -0,0 +1,24 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y + +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y + +CONFIG_NET_HOSTNAME_ENABLE=y +CONFIG_NET_HOSTNAME_DYNAMIC=y +CONFIG_NET_HOSTNAME="circuitpython" + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_RSA_C=y +CONFIG_MBEDTLS_PKCS1_V15=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=y +CONFIG_MBEDTLS_ENTROPY_C=y +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CTR_DRBG_C=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_USE_PSA_CRYPTO=n diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay new file mode 100644 index 0000000000000..eb94fe7de675a --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay @@ -0,0 +1,25 @@ +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + code_partition: partition@0 { + label = "code-partition"; + reg = <0x0 0x180000>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(4) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay b/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay new file mode 100644 index 0000000000000..ce9083dd62d42 --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay @@ -0,0 +1,34 @@ +&flash0 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Reserved memory for the second stage bootloader */ + second_stage_bootloader: partition@0 { + label = "second_stage_bootloader"; + reg = <0x00000000 0x100>; + read-only; + }; + + code_partition: partition@100 { + label = "code-partition"; + reg = <0x100 (0x180000 - 0x100)>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(2) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf new file mode 100644 index 0000000000000..11d26d946b16d --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf @@ -0,0 +1,26 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y + +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y + +CONFIG_NET_HOSTNAME_ENABLE=y +CONFIG_NET_HOSTNAME_DYNAMIC=y +CONFIG_NET_HOSTNAME="circuitpython" + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_RSA_C=y +CONFIG_MBEDTLS_PKCS1_V15=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=y +CONFIG_MBEDTLS_ENTROPY_C=y +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CTR_DRBG_C=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_USE_PSA_CRYPTO=n + +CONFIG_TEST_RANDOM_GENERATOR=y diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay new file mode 100644 index 0000000000000..ce9083dd62d42 --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay @@ -0,0 +1,34 @@ +&flash0 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Reserved memory for the second stage bootloader */ + second_stage_bootloader: partition@0 { + label = "second_stage_bootloader"; + reg = <0x00000000 0x100>; + read-only; + }; + + code_partition: partition@100 { + label = "code-partition"; + reg = <0x100 (0x180000 - 0x100)>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(2) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/common-hal/wifi/Radio.c b/ports/zephyr-cp/common-hal/wifi/Radio.c index 726b406b3ca89..35a0b76a36268 100644 --- a/ports/zephyr-cp/common-hal/wifi/Radio.c +++ b/ports/zephyr-cp/common-hal/wifi/Radio.c @@ -86,13 +86,19 @@ void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled) { // mdns_server_deinit_singleton(); // #endif printk("net_if_down\n"); - CHECK_ZEPHYR_RESULT(net_if_down(self->sta_netif)); + int res = net_if_down(self->sta_netif); + if (res < 0 && res != -EALREADY) { + raise_zephyr_error(res); + } self->started = false; return; } if (!self->started && enabled) { printk("net_if_up\n"); - CHECK_ZEPHYR_RESULT(net_if_up(self->sta_netif)); + int res = net_if_up(self->sta_netif); + if (res < 0 && res != -EALREADY) { + raise_zephyr_error(res); + } self->started = true; self->current_scan = NULL; // common_hal_wifi_radio_set_tx_power(self, CIRCUITPY_WIFI_DEFAULT_TX_POWER); diff --git a/ports/zephyr-cp/common-hal/wifi/__init__.c b/ports/zephyr-cp/common-hal/wifi/__init__.c index da26d56072444..f9e02be247600 100644 --- a/ports/zephyr-cp/common-hal/wifi/__init__.c +++ b/ports/zephyr-cp/common-hal/wifi/__init__.c @@ -52,16 +52,17 @@ static void _event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_eve switch (mgmt_event) { case NET_EVENT_WIFI_SCAN_RESULT: { - #if defined(CONFIG_NET_MGMT_EVENT_INFO) + printk("NET_EVENT_WIFI_SCAN_RESULT\n"); const struct wifi_scan_result *result = cb->info; if (result != NULL && self->current_scan != NULL) { wifi_scannednetworks_scan_result(self->current_scan, result); } - #endif break; } case NET_EVENT_WIFI_SCAN_DONE: - printk("NET_EVENT_WIFI_SCAN_DONE\n"); + printk("NET_EVENT_WIFI_SCAN_DONE (thread: %s prio=%d)\n", + k_thread_name_get(k_current_get()), + k_thread_priority_get(k_current_get())); if (self->current_scan != NULL) { k_poll_signal_raise(&self->current_scan->channel_done, 0); } @@ -105,6 +106,9 @@ static void _event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_eve case NET_EVENT_WIFI_AP_STA_DISCONNECTED: printk("NET_EVENT_WIFI_AP_STA_DISCONNECTED\n"); break; + default: + printk("unhandled net event %x\n", mgmt_event); + break; } } diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 5f51281870c4d..f6f10dde80149 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -270,6 +270,8 @@ def determine_enabled_modules(board_info, portdir, srcdir): network_enabled = board_info.get("wifi", False) or board_info.get("hostnetwork", False) if network_enabled: + enabled_modules.add("ipaddress") + module_reasons["ipaddress"] = "Zephyr networking enabled" enabled_modules.add("socketpool") module_reasons["socketpool"] = "Zephyr networking enabled" enabled_modules.add("hashlib") diff --git a/ports/zephyr-cp/cptools/compat2driver.py b/ports/zephyr-cp/cptools/compat2driver.py index dff09787b2881..aae490d8f7425 100644 --- a/ports/zephyr-cp/cptools/compat2driver.py +++ b/ports/zephyr-cp/cptools/compat2driver.py @@ -15,13 +15,15 @@ "atmel_sam0_adc": "adc", "atmel_sam_adc": "adc", "atmel_sam_afec": "adc", + "bflb_adc": "adc", "ene_kb106x_adc": "adc", "ene_kb1200_adc": "adc", "espressif_esp32_adc": "adc", "gd_gd32_adc": "adc", + "infineon_adc": "adc", "infineon_autanalog_sar_adc": "adc", - "infineon_cat1_adc": "adc", "infineon_hppass_sar_adc": "adc", + "infineon_sar_adc": "adc", "infineon_xmc4xxx_adc": "adc", "ite_it51xxx_adc": "adc", "ite_it8xxx2_adc": "adc", @@ -37,6 +39,7 @@ "maxim_max11117": "adc", "maxim_max11253": "adc", "maxim_max11254": "adc", + "microchip_adc_g1": "adc", "microchip_mcp356xr": "adc", "microchip_xec_adc": "adc", "nordic_nrf_adc": "adc", @@ -53,12 +56,16 @@ "nxp_vf610_adc": "adc", "raspberrypi_pico_adc": "adc", "realtek_rts5912_adc": "adc", - "renesas_ra_adc": "adc", + "renesas_ra_adc12": "adc", + "renesas_ra_adc16": "adc", "renesas_rx_adc": "adc", "renesas_rz_adc": "adc", "renesas_rz_adc_c": "adc", + "renesas_rz_adc_e": "adc", + "renesas_rza2m_adc": "adc", "renesas_smartbond_adc": "adc", "renesas_smartbond_sdadc": "adc", + "sifli_sf32lb_gpadc": "adc", "silabs_gecko_adc": "adc", "silabs_iadc": "adc", "silabs_siwx91x_adc": "adc", @@ -79,6 +86,18 @@ "ti_ads124s08": "adc", "ti_ads131m02": "adc", "ti_ads7052": "adc", + "ti_ads7950": "adc", + "ti_ads7951": "adc", + "ti_ads7952": "adc", + "ti_ads7953": "adc", + "ti_ads7954": "adc", + "ti_ads7955": "adc", + "ti_ads7956": "adc", + "ti_ads7957": "adc", + "ti_ads7958": "adc", + "ti_ads7959": "adc", + "ti_ads7960": "adc", + "ti_ads7961": "adc", "ti_am335x_adc": "adc", "ti_cc13xx_cc26xx_adc": "adc", "ti_cc23x0_adc": "adc", @@ -103,8 +122,10 @@ "cirrus_cs43l22": "audio", "dlg_da7212": "audio", "maxim_max98091": "audio", + "nordic_nrf_pdm": "audio", "nxp_dmic": "audio", "nxp_micfil": "audio", + "sifli_sf32lb_audcodec": "audio", "st_mpxxdtyy": "audio", "ti_pcm1681": "audio", "ti_tas6422dac": "audio", @@ -134,16 +155,22 @@ "st_stm32_bbram": "bbram", "zephyr_bbram_emul": "bbram", # + # biometrics + "adh_tech_gt5x": "biometrics", + "zephyr_biometrics_emul": "biometrics", + "zhiantec_zfm_x0": "biometrics", + # # bluetooth/hci "ambiq_bt_hci_spi": "bluetooth/hci", "espressif_esp32_bt_hci": "bluetooth/hci", - "infineon_cat1_bless_hci": "bluetooth/hci", + "infineon_bless_hci": "bluetooth/hci", + "infineon_bt_hci_uart": "bluetooth/hci", "infineon_cyw208xx_hci": "bluetooth/hci", - "infineon_cyw43xxx_bt_hci": "bluetooth/hci", "nxp_bt_hci_uart": "bluetooth/hci", "nxp_hci_ble": "bluetooth/hci", "renesas_bt_hci_da1453x": "bluetooth/hci", "renesas_bt_hci_da1469x": "bluetooth/hci", + "sifli_sf32lb_mailbox": "bluetooth/hci", "silabs_bt_hci_efr32": "bluetooth/hci", "silabs_siwx91x_bt_hci": "bluetooth/hci", "st_hci_spi_v1": "bluetooth/hci", @@ -195,11 +222,15 @@ "sbs_sbs_charger": "charger", "ti_bq24190": "charger", "ti_bq25180": "charger", + "ti_bq25186": "charger", + "ti_bq25188": "charger", "ti_bq25713": "charger", "x_powers_axp2101_charger": "charger", + "zephyr_charger_gpio": "charger", # # clock_control "adi_max32_gcr": "clock_control", + "alif_clockctrl": "clock_control", "ambiq_clkctrl": "clock_control", "arm_beetle_syscon": "clock_control", "arm_scmi_clock": "clock_control", @@ -210,12 +241,16 @@ "bflb_bl70x_clock_controller": "clock_control", "espressif_esp32_clock": "clock_control", "fixed_clock": "clock_control", + "focaltech_ft9001_cpm": "clock_control", "gd_gd32_cctl": "clock_control", "infineon_fixed_clock": "clock_control", "infineon_fixed_factor_clock": "clock_control", "infineon_peri_div": "clock_control", "intel_agilex5_clock": "clock_control", "ite_it51xxx_ecpm": "clock_control", + "microchip_pic32cm_jh_clock": "clock_control", + "microchip_pic32cm_pl_clock": "clock_control", + "microchip_pic32cz_ca_clock": "clock_control", "microchip_sam_d5x_e5x_clock": "clock_control", "microchip_sam_pmc": "clock_control", "microchip_sama7g5_sckc": "clock_control", @@ -246,6 +281,8 @@ "openisa_rv32m1_pcc": "clock_control", "pwm_clock": "clock_control", "raspberrypi_pico_clock_controller": "clock_control", + "realtek_bee_cctl": "clock_control", + "realtek_rts5817_clock": "clock_control", "realtek_rts5912_sccon": "clock_control", "renesas_r8a7795_cpg_mssr": "clock_control", "renesas_r8a779f0_cpg_mssr": "clock_control", @@ -267,13 +304,17 @@ "st_stm32_clock_mco": "clock_control", "st_stm32_clock_mux": "clock_control", "st_stm32f1_clock_mco": "clock_control", + "ti_k2g_sci_clk": "clock_control", "wch_rcc": "clock_control", # # comparator "ite_it51xxx_vcmp": "comparator", + "microchip_ac_g1_comparator": "comparator", "nordic_nrf_comp": "comparator", "nordic_nrf_lpcomp": "comparator", + "nxp_acomp": "comparator", "nxp_cmp": "comparator", + "nxp_hscmp": "comparator", "renesas_ra_acmphs": "comparator", "renesas_ra_lvd": "comparator", "renesas_rx_lvd": "comparator", @@ -297,16 +338,23 @@ "espressif_esp32_counter": "counter", "espressif_esp32_rtc_timer": "counter", "gd_gd32_timer": "counter", - "infineon_cat1_counter": "counter", + "infineon_counter": "counter", "infineon_tcpwm_counter": "counter", "ite_it51xxx_counter": "counter", "ite_it8xxx2_counter": "counter", "maxim_ds3231": "counter", "microchip_mcp7940n": "counter", + "microchip_sam_pit64b_counter": "counter", + "microchip_tc_g1_counter": "counter", + "microchip_tcc_g1_counter": "counter", "microchip_xec_timer": "counter", + "microcrystal_rv3032_counter": "counter", "neorv32_gptmr": "counter", "nordic_nrf_rtc": "counter", "nordic_nrf_timer": "counter", + "nuvoton_npck_lct": "counter", + "nuvoton_npcx_lct_v1": "counter", + "nuvoton_npcx_lct_v2": "counter", "nxp_ftm": "counter", "nxp_imx_epit": "counter", "nxp_imx_gpt": "counter", @@ -320,9 +368,12 @@ "nxp_mrt": "counter", "nxp_pit": "counter", "nxp_rtc": "counter", + "nxp_rtc_jdp": "counter", "nxp_s32_sys_timer": "counter", "nxp_stm": "counter", "nxp_tpm_timer": "counter", + "raspberrypi_pico_pit": "counter", + "raspberrypi_pico_pit_channel": "counter", "raspberrypi_pico_timer": "counter", "realtek_rts5912_slwtimer": "counter", "realtek_rts5912_timer": "counter", @@ -330,18 +381,22 @@ "renesas_rz_cmtw_counter": "counter", "renesas_rz_gtm_counter": "counter", "renesas_smartbond_timer": "counter", + "silabs_burtc_counter": "counter", "silabs_gecko_rtcc": "counter", + "silabs_timer_counter": "counter", "snps_dw_timers": "counter", "st_stm32_counter": "counter", "ti_cc23x0_lgpt": "counter", "ti_cc23x0_rtc": "counter", "ti_mspm0_timer_counter": "counter", "xlnx_xps_timer_1_00_a": "counter", - "zephyr_native_posix_counter": "counter", "zephyr_native_sim_counter": "counter", # # crc + "nxp_crc": "crc", + "nxp_lpc_crc": "crc", "renesas_ra_crc": "crc", + "sifli_sf32lb_crc": "crc", # # crypto "atmel_ataes132a": "crypto", @@ -351,10 +406,13 @@ "ite_it51xxx_sha": "crypto", "ite_it8xxx2_sha": "crypto", "ite_it8xxx2_sha_v2": "crypto", + "microchip_sha_g1_crypto": "crypto", "microchip_xec_symcr": "crypto", "nordic_nrf_ecb": "crypto", "nuvoton_npcx_sha": "crypto", "nxp_mcux_dcp": "crypto", + "nxp_s32_crypto_hse_mu": "crypto", + "raspberrypi_pico_sha256": "crypto", "realtek_rts5912_sha": "crypto", "renesas_smartbond_crypto": "crypto", "silabs_si32_aes": "crypto", @@ -388,6 +446,7 @@ "atmel_samd5x_dac": "dac", "espressif_esp32_dac": "dac", "gd_gd32_dac": "dac", + "microchip_dac_g1": "dac", "microchip_mcp4725": "dac", "microchip_mcp4728": "dac", "nxp_dac12": "dac", @@ -414,6 +473,9 @@ # dai/intel/ssp "intel_ssp_dai": "dai/intel/ssp", # + # dai/nxp/esai + "nxp_dai_esai": "dai/nxp/esai", + # # dai/nxp/micfil "nxp_dai_micfil": "dai/nxp/micfil", # @@ -462,7 +524,7 @@ "sitronix_st7796s": "display", "solomon_ssd1322": "display", "st_stm32_ltdc": "display", - "waveshare_7inch_dsi_lcd_c": "display", + "waveshare_dsi2dpi": "display", "zephyr_dummy_dc": "display", "zephyr_hub12": "display", "zephyr_sdl_dc": "display", @@ -471,6 +533,7 @@ "adi_max32_dma": "dma", "altr_msgdma": "dma", "andestech_atcdmacx00": "dma", + "arm_dma_pl330": "dma", "atmel_sam0_dmac": "dma", "atmel_sam_xdmac": "dma", "bflb_dma": "dma", @@ -479,7 +542,7 @@ "espressif_esp32_gdma": "dma", "gd_gd32_dma": "dma", "gd_gd32_dma_v1": "dma", - "infineon_cat1_dma": "dma", + "infineon_dma": "dma", "infineon_xmc4xxx_dma": "dma", "intel_adsp_gpdma": "dma", "intel_adsp_hda_host_in": "dma", @@ -488,8 +551,11 @@ "intel_adsp_hda_link_out": "dma", "intel_lpss": "dma", "intel_sedi_dma": "dma", + "microchip_dmac_g1_dma": "dma", "microchip_xec_dmac": "dma", "nuvoton_npcx_gdma": "dma", + "nxp_4ch_dma": "dma", + "nxp_edma": "dma", "nxp_lpc_dma": "dma", "nxp_mcux_edma": "dma", "nxp_pxp": "dma", @@ -525,6 +591,7 @@ # # edac "intel_ibecc": "edac", + "nxp_erm": "edac", "xlnx_zynqmp_ddrc_2_40a": "edac", # # eeprom @@ -547,7 +614,9 @@ "atmel_sam_trng": "entropy", "brcm_iproc_rng200": "entropy", "espressif_esp32_trng": "entropy", + "gd_gd32_trng": "entropy", "litex_prbs": "entropy", + "microchip_trng_g1_entropy": "entropy", "neorv32_trng": "entropy", "nordic_nrf_cracen_ctrdrbg": "entropy", "nordic_nrf_rng": "entropy", @@ -558,16 +627,20 @@ "nxp_kinetis_trng": "entropy", "nxp_lpc_rng": "entropy", "openisa_rv32m1_trng": "entropy", + "raspberrypi_pico_rng": "entropy", "renesas_smartbond_trng": "entropy", "sensry_sy1xx_trng": "entropy", + "sifli_sf32lb_trng": "entropy", "silabs_gecko_semailbox": "entropy", "silabs_gecko_trng": "entropy", "silabs_siwx91x_rng": "entropy", + "st_stm32_rng": "entropy", + "st_stm32_rng_noirq": "entropy", "telink_b91_trng": "entropy", "ti_cc13xx_cc26xx_trng": "entropy", + "ti_mspm0_trng": "entropy", "virtio_device4": "entropy", "zephyr_bt_hci_entropy": "entropy", - "zephyr_native_posix_rng": "entropy", "zephyr_native_sim_rng": "entropy", "zephyr_psa_crypto_rng": "entropy", # @@ -618,7 +691,11 @@ "virtio_net": "ethernet", "vnd_ethernet": "ethernet", "wiznet_w5500": "ethernet", + "wiznet_w6100": "ethernet", "xlnx_axi_ethernet_1_00_a": "ethernet", + "xlnx_gem": "ethernet", + "xlnx_xps_ethernetlite_1_00_a_mac": "ethernet", + "xlnx_xps_ethernetlite_3_00_a_mac": "ethernet", # # ethernet/dsa "microchip_ksz8463": "ethernet/dsa", @@ -637,6 +714,28 @@ "intel_eth_plat": "ethernet/intel", "intel_igc_mac": "ethernet/intel", # + # ethernet/mdio + "adi_adin2111_mdio": "ethernet/mdio", + "atmel_sam_mdio": "ethernet/mdio", + "espressif_esp32_mdio": "ethernet/mdio", + "infineon_xmc4xxx_mdio": "ethernet/mdio", + "intel_igc_mdio": "ethernet/mdio", + "litex_liteeth_mdio": "ethernet/mdio", + "microchip_lan865x_mdio": "ethernet/mdio", + "nxp_enet_mdio": "ethernet/mdio", + "nxp_enet_qos_mdio": "ethernet/mdio", + "nxp_imx_netc_emdio": "ethernet/mdio", + "nxp_s32_gmac_mdio": "ethernet/mdio", + "nxp_s32_netc_emdio": "ethernet/mdio", + "renesas_ra_mdio": "ethernet/mdio", + "sensry_sy1xx_mdio": "ethernet/mdio", + "snps_dwcxgmac_mdio": "ethernet/mdio", + "st_stm32_mdio": "ethernet/mdio", + "xlnx_axi_ethernet_1_00_a_mdio": "ethernet/mdio", + "xlnx_xps_ethernetlite_1_00_a_mdio": "ethernet/mdio", + "xlnx_xps_ethernetlite_3_00_a_mdio": "ethernet/mdio", + "zephyr_mdio_gpio": "ethernet/mdio", + # # ethernet/nxp_imx_netc "nxp_imx_netc_blk_ctrl": "ethernet/nxp_imx_netc", "nxp_imx_netc_psi": "ethernet/nxp_imx_netc", @@ -646,10 +745,13 @@ "adi_adin2111_phy": "ethernet/phy", "davicom_dm8806_phy": "ethernet/phy", "ethernet_phy": "ethernet/phy", + "ethernet_phy_fixed_link": "ethernet/phy", "microchip_ksz8081": "ethernet/phy", "microchip_ksz9131": "ethernet/phy", + "microchip_lan8742": "ethernet/phy", "microchip_t1s_phy": "ethernet/phy", "microchip_vsc8541": "ethernet/phy", + "motorcomm_yt8521": "ethernet/phy", "nxp_tja1103": "ethernet/phy", "nxp_tja11xx": "ethernet/phy", "qca_ar8031": "ethernet/phy", @@ -658,6 +760,7 @@ "ti_dp83867": "ethernet/phy", # # firmware/scmi + "arm_scmi": "firmware/scmi", "arm_scmi_shmem": "firmware/scmi", # # firmware/tisci @@ -673,20 +776,23 @@ "atmel_at45": "flash", "atmel_sam0_nvmctrl": "flash", "atmel_sam_flash_controller": "flash", + "bflb_flash_controller": "flash", "cdns_nand": "flash", "cdns_qspi_nor": "flash", "espressif_esp32_flash_controller": "flash", "gd_gd32_flash_controller": "flash", - "infineon_cat1_flash_controller": "flash", - "infineon_cat1_qspi_flash": "flash", + "infineon_flash_controller": "flash", + "infineon_qspi_flash": "flash", "infineon_xmc4xxx_flash_controller": "flash", "ite_it51xxx_manual_flash_1k": "flash", "ite_it8xxx2_flash_controller": "flash", "jedec_mspi_nor": "flash", + "jedec_spi_nand": "flash", "jedec_spi_nor": "flash", "microchip_nvmctrl_g1_flash": "flash", "mspi_atxp032": "flash", "mspi_is25xx0xx": "flash", + "netsol_s3axx04": "flash", "nordic_mram": "flash", "nordic_nrf51_flash_controller": "flash", "nordic_nrf52_flash_controller": "flash", @@ -699,6 +805,7 @@ "nuvoton_npcx_fiu_qspi": "flash", "nuvoton_numaker_fmc": "flash", "nuvoton_numaker_rmc": "flash", + "nxp_c40_flash_controller": "flash", "nxp_iap_fmc11": "flash", "nxp_iap_fmc54": "flash", "nxp_iap_fmc55": "flash", @@ -707,16 +814,19 @@ "nxp_imx_flexspi_mx25um51345g": "flash", "nxp_imx_flexspi_nor": "flash", "nxp_kinetis_ftfa": "flash", + "nxp_kinetis_ftfc": "flash", "nxp_kinetis_ftfe": "flash", "nxp_kinetis_ftfl": "flash", "nxp_msf1": "flash", "nxp_s32_qspi_hyperflash": "flash", "nxp_s32_qspi_nor": "flash", + "nxp_s32_xspi_hyperram": "flash", "nxp_xspi_nor": "flash", "openisa_rv32m1_ftfe": "flash", "raspberrypi_pico_flash_controller": "flash", "realtek_rts5912_flash_controller": "flash", "renesas_ra_flash_hp_controller": "flash", + "renesas_ra_flash_lp_controller": "flash", "renesas_ra_mram_controller": "flash", "renesas_ra_ospi_b_nor": "flash", "renesas_ra_qspi_nor": "flash", @@ -763,6 +873,9 @@ # fuel_gauge/composite "zephyr_fuel_gauge_composite": "fuel_gauge/composite", # + # fuel_gauge/hy4245 + "hycon_hy4245": "fuel_gauge/hy4245", + # # fuel_gauge/lc709203f "onnn_lc709203f": "fuel_gauge/lc709203f", # @@ -785,10 +898,12 @@ "quectel_lc26g": "gnss", "quectel_lc76g": "gnss", "quectel_lc86g": "gnss", - "u_blox_f9p": "gnss", - "u_blox_m8": "gnss", "zephyr_gnss_emul": "gnss", # + # gnss/u_blox + "u_blox_f9p": "gnss/u_blox", + "u_blox_m8": "gnss/u_blox", + # # gpio "adi_ad559x_gpio": "gpio", "adi_adp5585_gpio": "gpio", @@ -825,7 +940,7 @@ "fcs_fxl6408": "gpio", "gaisler_grgpio": "gpio", "gd_gd32_gpio": "gpio", - "infineon_cat1_gpio": "gpio", + "infineon_gpio": "gpio", "infineon_tle9104_gpio": "gpio", "infineon_xmc4xxx_gpio": "gpio", "intel_gpio": "gpio", @@ -845,12 +960,10 @@ "microchip_mcp23s09": "gpio", "microchip_mcp23s17": "gpio", "microchip_mcp23s18": "gpio", - "microchip_mec5_gpio": "gpio", "microchip_mpfs_gpio": "gpio", "microchip_port_g1_gpio": "gpio", "microchip_sam_pio4": "gpio", "microchip_xec_gpio": "gpio", - "microchip_xec_gpio_v2": "gpio", "neorv32_gpio": "gpio", "nordic_npm1300_gpio": "gpio", "nordic_npm1304_gpio": "gpio", @@ -886,11 +999,14 @@ "nxp_pcal9722": "gpio", "nxp_pcf857x": "gpio", "nxp_sc18im704_gpio": "gpio", + "nxp_sc18is606_gpio": "gpio", "nxp_siul2_gpio": "gpio", "openisa_rv32m1_gpio": "gpio", "quicklogic_eos_s3_gpio": "gpio", "raspberrypi_pico_gpio_port": "gpio", "raspberrypi_rp1_gpio": "gpio", + "realtek_ameba_gpio": "gpio", + "realtek_bee_gpio": "gpio", "realtek_rts5912_gpio": "gpio", "renesas_ra_gpio_ioport": "gpio", "renesas_rcar_gpio": "gpio", @@ -937,8 +1053,11 @@ "zephyr_gpio_emul": "gpio", "zephyr_gpio_emul_sdl": "gpio", # - # haptics - "ti_drv2605": "haptics", + # haptics/cirrus + "cirrus_cs40l5x": "haptics/cirrus", + # + # haptics/ti + "ti_drv2605": "haptics/ti", # # hdlc_rcp_if "nxp_hdlc_rcp_if": "hdlc_rcp_if", @@ -956,7 +1075,9 @@ "nxp_lpc_uid": "hwinfo", # # hwspinlock + "nxp_sema42": "hwspinlock", "sqn_hwspinlock": "hwspinlock", + "vnd_hwspinlock": "hwspinlock", # # i2c "adi_max32_i2c": "i2c", @@ -968,6 +1089,7 @@ "atmel_sam_i2c_twi": "i2c", "atmel_sam_i2c_twihs": "i2c", "atmel_sam_i2c_twim": "i2c", + "bflb_i2c": "i2c", "brcm_iproc_i2c": "i2c", "cdns_i2c": "i2c", "ene_kb1200_i2c": "i2c", @@ -976,8 +1098,7 @@ "gd_gd32_i2c": "i2c", "gpio_i2c": "i2c", "gpio_i2c_switch": "i2c", - "infineon_cat1_i2c": "i2c", - "infineon_cat1_i2c_pdl": "i2c", + "infineon_i2c": "i2c", "infineon_xmc4xxx_i2c": "i2c", "intel_sedi_i2c": "i2c", "ite_enhance_i2c": "i2c", @@ -986,8 +1107,11 @@ "litex_i2c": "i2c", "litex_litei2c": "i2c", "microchip_mpfs_i2c": "i2c", + "microchip_sercom_g1_i2c": "i2c", "microchip_xec_i2c": "i2c", "microchip_xec_i2c_v2": "i2c", + "nordic_nrf_twi": "i2c", + "nordic_nrf_twim": "i2c", "nordic_nrf_twis": "i2c", "nuvoton_npcx_i2c_ctrl": "i2c", "nuvoton_npcx_i2c_port": "i2c", @@ -1007,11 +1131,14 @@ "renesas_rx_i2c": "i2c", "renesas_rz_iic": "i2c", "renesas_rz_riic": "i2c", + "renesas_rza2m_riic": "i2c", "renesas_smartbond_i2c": "i2c", "sensry_sy1xx_i2c": "i2c", "sifive_i2c0": "i2c", + "sifli_sf32lb_i2c": "i2c", "silabs_gecko_i2c": "i2c", "silabs_i2c": "i2c", + "snps_designware_i2c": "i2c", "st_stm32_i2c_v1": "i2c", "st_stm32_i2c_v2": "i2c", "telink_b91_i2c": "i2c", @@ -1032,9 +1159,12 @@ "zephyr_i2c_target_eeprom": "i2c/target", # # i2s + "adi_max32_i2s": "i2s", "ambiq_i2s": "i2s", "atmel_sam_ssc": "i2s", "espressif_esp32_i2s": "i2s", + "infineon_i2s": "i2s", + "nordic_nrf_i2s": "i2s", "nxp_lpc_i2s": "i2s", "nxp_mcux_i2s": "i2s", "renesas_ra_i2s_ssie": "i2s", @@ -1075,12 +1205,14 @@ "adc_keys": "input", "analog_axis": "input", "arduino_modulino_buttons": "input", + "bflb_irx": "input", "chipsemi_chsc5x": "input", "chipsemi_chsc6x": "input", "cirque_pinnacle": "input", "cypress_cy8cmbr3xxx": "input", "espressif_esp32_touch": "input", "focaltech_ft5336": "input", + "focaltech_ft6146": "input", "futaba_sbus": "input", "goodix_gt911": "input", "gpio_kbd_matrix": "input", @@ -1096,6 +1228,7 @@ "nintendo_nunchuk": "input", "nuvoton_npcx_kbd": "input", "nxp_mcux_kpp": "input", + "parade_tma525b": "input", "pixart_pat912x": "input", "pixart_paw32xx": "input", "pixart_pmw3610": "input", @@ -1109,11 +1242,13 @@ "st_stm32_tsc": "input", "st_stmpe811": "input", "vishay_vs1838b": "input", + "wch_ch9350l": "input", "xptek_xpt2046": "input", "zephyr_input_sdl_touch": "input", "zephyr_native_linux_evdev": "input", # # interrupt_controller + "adi_max32_rv32_intc": "interrupt_controller", "arm_gic_v1": "interrupt_controller", "arm_gic_v2": "interrupt_controller", "arm_gic_v3": "interrupt_controller", @@ -1132,17 +1267,22 @@ "ite_it8xxx2_wuc": "interrupt_controller", "litex_vexriscv_intc0": "interrupt_controller", "mediatek_adsp_intc": "interrupt_controller", + "microchip_eic_g1_intc": "interrupt_controller", "microchip_xec_ecia": "interrupt_controller", "nuclei_eclic": "interrupt_controller", "nuvoton_npcx_miwu": "interrupt_controller", + "nxp_gint": "interrupt_controller", "nxp_irqsteer_intc": "interrupt_controller", "nxp_pint": "interrupt_controller", "nxp_s32_wkpu": "interrupt_controller", "nxp_siul2_eirq": "interrupt_controller", "openisa_rv32m1_intmux": "interrupt_controller", + "renesas_rx_grp_intc": "interrupt_controller", "renesas_rx_icu": "interrupt_controller", "renesas_rz_ext_irq": "interrupt_controller", + "renesas_rz_tint": "interrupt_controller", "riscv_clic": "interrupt_controller", + "riscv_imsic": "interrupt_controller", "shared_irq": "interrupt_controller", "sifive_plic_1_0_0": "interrupt_controller", "snps_arcv2_intc": "interrupt_controller", @@ -1182,6 +1322,7 @@ "nxp_pca9633": "led", "onnn_ncp5623": "led", "pwm_leds": "led", + "sct_sct2024": "led", "ti_lp3943": "led", "ti_lp5009": "led", "ti_lp5012": "led", @@ -1209,9 +1350,14 @@ # lora "reyax_rylrxxx": "lora", # - # lora/loramac_node - "semtech_sx1272": "lora/loramac_node", - "semtech_sx1276": "lora/loramac_node", + # lora/loramac-node + "semtech_sx1272": "lora/loramac-node", + "semtech_sx1276": "lora/loramac-node", + # + # lora/native/sx126x + "semtech_sx1261": "lora/native/sx126x", + "semtech_sx1262": "lora/native/sx126x", + "st_stm32wl_subghz_radio": "lora/native/sx126x", # # mbox "andestech_mbox_plic_sw": "mbox", @@ -1228,31 +1374,13 @@ "nxp_mbox_imx_mu": "mbox", "nxp_mbox_mailbox": "mbox", "nxp_s32_mru": "mbox", + "raspberrypi_pico_mbox": "mbox", "renesas_ra_ipc_mbox": "mbox", "renesas_rz_mhu_mbox": "mbox", "st_mbox_stm32_hsem": "mbox", "ti_omap_mailbox": "mbox", "ti_secure_proxy": "mbox", - # - # mdio - "adi_adin2111_mdio": "mdio", - "atmel_sam_mdio": "mdio", - "espressif_esp32_mdio": "mdio", - "infineon_xmc4xxx_mdio": "mdio", - "intel_igc_mdio": "mdio", - "litex_liteeth_mdio": "mdio", - "microchip_lan865x_mdio": "mdio", - "nxp_enet_mdio": "mdio", - "nxp_enet_qos_mdio": "mdio", - "nxp_imx_netc_emdio": "mdio", - "nxp_s32_gmac_mdio": "mdio", - "nxp_s32_netc_emdio": "mdio", - "renesas_ra_mdio": "mdio", - "sensry_sy1xx_mdio": "mdio", - "snps_dwcxgmac_mdio": "mdio", - "st_stm32_mdio": "mdio", - "xlnx_axi_ethernet_1_00_a_mdio": "mdio", - "zephyr_mdio_gpio": "mdio", + "xlnx_mbox_versal_ipi_mailbox": "mbox", # # memc "adi_max32_hpb": "memc", @@ -1261,9 +1389,11 @@ "mspi_aps6404l": "memc", "mspi_aps_z8": "memc", "nxp_imx_flexspi": "memc", + "nxp_imx_flexspi_is66wvs8m8": "memc", "nxp_imx_flexspi_s27ks0641": "memc", "nxp_imx_flexspi_w956a8mbya": "memc", "nxp_s32_qspi": "memc", + "nxp_s32_xspi": "memc", "nxp_xspi": "memc", "nxp_xspi_psram": "memc", "renesas_ra_sdram": "memc", @@ -1290,6 +1420,7 @@ "maxim_max20335": "mfd", "maxim_max31790": "mfd", "microchip_sam_flexcom": "mfd", + "microcrystal_rv3032_mfd": "mfd", "motorola_mc146818_mfd": "mfd", "nordic_npm1300": "mfd", "nordic_npm1304": "mfd", @@ -1299,13 +1430,18 @@ "nxp_lp_flexcomm": "mfd", "nxp_pca9422": "mfd", "nxp_pf1550": "mfd", + "nxp_sc18is606": "mfd", "rohm_bd8lb600fs": "mfd", # # mipi_dbi + "bflb_dbi": "mipi_dbi", + "espressif_esp32_lcd_cam_mipi_dbi": "mipi_dbi", "nxp_lcdic": "mipi_dbi", "nxp_mipi_dbi_dcnano_lcdif": "mipi_dbi", "nxp_mipi_dbi_flexio_lcdif": "mipi_dbi", + "raspberrypi_pico_mipi_dbi_pio": "mipi_dbi", "renesas_smartbond_mipi_dbi": "mipi_dbi", + "sifli_sf32lb_lcdc_mipi_dbi": "mipi_dbi", "st_stm32_fmc_mipi_dbi": "mipi_dbi", "zephyr_mipi_dbi_bitbang": "mipi_dbi", "zephyr_mipi_dbi_spi": "mipi_dbi", @@ -1370,6 +1506,7 @@ "intel_timeaware_gpio": "misc/timeaware_gpio", # # mm + "intel_adsp_mtl_tlb": "mm", "intel_adsp_tlb": "mm", # # modem @@ -1381,6 +1518,7 @@ "quectel_eg800q": "modem", "simcom_a76xx": "modem", "sqn_gm02s": "modem", + "st_st87mxx": "modem", "telit_me310g1": "modem", "telit_me910g1": "modem", "u_blox_lara_r6": "modem", @@ -1390,8 +1528,10 @@ # # modem/hl78xx "swir_hl7800": "modem/hl78xx", + "swir_hl7800_gnss": "modem/hl78xx", "swir_hl7800_offload": "modem/hl78xx", "swir_hl7812": "modem/hl78xx", + "swir_hl7812_gnss": "modem/hl78xx", "swir_hl7812_offload": "modem/hl78xx", # # modem/simcom/sim7080 @@ -1400,11 +1540,21 @@ # mspi "ambiq_mspi_controller": "mspi", "snps_designware_ssi": "mspi", + "st_stm32_ospi_controller": "mspi", + "st_stm32_qspi_controller": "mspi", + "st_stm32_xspi_controller": "mspi", "zephyr_mspi_emul_controller": "mspi", # # opamp "nxp_opamp": "opamp", "nxp_opamp_fast": "opamp", + "st_stm32_opamp": "opamp", + # + # otp + "nxp_ocotp": "otp", + "sifli_sf32lb_efuse": "otp", + "st_stm32_bsec": "otp", + "zephyr_otp_emul": "otp", # # pcie/controller "brcm_brcmstb_pcie": "pcie/controller", @@ -1423,11 +1573,12 @@ "nuvoton_npcx_peci": "peci", # # pinctrl + "alif_pinctrl": "pinctrl", + "brcm_bcm2711_pinctrl": "pinctrl", "ene_kb106x_pinctrl": "pinctrl", "ene_kb1200_pinctrl": "pinctrl", "infineon_xmc4xxx_pinctrl": "pinctrl", "ite_it8xxx2_pinctrl_func": "pinctrl", - "microchip_mec5_pinctrl": "pinctrl", "microchip_xec_pinctrl": "pinctrl", "nuvoton_numaker_pinctrl": "pinctrl", "nuvoton_numicro_pinctrl": "pinctrl", @@ -1461,10 +1612,12 @@ "renesas_rzt2m_pinctrl": "pinctrl/renesas/rz", # # pm_cpu_ops + "arm_fvp_pwrc": "pm_cpu_ops", "arm_psci_0_2": "pm_cpu_ops", "arm_psci_1_1": "pm_cpu_ops", # # power_domain + "arm_scmi_power_domain": "power_domain", "intel_adsp_power_domain": "power_domain", "nordic_nrfs_gdpwr": "power_domain", "nordic_nrfs_swext": "power_domain", @@ -1476,6 +1629,7 @@ "ti_sci_pm_domain": "power_domain", # # ps2 + "ite_it51xxx_ps2": "ps2", "microchip_xec_ps2": "ps2", "nuvoton_npcx_ps2_channel": "ps2", "nuvoton_npcx_ps2_ctrl": "ps2", @@ -1494,6 +1648,8 @@ "atmel_sam0_tc_pwm": "pwm", "atmel_sam0_tcc_pwm": "pwm", "atmel_sam_pwm": "pwm", + "bflb_pwm_1": "pwm", + "bflb_pwm_2": "pwm", "ene_kb106x_pwm": "pwm", "ene_kb1200_pwm": "pwm", "espressif_esp32_ledc": "pwm", @@ -1509,10 +1665,12 @@ "ite_it8xxx2_pwm": "pwm", "litex_pwm": "pwm", "maxim_max31790_pwm": "pwm", + "microchip_tc_g1_pwm": "pwm", "microchip_tcc_g1_pwm": "pwm", "microchip_xec_pwm": "pwm", "microchip_xec_pwmbbled": "pwm", "neorv32_pwm": "pwm", + "nordic_nrf_pwm": "pwm", "nordic_nrf_sw_pwm": "pwm", "nuvoton_npcx_pwm": "pwm", "nuvoton_numaker_pwm": "pwm", @@ -1534,7 +1692,10 @@ "renesas_rx_mtu_pwm": "pwm", "renesas_rz_gpt_pwm": "pwm", "renesas_rz_mtu_pwm": "pwm", + "renesas_rza2m_gpt_pwm": "pwm", "sifive_pwm0": "pwm", + "sifli_sf32lb_atim_pwm": "pwm", + "sifli_sf32lb_gpt_pwm": "pwm", "silabs_gecko_pwm": "pwm", "silabs_letimer_pwm": "pwm", "silabs_siwx91x_pwm": "pwm", @@ -1568,10 +1729,13 @@ "regulator_fixed": "regulator", "regulator_gpio": "regulator", "renesas_smartbond_regulator": "regulator", + "st_stm32_vrefbuf": "regulator", + "ti_tps55287": "regulator", "zephyr_fake_regulator": "regulator", # # reset "aspeed_ast10x0_reset": "reset", + "focaltech_ft9001_cpm_rctl": "reset", "gd_gd32_rctl": "reset", "intel_socfpga_reset": "reset", "microchip_mpfs_reset": "reset", @@ -1582,6 +1746,7 @@ "nxp_mrcc_reset": "reset", "nxp_rstctl": "reset", "raspberrypi_pico_reset": "reset", + "realtek_rts5817_reset": "reset", "reset_mmio": "reset", "sifli_sf32lb_rcc_rctl": "reset", "st_stm32_rcc_rctl": "reset", @@ -1593,15 +1758,19 @@ "zephyr_retained_reg": "retained_mem", # # rtc + "adi_max31331": "rtc", "ambiq_am1805": "rtc", "ambiq_rtc": "rtc", "atmel_sam_rtc": "rtc", "epson_rx8130ce_rtc": "rtc", - "infineon_cat1_rtc": "rtc", + "infineon_rtc": "rtc", "infineon_xmc4xxx_rtc": "rtc", + "maxim_ds1302": "rtc", "maxim_ds1307": "rtc", "maxim_ds1337": "rtc", "maxim_ds3231_rtc": "rtc", + "microchip_rtc_g1": "rtc", + "microchip_rtc_g2": "rtc", "microcrystal_rv3028": "rtc", "microcrystal_rv3032": "rtc", "microcrystal_rv8803": "rtc", @@ -1617,6 +1786,7 @@ "realtek_rts5912_rtc": "rtc", "renesas_ra_rtc": "rtc", "renesas_smartbond_rtc": "rtc", + "sifli_sf32lb_rtc": "rtc", "silabs_siwx91x_rtc": "rtc", "st_stm32_rtc": "rtc", "ti_bq32002": "rtc", @@ -1631,7 +1801,7 @@ "atmel_sam_hsmci": "sdhc", "cdns_sdhc": "sdhc", "espressif_esp32_sdhc_slot": "sdhc", - "infineon_cat1_sdhc_sdio": "sdhc", + "infineon_sdhc_sdio": "sdhc", "intel_emmc_host": "sdhc", "microchip_sama7g5_sdmmc": "sdhc", "nxp_imx_usdhc": "sdhc", @@ -1648,6 +1818,9 @@ # sensor/adi/ad2s1210 "adi_ad2s1210": "sensor/adi/ad2s1210", # + # sensor/adi/ade7978 + "adi_ade7978": "sensor/adi/ade7978", + # # sensor/adi/adltc2990 "adi_adltc2990": "sensor/adi/adltc2990", # @@ -1670,6 +1843,9 @@ # sensor/adi/adxl372 "adi_adxl372": "sensor/adi/adxl372", # + # sensor/adi/max30210 + "adi_max30210": "sensor/adi/max30210", + # # sensor/adi/max32664c "maxim_max32664c": "sensor/adi/max32664c", # @@ -1682,6 +1858,9 @@ # sensor/amg88xx "panasonic_amg88xx": "sensor/amg88xx", # + # sensor/ams/ams_as5048 + "ams_as5048": "sensor/ams/ams_as5048", + # # sensor/ams/ams_as5600 "ams_as5600": "sensor/ams/ams_as5600", # @@ -1744,6 +1923,12 @@ # sensor/bosch/bmc150_magn "bosch_bmc150_magn": "sensor/bosch/bmc150_magn", # + # sensor/bosch/bme280 + "bosch_bme280": "sensor/bosch/bme280", + # + # sensor/bosch/bme680 + "bosch_bme680": "sensor/bosch/bme680", + # # sensor/bosch/bmg160 "bosch_bmg160": "sensor/bosch/bmg160", # @@ -1760,6 +1945,12 @@ # sensor/bosch/bmi323 "bosch_bmi323": "sensor/bosch/bmi323", # + # sensor/bosch/bmm150 + "bosch_bmm150": "sensor/bosch/bmm150", + # + # sensor/bosch/bmm350 + "bosch_bmm350": "sensor/bosch/bmm350", + # # sensor/bosch/bmp180 "bosch_bmp180": "sensor/bosch/bmp180", # @@ -1767,6 +1958,9 @@ "bosch_bmp388": "sensor/bosch/bmp388", "bosch_bmp390": "sensor/bosch/bmp388", # + # sensor/bosch/bmp581 + "bosch_bmp581": "sensor/bosch/bmp581", + # # sensor/broadcom/afbr_s50 "brcm_afbr_s50": "sensor/broadcom/afbr_s50", # @@ -1825,6 +2019,9 @@ # sensor/infineon/xmc4xxx_temp "infineon_xmc4xxx_temp": "sensor/infineon/xmc4xxx_temp", # + # sensor/ist8310 + "isentek_ist8310": "sensor/ist8310", + # # sensor/ite/ite_tach_it51xxx "ite_it51xxx_tach": "sensor/ite/ite_tach_it51xxx", # @@ -1837,9 +2034,6 @@ # sensor/jedec/jc42 "jedec_jc_42_4_temp": "sensor/jedec/jc42", # - # sensor/liteon/ltr329 - "liteon_ltr329": "sensor/liteon/ltr329", - # # sensor/liteon/ltrf216a "liteon_ltrf216a": "sensor/liteon/ltrf216a", # @@ -1875,6 +2069,9 @@ # sensor/maxim/max31855 "maxim_max31855": "sensor/maxim/max31855", # + # sensor/maxim/max31865 + "maxim_max31865": "sensor/maxim/max31865", + # # sensor/maxim/max31875 "maxim_max31875": "sensor/maxim/max31875", # @@ -1900,6 +2097,9 @@ # sensor/memsic/mc3419 "memsic_mc3419": "sensor/memsic/mc3419", # + # sensor/memsic/mmc56x3 + "memsic_mmc56x3": "sensor/memsic/mmc56x3", + # # sensor/mhz19b "winsen_mhz19b": "sensor/mhz19b", # @@ -1945,6 +2145,9 @@ # sensor/nuvoton/nuvoton_adc_cmp_npcx "nuvoton_adc_cmp": "sensor/nuvoton/nuvoton_adc_cmp_npcx", # + # sensor/nuvoton/nuvoton_adc_v2t_npcx + "nuvoton_npcx_adc_v2t": "sensor/nuvoton/nuvoton_adc_v2t_npcx", + # # sensor/nuvoton/nuvoton_tach_npcx "nuvoton_npcx_tach": "sensor/nuvoton/nuvoton_tach_npcx", # @@ -1975,6 +2178,9 @@ # sensor/nxp/nxp_tempmon "nxp_tempmon": "sensor/nxp/nxp_tempmon", # + # sensor/nxp/nxp_tempsense + "nxp_tempsense": "sensor/nxp/nxp_tempsense", + # # sensor/nxp/nxp_tmpsns "nxp_tmpsns": "sensor/nxp/nxp_tmpsns", # @@ -2045,6 +2251,9 @@ # sensor/rpi_pico_temp "raspberrypi_pico_temp": "sensor/rpi_pico_temp", # + # sensor/rv3032_temp + "microcrystal_rv3032_temp": "sensor/rv3032_temp", + # # sensor/s11059 "hamamatsu_s11059": "sensor/s11059", # @@ -2074,6 +2283,9 @@ # sensor/sensirion/sts4x "sensirion_sts4x": "sensor/sensirion/sts4x", # + # sensor/sifli/sf32lb_tsen + "sifli_sf32lb_tsen": "sensor/sifli/sf32lb_tsen", + # # sensor/silabs/si7006 "sensirion_sht21": "sensor/silabs/si7006", "silabs_si7006": "sensor/silabs/si7006", @@ -2175,6 +2387,14 @@ # sensor/st/lsm6dsv16x "DT_DRV_COMPAT_LSM6DSV16X": "sensor/st/lsm6dsv16x", "DT_DRV_COMPAT_LSM6DSV32X": "sensor/st/lsm6dsv16x", + "st_lsm6dsv16x": "sensor/st/lsm6dsv16x", + "st_lsm6dsv32x": "sensor/st/lsm6dsv16x", + # + # sensor/st/lsm6dsvxxx + "st_ism6hg256x": "sensor/st/lsm6dsvxxx", + "st_lsm6dsv320x": "sensor/st/lsm6dsvxxx", + "st_lsm6dsv80x": "sensor/st/lsm6dsvxxx", + "st_lsm6dsvxxx": "sensor/st/lsm6dsvxxx", # # sensor/st/lsm9ds0_gyro "st_lsm9ds0_gyro": "sensor/st/lsm9ds0_gyro", @@ -2239,7 +2459,11 @@ "invensense_icm42670s": "sensor/tdk/icm42x70", # # sensor/tdk/icm45686 + "invensense_icm45605": "sensor/tdk/icm45686", + "invensense_icm45605s": "sensor/tdk/icm45686", "invensense_icm45686": "sensor/tdk/icm45686", + "invensense_icm45686s": "sensor/tdk/icm45686", + "invensense_icm45688p": "sensor/tdk/icm45686", # # sensor/tdk/icp101xx "invensense_icp101xx": "sensor/tdk/icp101xx", @@ -2269,6 +2493,7 @@ "ti_ina226": "sensor/ti/ina2xx", "ti_ina228": "sensor/ti/ina2xx", "ti_ina230": "sensor/ti/ina2xx", + "ti_ina232": "sensor/ti/ina2xx", "ti_ina236": "sensor/ti/ina2xx", "ti_ina237": "sensor/ti/ina2xx", # @@ -2281,8 +2506,10 @@ # sensor/ti/lm95234 "national_lm95234": "sensor/ti/lm95234", # - # sensor/ti/opt3001 - "ti_opt3001": "sensor/ti/opt3001", + # sensor/ti/opt300x + "ti_opt3001": "sensor/ti/opt300x", + "ti_opt3004": "sensor/ti/opt300x", + "ti_opt300x": "sensor/ti/opt300x", # # sensor/ti/ti_hdc "ti_hdc": "sensor/ti/ti_hdc", @@ -2301,6 +2528,7 @@ # # sensor/ti/tmp108 "ams_as6212": "sensor/ti/tmp108", + "ams_as6221": "sensor/ti/tmp108", "ti_tmp108": "sensor/ti/tmp108", # # sensor/ti/tmp112 @@ -2388,10 +2616,10 @@ "espressif_esp32_lpuart": "serial", "espressif_esp32_uart": "serial", "espressif_esp32_usb_serial": "serial", + "focaltech_ft9001_usart": "serial", "gaisler_apbuart": "serial", "gd_gd32_usart": "serial", - "infineon_cat1_uart": "serial", - "infineon_cat1_uart_pdl": "serial", + "infineon_uart": "serial", "infineon_xmc4xxx_uart": "serial", "intel_lw_uart": "serial", "intel_sedi_uart": "serial", @@ -2400,7 +2628,6 @@ "litex_uart": "serial", "lowrisc_opentitan_uart": "serial", "microchip_coreuart": "serial", - "microchip_mec5_uart": "serial", "microchip_sercom_g1_uart": "serial", "microchip_xec_uart": "serial", "neorv32_uart": "serial", @@ -2420,6 +2647,8 @@ "openisa_rv32m1_lpuart": "serial", "quicklogic_usbserialport_s3b": "serial", "raspberrypi_pico_uart_pio": "serial", + "realtek_ameba_loguart": "serial", + "realtek_bee_uart": "serial", "realtek_rts5912_uart": "serial", "renesas_ra8_uart_sci_b": "serial", "renesas_ra_sci_uart": "serial", @@ -2458,7 +2687,6 @@ "xen_hvc_consoleio": "serial", "xlnx_xps_uartlite_1_00_a": "serial", "xlnx_xuartps": "serial", - "zephyr_native_posix_uart": "serial", "zephyr_native_pty_uart": "serial", "zephyr_native_tty_uart": "serial", "zephyr_nus_uart": "serial", @@ -2482,14 +2710,14 @@ "arm_pl022": "spi", "atmel_sam0_spi": "spi", "atmel_sam_spi": "spi", + "bflb_spi": "spi", "cdns_spi": "spi", "cypress_psoc6_spi": "spi", "egis_et171_spi": "spi", "espressif_esp32_spi": "spi", "gaisler_spimctrl": "spi", "gd_gd32_spi": "spi", - "infineon_cat1_spi": "spi", - "infineon_cat1_spi_pdl": "spi", + "infineon_spi": "spi", "infineon_xmc4xxx_spi": "spi", "intel_penwell_spi": "spi", "intel_sedi_spi": "spi", @@ -2498,11 +2726,13 @@ "litex_spi": "spi", "litex_spi_litespi": "spi", "lowrisc_opentitan_spi": "spi", - "microchip_mec5_qspi": "spi", "microchip_mpfs_qspi": "spi", "microchip_mpfs_spi": "spi", + "microchip_sercom_g1_spi": "spi", "microchip_xec_qmspi": "spi", "microchip_xec_qmspi_ldma": "spi", + "nordic_nrf_spi": "spi", + "nordic_nrf_spim": "spi", "nuvoton_npcx_spip": "spi", "nuvoton_numaker_spi": "spi", "nxp_dspi": "spi", @@ -2514,13 +2744,18 @@ "opencores_spi_simple": "spi", "openisa_rv32m1_lpspi": "spi", "raspberrypi_pico_spi_pio": "spi", + "realtek_rts5912_spi": "spi", "renesas_ra8_spi_b": "spi", "renesas_ra_spi": "spi", + "renesas_ra_spi_sci": "spi", + "renesas_ra_spi_sci_b": "spi", "renesas_rx_rspi": "spi", "renesas_rz_rspi": "spi", "renesas_rz_spi": "spi", "renesas_smartbond_spi": "spi", + "sensry_sy1xx_spi": "spi", "sifive_spi0": "spi", + "sifli_sf32lb_spi": "spi", "silabs_eusart_spi": "spi", "silabs_gspi": "spi", "silabs_usart_spi": "spi", @@ -2540,16 +2775,29 @@ "nxp_lpspi": "spi/spi_nxp_lpspi", # # stepper - "zephyr_fake_stepper": "stepper", - "zephyr_h_bridge_stepper": "stepper", + "zephyr_fake_stepper_ctrl": "stepper", + "zephyr_fake_stepper_driver": "stepper", + # + # stepper/adi_tmc/tmc22xx + "adi_tmc2209": "stepper/adi_tmc/tmc22xx", + # + # stepper/adi_tmc/tmc50xx + "adi_tmc50xx": "stepper/adi_tmc/tmc50xx", + "adi_tmc50xx_stepper_ctrl": "stepper/adi_tmc/tmc50xx", + "adi_tmc50xx_stepper_driver": "stepper/adi_tmc/tmc50xx", # - # stepper/adi_tmc - "adi_tmc2209": "stepper/adi_tmc", - "adi_tmc50xx": "stepper/adi_tmc", + # stepper/adi_tmc/tmc51xx + "adi_tmc51xx": "stepper/adi_tmc/tmc51xx", + "adi_tmc51xx_stepper_ctrl": "stepper/adi_tmc/tmc51xx", + "adi_tmc51xx_stepper_driver": "stepper/adi_tmc/tmc51xx", # # stepper/allegro "allegro_a4979": "stepper/allegro", # + # stepper/gpio_stepper + "zephyr_gpio_step_dir_stepper_ctrl": "stepper/gpio_stepper", + "zephyr_h_bridge_stepper_ctrl": "stepper/gpio_stepper", + # # stepper/ti "ti_drv84xx": "stepper/ti", # @@ -2561,10 +2809,11 @@ "linaro_optee_tz": "tee/optee", # # timer + "adi_max32_rv32_sys_timer": "timer", "ambiq_stimer": "timer", "atmel_sam0_rtc": "timer", "gaisler_gptimer": "timer", - "infineon_cat1_lp_timer": "timer", + "infineon_lp_timer": "timer", "intel_adsp_timer": "timer", "intel_hpet": "timer", "ite_it51xxx_timer": "timer", @@ -2597,6 +2846,10 @@ # usb/bc12 "diodes_pi3usb9201": "usb/bc12", # + # usb/common/stm32 + "*/": "usb/common/stm32", + "st_stm32u5_otghs_phy": "usb/common/stm32", + # # usb/device "atmel_sam_usbc": "usb/device", "atmel_sam_usbhs": "usb/device", @@ -2607,6 +2860,7 @@ "atmel_sam0_usb": "usb/udc", "ite_it82xx2_usb": "usb/udc", "nordic_nrf_usbd": "usb/udc", + "nuvoton_numaker_hsusbd": "usb/udc", "nuvoton_numaker_usbd": "usb/udc", "nxp_ehci": "usb/udc", "nxp_kinetis_usbd": "usb/udc", @@ -2647,15 +2901,18 @@ # # video "aptina_mt9m114": "video", - "espressif_esp32_lcd_cam": "video", + "espressif_esp32_lcd_cam_dvp": "video", "galaxycore_gc2145": "video", "himax_hm01b0": "video", + "himax_hm0360": "video", "nxp_imx_csi": "video", "nxp_mipi_csi2rx": "video", "nxp_video_smartdma": "video", "ovti_ov2640": "video", "ovti_ov5640": "video", + "ovti_ov5642": "video", "ovti_ov7670": "video", + "ovti_ov7675": "video", "ovti_ov7725": "video", "ovti_ov9655": "video", "renesas_ra_ceu": "video", @@ -2688,19 +2945,21 @@ # # watchdog "adi_max32_watchdog": "watchdog", + "adi_max42500_watchdog": "watchdog", "ambiq_watchdog": "watchdog", "andestech_atcwdt200": "watchdog", "arm_cmsdk_watchdog": "watchdog", "atmel_sam0_watchdog": "watchdog", "atmel_sam4l_watchdog": "watchdog", "atmel_sam_watchdog": "watchdog", + "bflb_wdt": "watchdog", "ene_kb106x_watchdog": "watchdog", "ene_kb1200_watchdog": "watchdog", "espressif_esp32_watchdog": "watchdog", "espressif_esp32_xt_wdt": "watchdog", "gd_gd32_fwdgt": "watchdog", "gd_gd32_wwdgt": "watchdog", - "infineon_cat1_watchdog": "watchdog", + "infineon_watchdog": "watchdog", "infineon_xmc4xxx_watchdog": "watchdog", "intel_adsp_watchdog": "watchdog", "intel_tco_wdt": "watchdog", @@ -2708,11 +2967,13 @@ "ite_it8xxx2_watchdog": "watchdog", "litex_watchdog": "watchdog", "lowrisc_opentitan_aontimer": "watchdog", + "microchip_wdt_g1": "watchdog", "microchip_xec_watchdog": "watchdog", "nordic_npm1300_wdt": "watchdog", "nordic_npm1304_wdt": "watchdog", "nordic_npm2100_wdt": "watchdog", "nordic_npm6001_wdt": "watchdog", + "nordic_nrf_wdt": "watchdog", "nuvoton_npcx_watchdog": "watchdog", "nuvoton_numaker_wwdt": "watchdog", "nxp_cop": "watchdog", @@ -2725,6 +2986,7 @@ "nxp_s32_swt": "watchdog", "nxp_wdog32": "watchdog", "raspberrypi_pico_watchdog": "watchdog", + "realtek_rts5817_watchdog": "watchdog", "realtek_rts5912_watchdog": "watchdog", "renesas_rx_iwdt": "watchdog", "renesas_rz_wdt": "watchdog", @@ -2751,10 +3013,16 @@ # wifi/esp_at "espressif_esp_at": "wifi/esp_at", # + # wifi/esp_hosted + "espressif_esp_hosted": "wifi/esp_hosted", + # # wifi/eswifi "inventek_eswifi": "wifi/eswifi", "inventek_eswifi_uart": "wifi/eswifi", # + # wifi/infineon + "infineon_airoc_wifi": "wifi/infineon", + # # wifi/nrf_wifi/off_raw_tx/src "nordic_wlan": "wifi/nrf_wifi/off_raw_tx/src", # diff --git a/ports/zephyr-cp/cptools/gen_compat2driver.py b/ports/zephyr-cp/cptools/gen_compat2driver.py index 1e529072dab4a..ffe8da185969f 100644 --- a/ports/zephyr-cp/cptools/gen_compat2driver.py +++ b/ports/zephyr-cp/cptools/gen_compat2driver.py @@ -3,7 +3,7 @@ mapping = {} drivers = pathlib.Path("zephyr/drivers") -for p in drivers.glob("**/*.c"): +for p in drivers.glob("**/*.[ch]"): for line in p.open(): if line.startswith("#define DT_DRV_COMPAT"): compat = line.rsplit(None, 1)[-1].strip() diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index c123d90ce7816..5000221d5c005 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -245,6 +245,34 @@ "D1", "D0", ], + "raspberrypi,pico-header": [ + "GP0", + "GP1", + "GP2", + "GP3", + "GP4", + "GP5", + "GP6", + "GP7", + "GP8", + "GP9", + "GP10", + "GP11", + "GP12", + "GP13", + "GP14", + "GP15", + "GP16", + "GP17", + "GP18", + "GP19", + "GP20", + "GP21", + "GP22", + ["GP26_A0", "GP26", "A0"], + ["GP27_A1", "GP27", "A1"], + ["GP28_A2", "GP28", "A2"], + ], } EXCEPTIONAL_DRIVERS = ["entropy", "gpio", "led"] @@ -583,7 +611,11 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa num = int.from_bytes(gpio_map.value[offset + 4 : offset + 8], "big") if (label, num) not in board_names: board_names[(label, num)] = [] - board_names[(label, num)].append(connector_pins[i]) + pin_entry = connector_pins[i] + if isinstance(pin_entry, list): + board_names[(label, num)].extend(pin_entry) + else: + board_names[(label, num)].append(pin_entry) i += 1 if "gpio-leds" in compatible: for led in node.nodes: diff --git a/ports/zephyr-cp/mpconfigport.h b/ports/zephyr-cp/mpconfigport.h index 491b5293e2ebc..23bb0b55b6f1a 100644 --- a/ports/zephyr-cp/mpconfigport.h +++ b/ports/zephyr-cp/mpconfigport.h @@ -20,6 +20,8 @@ // Disable native _Float16 handling for host builds. #define MICROPY_FLOAT_USE_NATIVE_FLT16 (0) +#define MICROPY_NLR_THUMB_USE_LONG_JUMP (1) + //////////////////////////////////////////////////////////////////////////////////////////////////// // This also includes mpconfigboard.h. diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index c40475177e000..847f864f0bb26 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -25,6 +25,7 @@ #include "lib/tlsf/tlsf.h" #include +#include #if defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) #include "perfetto_encoder.h" @@ -130,6 +131,9 @@ static void _tick_function(struct k_timer *timer_id) { } safe_mode_t port_init(void) { + // We run CircuitPython at the lowest priority (just higher than idle.) + // This allows networking and USB to preempt us. + k_thread_priority_set(k_current_get(), CONFIG_NUM_PREEMPT_PRIORITIES - 1); k_timer_init(&tick_timer, _tick_function, NULL); perfetto_emit_circuitpython_tracks(); return SAFE_MODE_NONE; From d07ee5b970eba8a604495a7db1fbda99d2d42648 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Thu, 18 Dec 2025 11:09:18 -0800 Subject: [PATCH 480/517] Add audiobusio.I2SOut() support to Zephyr Relies on added SDL audio emulation. --- locale/circuitpython.pot | 1 + ports/zephyr-cp/Makefile | 2 +- ports/zephyr-cp/background.c | 19 +- .../autogen_board_info.toml | 2 +- .../boards/frdm_mcxn947_mcxn947_cpu0.conf | 1 + .../boards/frdm_rw612_rw612_cpu0.overlay | 12 + .../boards/mimxrt1170_evk_mimxrt1176_cm7.conf | 1 + .../mimxrt1170_evk_mimxrt1176_cm7.overlay | 4 + .../native/native_sim/autogen_board_info.toml | 18 +- .../nrf5340bsim/autogen_board_info.toml | 2 +- ports/zephyr-cp/boards/native_sim.conf | 3 + .../nordic/nrf5340dk/autogen_board_info.toml | 18 +- .../nordic/nrf54h20dk/autogen_board_info.toml | 2 +- .../nordic/nrf54l15dk/autogen_board_info.toml | 2 +- .../nordic/nrf7002dk/autogen_board_info.toml | 2 +- .../boards/nrf5340dk_nrf5340_cpuapp.overlay | 24 ++ .../nxp/frdm_mcxn947/autogen_board_info.toml | 18 +- .../nxp/frdm_rw612/autogen_board_info.toml | 2 +- .../mimxrt1170_evk/autogen_board_info.toml | 18 +- .../da14695_dk_usb/autogen_board_info.toml | 2 +- .../renesas/ek_ra6m5/autogen_board_info.toml | 2 +- .../renesas/ek_ra8d1/autogen_board_info.toml | 2 +- .../nucleo_n657x0_q/autogen_board_info.toml | 2 +- .../nucleo_u575zi_q/autogen_board_info.toml | 2 +- .../st/stm32h750b_dk/autogen_board_info.toml | 2 +- .../st/stm32h7b3i_dk/autogen_board_info.toml | 2 +- .../stm32wba65i_dk1/autogen_board_info.toml | 2 +- .../zephyr-cp/common-hal/audiobusio/I2SOut.c | 297 ++++++++++++++++++ .../zephyr-cp/common-hal/audiobusio/I2SOut.h | 47 +++ ports/zephyr-cp/common-hal/audiobusio/PDMIn.c | 39 +++ ports/zephyr-cp/common-hal/audiobusio/PDMIn.h | 18 ++ .../common-hal/audiobusio/__init__.c | 7 + .../common-hal/audiobusio/__init__.h | 9 + .../common-hal/zephyr_kernel/__init__.c | 8 +- .../zephyr-cp/cptools/build_circuitpython.py | 41 ++- ports/zephyr-cp/cptools/compat2driver.py | 1 + ports/zephyr-cp/cptools/zephyr2cp.py | 57 +++- ports/zephyr-cp/prj.conf | 4 + ports/zephyr-cp/supervisor/port.c | 8 + ports/zephyr-cp/tests/conftest.py | 8 +- ports/zephyr-cp/tests/test_audiobusio.py | 216 +++++++++++++ shared-bindings/audiobusio/I2SOut.c | 2 + shared-module/audiomixer/Mixer.c | 10 +- .../audiobusio/i2s_sample_loop.py | 74 +++-- 44 files changed, 906 insertions(+), 107 deletions(-) create mode 100644 ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf create mode 100644 ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay create mode 100644 ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf create mode 100644 ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay create mode 100644 ports/zephyr-cp/common-hal/audiobusio/I2SOut.c create mode 100644 ports/zephyr-cp/common-hal/audiobusio/I2SOut.h create mode 100644 ports/zephyr-cp/common-hal/audiobusio/PDMIn.c create mode 100644 ports/zephyr-cp/common-hal/audiobusio/PDMIn.h create mode 100644 ports/zephyr-cp/common-hal/audiobusio/__init__.c create mode 100644 ports/zephyr-cp/common-hal/audiobusio/__init__.h create mode 100644 ports/zephyr-cp/tests/test_audiobusio.py diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index efa38764c9ab6..07623ea94a078 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -2411,6 +2411,7 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c #: ports/zephyr-cp/common-hal/busio/I2C.c #: ports/zephyr-cp/common-hal/busio/SPI.c #: ports/zephyr-cp/common-hal/busio/UART.c diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index 5c4db4f9065d3..12f22d7c3ae83 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -68,7 +68,7 @@ run-sim: echo "Populating build-native_native_sim/flash.bin from ./CIRCUITPY"; \ mcopy -s -i build-native_native_sim/flash.bin CIRCUITPY/* ::; \ fi - build-native_native_sim/firmware.exe --flash=build-native_native_sim/flash.bin --flash_rm -wait_uart -rt + build-native_native_sim/firmware.exe --flash=build-native_native_sim/flash.bin --flash_rm -wait_uart -rt --i2s_capture=build-native_native_sim/i2s_capture.wav menuconfig: west build $(WEST_SHIELD_ARGS) --sysbuild -d $(BUILD) -t menuconfig -- $(WEST_CMAKE_ARGS) diff --git a/ports/zephyr-cp/background.c b/ports/zephyr-cp/background.c index 1abc034e878ae..56e9e98f1f245 100644 --- a/ports/zephyr-cp/background.c +++ b/ports/zephyr-cp/background.c @@ -9,17 +9,7 @@ #include "py/runtime.h" #include "supervisor/port.h" -#if CIRCUITPY_DISPLAYIO -#include "shared-module/displayio/__init__.h" -#endif - -#if CIRCUITPY_AUDIOBUSIO -#include "common-hal/audiobusio/I2SOut.h" -#endif - -#if CIRCUITPY_AUDIOPWMIO -#include "common-hal/audiopwmio/PWMAudioOut.h" -#endif +#include void port_start_background_tick(void) { } @@ -28,12 +18,7 @@ void port_finish_background_tick(void) { } void port_background_tick(void) { - #if CIRCUITPY_AUDIOPWMIO - audiopwmout_background(); - #endif - #if CIRCUITPY_AUDIOBUSIO - i2s_background(); - #endif + // No, ticks. We use Zephyr threads instead. } void port_background_task(void) { diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 1672ab0b461c7..dc7b9364c2ec1 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf new file mode 100644 index 0000000000000..61f2d18ca3c78 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf @@ -0,0 +1 @@ +CONFIG_DMA_TCD_QUEUE_SIZE=4 diff --git a/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay b/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay new file mode 100644 index 0000000000000..9c517e4325514 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay @@ -0,0 +1,12 @@ +&w25q512jvfiq { + partitions { + /delete-node/ storage_partition; + circuitpy_partition: partition@620000 { + label = "circuitpy"; + reg = <0x00620000 (DT_SIZE_M(58) - DT_SIZE_K(128))>; + }; + } + +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf new file mode 100644 index 0000000000000..61f2d18ca3c78 --- /dev/null +++ b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf @@ -0,0 +1 @@ +CONFIG_DMA_TCD_QUEUE_SIZE=4 diff --git a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay index 89a78998cea74..ac6fdd8654e29 100644 --- a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay +++ b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay @@ -8,4 +8,8 @@ }; }; +&sai1 { + mclk-output; +}; + #include "../app.overlay" diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 51a9f1b347621..dd4040607fb7f 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -15,14 +15,14 @@ alarm = false analogbufio = false analogio = false atexit = false -audiobusio = false -audiocore = false -audiodelays = false -audiofilters = false -audiofreeverb = false +audiobusio = true # Zephyr board has audiobusio +audiocore = true # Zephyr board has audiobusio +audiodelays = true # Zephyr board has audiobusio +audiofilters = true # Zephyr board has audiobusio +audiofreeverb = true # Zephyr board has audiobusio audioio = false -audiomixer = false -audiomp3 = false +audiomixer = true # Zephyr board has audiobusio +audiomp3 = true # Zephyr board has audiobusio audiopwmio = false audiospeed = false aurora_epaper = false @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -97,7 +97,7 @@ ssl = false storage = true # Zephyr board has flash struct = true supervisor = true -synthio = false +synthio = true # Zephyr board has audiobusio terminalio = true # Zephyr board has busio tilepalettemapper = true # Zephyr board has busio time = true diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 36df4d16caadf..00f7178363943 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/native_sim.conf b/ports/zephyr-cp/boards/native_sim.conf index 739a71eeeb61e..e02cd0ac84eb2 100644 --- a/ports/zephyr-cp/boards/native_sim.conf +++ b/ports/zephyr-cp/boards/native_sim.conf @@ -23,6 +23,9 @@ CONFIG_EEPROM=y CONFIG_EEPROM_AT24=y CONFIG_EEPROM_AT2X_EMUL=y +# I2S SDL emulation for audio testing +CONFIG_I2S_SDL=y + CONFIG_NETWORKING=y CONFIG_NET_IPV4=y CONFIG_NET_TCP=y diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 9cb94909047fa..92cb4ef1a80bf 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -15,14 +15,14 @@ alarm = false analogbufio = false analogio = false atexit = false -audiobusio = false -audiocore = false -audiodelays = false -audiofilters = false -audiofreeverb = false +audiobusio = true # Zephyr board has audiobusio +audiocore = true # Zephyr board has audiobusio +audiodelays = true # Zephyr board has audiobusio +audiofilters = true # Zephyr board has audiobusio +audiofreeverb = true # Zephyr board has audiobusio audioio = false -audiomixer = false -audiomp3 = false +audiomixer = true # Zephyr board has audiobusio +audiomp3 = true # Zephyr board has audiobusio audiopwmio = false audiospeed = false aurora_epaper = false @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -97,7 +97,7 @@ ssl = false storage = true # Zephyr board has flash struct = true supervisor = true -synthio = false +synthio = true # Zephyr board has audiobusio terminalio = true # Zephyr board has busio tilepalettemapper = true # Zephyr board has busio time = true diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index c900bcda2e9da..5d5f325af81aa 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index ae95c3c01d600..9792f8aca74a6 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index e1d3834d7a78d..62fa896c0ecb2 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 0000000000000..eb899df8cc9ea --- /dev/null +++ b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,24 @@ +&pinctrl { + i2s0_default_alt: i2s0_default_alt { + group1 { + psels = , + , + , + , + ; + }; + }; +}; + +&clock { + hfclkaudio-frequency = <11289600>; +}; + +i2s_rxtx: &i2s0 { + status = "okay"; + pinctrl-0 = <&i2s0_default_alt>; + pinctrl-names = "default"; + clock-source = "ACLK"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index c21c176c8565f..b121a1e82349a 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -15,14 +15,14 @@ alarm = false analogbufio = false analogio = false atexit = false -audiobusio = false -audiocore = false -audiodelays = false -audiofilters = false -audiofreeverb = false +audiobusio = true # Zephyr board has audiobusio +audiocore = true # Zephyr board has audiobusio +audiodelays = true # Zephyr board has audiobusio +audiofilters = true # Zephyr board has audiobusio +audiofreeverb = true # Zephyr board has audiobusio audioio = false -audiomixer = false -audiomp3 = false +audiomixer = true # Zephyr board has audiobusio +audiomp3 = true # Zephyr board has audiobusio audiopwmio = false audiospeed = false aurora_epaper = false @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -97,7 +97,7 @@ ssl = false storage = true # Zephyr board has flash struct = true supervisor = true -synthio = false +synthio = true # Zephyr board has audiobusio terminalio = true # Zephyr board has busio tilepalettemapper = true # Zephyr board has busio time = true diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index a5214dfe77435..e5b51390538ec 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index 08eca8ba202a7..57ff5fe8c813e 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -15,14 +15,14 @@ alarm = false analogbufio = false analogio = false atexit = false -audiobusio = false -audiocore = false -audiodelays = false -audiofilters = false -audiofreeverb = false +audiobusio = true # Zephyr board has audiobusio +audiocore = true # Zephyr board has audiobusio +audiodelays = true # Zephyr board has audiobusio +audiofilters = true # Zephyr board has audiobusio +audiofreeverb = true # Zephyr board has audiobusio audioio = false -audiomixer = false -audiomp3 = false +audiomixer = true # Zephyr board has audiobusio +audiomp3 = true # Zephyr board has audiobusio audiopwmio = false audiospeed = false aurora_epaper = false @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -97,7 +97,7 @@ ssl = false storage = false struct = true supervisor = true -synthio = false +synthio = true # Zephyr board has audiobusio terminalio = true # Zephyr board has busio tilepalettemapper = true # Zephyr board has busio time = true diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index 73538f827c4b8..e11d98ac9a161 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 2e765e145a482..47a54e9632a0b 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index fc17d889c0f85..c4a071b068831 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index b108b2fd8e9ac..f526b1e90fa80 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index bbe481d1713ef..2c904026fba1c 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml index f9db69fb9d84c..3e8208d82c967 100644 --- a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index 53009db597d9f..a85cd9e3cc5a8 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index b2c1c15a0bd09..36213878c2992 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false diff --git a/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c new file mode 100644 index 0000000000000..5e2e1fac8a474 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c @@ -0,0 +1,297 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/audiobusio/I2SOut.h" + +#include +#include +#include +#include +#include + +#include "bindings/zephyr_kernel/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/audiocore/__init__.h" +#include "py/runtime.h" + +#if CIRCUITPY_AUDIOBUSIO_I2SOUT + +#define AUDIO_THREAD_STACK_SIZE 2048 +#define AUDIO_THREAD_PRIORITY 5 + +// Forward declarations +static void fill_buffer(audiobusio_i2sout_obj_t *self, uint8_t *buffer, size_t buffer_size); +static void audio_thread_func(void *self_in, void *unused1, void *unused2); + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_audiobusio_i2sout_construct_from_device(audiobusio_i2sout_obj_t *self, const struct device *i2s_device) { + self->base.type = &audiobusio_i2sout_type; + self->i2s_dev = i2s_device; + self->left_justified = false; + self->playing = false; + self->paused = false; + self->sample = NULL; + self->slab_buffer = NULL; + self->thread_stack = NULL; + self->thread_id = NULL; + self->block_size = 0; + + return MP_OBJ_FROM_PTR(self); +} + +// Standard audiobusio construct - not used in Zephyr port (devices come from device tree) +void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, + const mcu_pin_obj_t *bit_clock, const mcu_pin_obj_t *word_select, + const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock, bool left_justified) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("Use device tree to define %q devices"), MP_QSTR_I2S); +} + +bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t *self) { + return self->i2s_dev == NULL; +} + +void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) { + if (common_hal_audiobusio_i2sout_deinited(self)) { + return; + } + + // Stop playback (which will free buffers) + common_hal_audiobusio_i2sout_stop(self); + + // Note: Pins and I2S device are managed by Zephyr, not released here + self->i2s_dev = NULL; +} + +static void fill_buffer(audiobusio_i2sout_obj_t *self, uint8_t *buffer, size_t buffer_size) { + if (self->sample == NULL || self->paused || self->stopping) { + // Fill with silence + memset(buffer, 0, buffer_size); + return; + } + + uint32_t bytes_filled = 0; + while (bytes_filled < buffer_size) { + uint8_t *sample_buffer; + uint32_t sample_buffer_length; + + audioio_get_buffer_result_t result = audiosample_get_buffer( + self->sample, false, 0, &sample_buffer, &sample_buffer_length); + + if (result == GET_BUFFER_ERROR) { + // Error getting buffer, stop playback + self->stopping = true; + memset(buffer + bytes_filled, 0, buffer_size - bytes_filled); + return; + } + + if (result == GET_BUFFER_DONE) { + if (self->loop) { + // Reset to beginning + audiosample_reset_buffer(self->sample, false, 0); + } else { + // Done playing, fill rest with silence + self->stopping = true; + i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DRAIN); + memset(buffer + bytes_filled, 0, buffer_size - bytes_filled); + return; + } + } + + // Copy data to buffer + uint32_t bytes_to_copy = sample_buffer_length; + if (bytes_filled + bytes_to_copy > buffer_size) { + bytes_to_copy = buffer_size - bytes_filled; + } + + memcpy(buffer + bytes_filled, sample_buffer, bytes_to_copy); + bytes_filled += bytes_to_copy; + } +} + +static void audio_thread_func(void *self_in, void *unused1, void *unused2) { + audiobusio_i2sout_obj_t *self = (audiobusio_i2sout_obj_t *)self_in; + + while (!self->stopping) { + uint8_t *next_buffer = NULL; + // Wait until I2S has freed the buffer it is sending. + if (k_mem_slab_alloc(&self->mem_slab, (void **)&next_buffer, K_FOREVER) != 0) { + break; + } + if (self->stopping) { + // Stopping so break. + k_mem_slab_free(&self->mem_slab, next_buffer); + break; + } + fill_buffer(self, next_buffer, self->block_size); + + // Write to I2S + int ret = i2s_write(self->i2s_dev, next_buffer, self->block_size); + if (ret < 0) { + printk("i2s_write failed: %d\n", ret); + // Error writing, stop playback + self->playing = false; + break; + } + } +} + +void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self, + mp_obj_t sample, bool loop) { + // Stop any existing playback + if (self->playing) { + common_hal_audiobusio_i2sout_stop(self); + } + + // Get sample information + uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); + uint32_t sample_rate = audiosample_get_sample_rate(sample); + uint8_t channel_count = audiosample_get_channel_count(sample); + + // Store sample parameters + self->sample = sample; + self->loop = loop; + self->bytes_per_sample = bits_per_sample / 8; + self->channel_count = channel_count; + self->stopping = false; + + // Get buffer structure from the sample + bool single_buffer, samples_signed; + uint32_t max_buffer_length; + uint8_t sample_spacing; + audiosample_get_buffer_structure(sample, /* single_channel_output */ false, + &single_buffer, &samples_signed, &max_buffer_length, &sample_spacing); + + // Use max_buffer_length from the sample as the block size + self->block_size = max_buffer_length; + if (channel_count == 1) { + // Make room for stereo samples. + self->block_size *= 2; + } + size_t block_size = self->block_size; + uint32_t num_blocks = 4; // Use 4 blocks for buffering + + // Allocate memory slab buffer + self->slab_buffer = m_malloc(self->block_size * num_blocks); + + // Initialize memory slab + int ret = k_mem_slab_init(&self->mem_slab, self->slab_buffer, block_size, num_blocks); + CHECK_ZEPHYR_RESULT(ret); + + // Configure I2S + struct i2s_config config; + config.word_size = bits_per_sample; + config.channels = 2; + config.format = self->left_justified ? I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED : I2S_FMT_DATA_FORMAT_I2S; + config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER; + config.frame_clk_freq = sample_rate; + config.mem_slab = &self->mem_slab; + config.block_size = block_size; + config.timeout = 1000; // Not a k_timeout_t. In milliseconds. + + // Configure returns EINVAL if the I2S device is not ready. We loop on this + // because it should be ready after it comes to a complete stop. + ret = -EAGAIN; + while (ret == -EAGAIN) { + ret = i2s_configure(self->i2s_dev, I2S_DIR_TX, &config); + } + if (ret != 0) { + common_hal_audiobusio_i2sout_stop(self); + raise_zephyr_error(ret); + } + + // Fill every slab before starting playback to avoid underruns. + for (uint32_t i = 0; i < num_blocks; i++) { + uint8_t *buf = NULL; + k_mem_slab_alloc(&self->mem_slab, (void **)&buf, K_NO_WAIT); + fill_buffer(self, buf, block_size); + ret = i2s_write(self->i2s_dev, buf, block_size); + if (ret < 0) { + printk("i2s_write failed: %d\n", ret); + common_hal_audiobusio_i2sout_stop(self); + raise_zephyr_error(ret); + } + } + + // Allocate thread stack with proper MPU alignment for HW stack protection + self->thread_stack = k_thread_stack_alloc(AUDIO_THREAD_STACK_SIZE, 0); + + // Create and start audio processing thread + self->thread_id = k_thread_create(&self->thread, self->thread_stack, + AUDIO_THREAD_STACK_SIZE, + audio_thread_func, + self, NULL, NULL, + AUDIO_THREAD_PRIORITY, 0, K_NO_WAIT); + // Start I2S + ret = i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START); + if (ret < 0) { + common_hal_audiobusio_i2sout_stop(self); + raise_zephyr_error(ret); + } + + self->playing = true; +} + +void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t *self) { + if (!self->playing) { + return; + } + + self->playing = false; + self->paused = false; + self->stopping = true; + + // Stop I2S + i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP); + + // Wait for thread to finish + if (self->thread_id != NULL) { + k_thread_join(self->thread_id, K_FOREVER); + self->thread_id = NULL; + } + + // Free thread stack + if (self->thread_stack != NULL) { + k_thread_stack_free(self->thread_stack); + self->thread_stack = NULL; + } + + // Free buffers + if (self->slab_buffer != NULL) { + m_free(self->slab_buffer); + self->slab_buffer = NULL; + } + + self->sample = NULL; +} + +bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t *self) { + return self->playing; +} + +void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t *self) { + if (!self->playing || self->paused) { + return; + } + + self->paused = true; + i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_STOP); +} + +void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t *self) { + if (!self->playing || !self->paused) { + return; + } + + self->paused = false; + // Thread will automatically resume filling buffers + i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START); +} + +bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t *self) { + return self->paused; +} + +#endif // CIRCUITPY_AUDIOBUSIO_I2SOUT diff --git a/ports/zephyr-cp/common-hal/audiobusio/I2SOut.h b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.h new file mode 100644 index 0000000000000..916471fa83328 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.h @@ -0,0 +1,47 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audiocore/__init__.h" + +#include +#include +#include + +#if CIRCUITPY_AUDIOBUSIO_I2SOUT + +typedef struct { + mp_obj_base_t base; + const struct device *i2s_dev; + const mcu_pin_obj_t *bit_clock; + const mcu_pin_obj_t *word_select; + const mcu_pin_obj_t *data; + const mcu_pin_obj_t *main_clock; + mp_obj_t sample; + struct k_mem_slab mem_slab; + char *slab_buffer; + struct k_thread thread; + k_thread_stack_t *thread_stack; + k_tid_t thread_id; + size_t block_size; + bool left_justified; + bool playing; + bool paused; + bool loop; + bool stopping; + bool single_buffer; + uint8_t bytes_per_sample; + uint8_t channel_count; +} audiobusio_i2sout_obj_t; + +mp_obj_t common_hal_audiobusio_i2sout_construct_from_device(audiobusio_i2sout_obj_t *self, const struct device *i2s_device); + +void i2sout_reset(void); + +#endif // CIRCUITPY_AUDIOBUSIO_I2SOUT diff --git a/ports/zephyr-cp/common-hal/audiobusio/PDMIn.c b/ports/zephyr-cp/common-hal/audiobusio/PDMIn.c new file mode 100644 index 0000000000000..3d3cfef525849 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/PDMIn.c @@ -0,0 +1,39 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/audiobusio/PDMIn.h" + +#include "py/runtime.h" + +#if CIRCUITPY_AUDIOBUSIO_PDMIN + +void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, + const mcu_pin_obj_t *clock_pin, const mcu_pin_obj_t *data_pin, + uint32_t sample_rate, uint8_t bit_depth, bool mono, uint8_t oversample) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_audiobusio_pdmin_deinited(audiobusio_pdmin_obj_t *self) { + return true; +} + +void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t *self) { +} + +uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t *self) { + return 0; +} + +uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t *self) { + return 0; +} + +uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t *self, + uint16_t *output_buffer, uint32_t output_buffer_length) { + return 0; +} + +#endif // CIRCUITPY_AUDIOBUSIO_PDMIN diff --git a/ports/zephyr-cp/common-hal/audiobusio/PDMIn.h b/ports/zephyr-cp/common-hal/audiobusio/PDMIn.h new file mode 100644 index 0000000000000..195a436f3cf61 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/PDMIn.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "common-hal/microcontroller/Pin.h" + +#if CIRCUITPY_AUDIOBUSIO_PDMIN + +typedef struct { + mp_obj_base_t base; +} audiobusio_pdmin_obj_t; + +#endif // CIRCUITPY_AUDIOBUSIO_PDMIN diff --git a/ports/zephyr-cp/common-hal/audiobusio/__init__.c b/ports/zephyr-cp/common-hal/audiobusio/__init__.c new file mode 100644 index 0000000000000..5d2e802904d01 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// No special initialization required for audiobusio diff --git a/ports/zephyr-cp/common-hal/audiobusio/__init__.h b/ports/zephyr-cp/common-hal/audiobusio/__init__.h new file mode 100644 index 0000000000000..8ba7882bf9474 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/__init__.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// No common definitions needed for audiobusio diff --git a/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c b/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c index b7a5bf9dbf1b4..178f33e028d73 100644 --- a/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c +++ b/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c @@ -9,7 +9,7 @@ #include #include - +#include void raise_zephyr_error(int err) { if (err == 0) { @@ -46,6 +46,12 @@ void raise_zephyr_error(int err) { case EADDRINUSE: printk("EADDRINUSE\n"); break; + case EIO: + printk("EIO\n"); + break; + case ENOSYS: + printk("ENOSYS\n"); + break; case EINVAL: printk("EINVAL\n"); break; diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 5f51281870c4d..bd4f22a03cd41 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -59,12 +59,22 @@ "supervisor", "errno", "io", + "math", ] # Flags that don't match with with a *bindings module. Some used by adafruit_requests -MPCONFIG_FLAGS = ["array", "errno", "io", "json"] +MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math"] # List of other modules (the value) that can be enabled when another one (the key) is. REVERSE_DEPENDENCIES = { + "audiobusio": ["audiocore"], + "audiocore": [ + "audiodelays", + "audiofilters", + "audiofreeverb", + "audiomixer", + "audiomp3", + "synthio", + ], "busio": ["fourwire", "i2cdisplaybus", "sdcardio", "sharpdisplay"], "fourwire": ["displayio", "busdisplay", "epaperdisplay"], "i2cdisplaybus": ["displayio", "busdisplay", "epaperdisplay"], @@ -86,8 +96,16 @@ # Other flags to set when a module is enabled EXTRA_FLAGS = { - "busio": ["BUSIO_SPI", "BUSIO_I2C"], - "rotaryio": ["ROTARYIO_SOFTENCODER"], + "audiobusio": {"AUDIOBUSIO_I2SOUT": 1, "AUDIOBUSIO_PDMIN": 0}, + "busio": {"BUSIO_SPI": 1, "BUSIO_I2C": 1}, + "rotaryio": {"ROTARYIO_SOFTENCODER": 1}, + "synthio": {"SYNTHIO_MAX_CHANNELS": 12}, +} + +# Library sources. Will be globbed from the top level directory +# No QSTR processing or CIRCUITPY specific flags +LIBRARY_SOURCE = { + "audiomp3": ["lib/mp3/src/*.c"], } SHARED_MODULE_AND_COMMON_HAL = ["_bleio", "os", "rotaryio"] @@ -332,6 +350,9 @@ async def build_circuitpython(): circuitpython_flags.append("-DLONGINT_IMPL_MPZ") circuitpython_flags.append("-DCIRCUITPY_SSL_MBEDTLS") circuitpython_flags.append("-DFFCONF_H='\"lib/oofatfs/ffconf.h\"'") + circuitpython_flags.append( + "-D_DEFAULT_SOURCE" + ) # To get more from picolibc to match newlib such as M_PI circuitpython_flags.extend(("-I", srcdir)) circuitpython_flags.extend(("-I", builddir)) circuitpython_flags.extend(("-I", portdir)) @@ -482,6 +503,7 @@ async def build_circuitpython(): # Make sure all modules have a setting by filling in defaults. hal_source = [] + library_sources = [] autogen_board_info = tomlkit.document() autogen_board_info.add( tomlkit.comment( @@ -509,8 +531,8 @@ async def build_circuitpython(): if enabled: if module.name in EXTRA_FLAGS: - for flag in EXTRA_FLAGS[module.name]: - circuitpython_flags.append(f"-DCIRCUITPY_{flag}=1") + for flag, value in EXTRA_FLAGS[module.name].items(): + circuitpython_flags.append(f"-DCIRCUITPY_{flag}={value}") if enabled: hal_source.extend(portdir.glob(f"bindings/{module.name}/*.c")) @@ -520,6 +542,9 @@ async def build_circuitpython(): if len(hal_source) == len_before or module.name in SHARED_MODULE_AND_COMMON_HAL: hal_source.extend(top.glob(f"shared-module/{module.name}/*.c")) hal_source.extend(top.glob(f"shared-bindings/{module.name}/*.c")) + if module.name in LIBRARY_SOURCE: + for library_source in LIBRARY_SOURCE[module.name]: + library_sources.extend(top.glob(library_source)) if os.environ.get("CI", "false") == "true": # Warn if it isn't up to date. @@ -621,6 +646,12 @@ async def build_circuitpython(): objects = [] async with asyncio.TaskGroup() as tg: + for source_file in library_sources: + source_file = top / source_file + build_file = source_file.with_suffix(".o") + object_file = builddir / (build_file.relative_to(top)) + objects.append(object_file) + tg.create_task(compiler.compile(source_file, object_file)) for source_file in source_files: source_file = top / source_file build_file = source_file.with_suffix(".o") diff --git a/ports/zephyr-cp/cptools/compat2driver.py b/ports/zephyr-cp/cptools/compat2driver.py index dff09787b2881..49c462b9dedd6 100644 --- a/ports/zephyr-cp/cptools/compat2driver.py +++ b/ports/zephyr-cp/cptools/compat2driver.py @@ -1042,6 +1042,7 @@ "st_stm32_i2s": "i2s", "st_stm32_sai": "i2s", "vnd_i2s": "i2s", + "zephyr_i2s_sdl": "i2s", # # i3c "adi_max32_i3c": "i3c", diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index c123d90ce7816..20df229000b59 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -23,6 +23,7 @@ "nordic_nrf_twi": "i2c", "nordic_nrf_spim": "spi", "nordic_nrf_spi": "spi", + "nordic_nrf_i2s": "i2s", } # These are controllers, not the flash devices themselves. @@ -34,6 +35,8 @@ BUSIO_CLASSES = {"serial": "UART", "i2c": "I2C", "spi": "SPI"} +AUDIOBUSIO_CLASSES = {"i2s": "I2SOut"} + CONNECTORS = { "mikro-bus": [ "AN", @@ -411,6 +414,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa "usb_device": False, "_bleio": False, "hostnetwork": board_id in ["native_sim"], + "audiobusio": False, } config_bt_enabled = False @@ -545,6 +549,13 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa board_info["wifi"] = True elif driver == "bluetooth/hci": ble_hardware_present = True + elif driver in AUDIOBUSIO_CLASSES: + # audiobusio driver (i2s, audio/dmic) + board_info["audiobusio"] = True + logger.info(f"Supported audiobusio driver: {driver}") + if driver not in active_zephyr_devices: + active_zephyr_devices[driver] = [] + active_zephyr_devices[driver].append(node.labels) elif driver in EXCEPTIONAL_DRIVERS: pass elif driver in BUSIO_CLASSES: @@ -673,15 +684,25 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa zephyr_binding_headers = [] zephyr_binding_objects = [] zephyr_binding_labels = [] + i2sout_instance_names = [] for driver, instances in active_zephyr_devices.items(): - driverclass = BUSIO_CLASSES[driver] - zephyr_binding_headers.append(f'#include "shared-bindings/busio/{driverclass}.h"') + # Determine if this is busio or audiobusio + if driver in BUSIO_CLASSES: + module = "busio" + driverclass = BUSIO_CLASSES[driver] + elif driver in AUDIOBUSIO_CLASSES: + module = "audiobusio" + driverclass = AUDIOBUSIO_CLASSES[driver] + else: + continue - # Designate a main bus such as board.I2C. + zephyr_binding_headers.append(f'#include "shared-bindings/{module}/{driverclass}.h"') + + # Designate a main device such as board.I2C or board.I2S. if len(instances) == 1: instances[0].append(driverclass) else: - # Check to see if a main bus has already been designated + # Check to see if a main device has already been designated found_main = False for labels in instances: for label in labels: @@ -697,23 +718,28 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa if found_main: break for labels in instances: - instance_name = f"{driver}_{labels[0]}" + instance_name = f"{driver.replace('/', '_')}_{labels[0]}" c_function_name = f"_{instance_name}" singleton_ptr = f"{c_function_name}_singleton" function_object = f"{c_function_name}_obj" - busio_type = f"busio_{driverclass.lower()}" + obj_type = f"{module}_{driverclass.lower()}" - # UART needs a receiver buffer + # Handle special cases for different drivers if driver == "serial": + # UART needs a receiver buffer buffer_decl = f"static byte {instance_name}_buffer[128];" construct_call = f"common_hal_busio_uart_construct_from_device(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]})), 128, {instance_name}_buffer)" else: + # Default case (I2C, SPI, I2S) buffer_decl = "" - construct_call = f"common_hal_busio_{driverclass.lower()}_construct_from_device(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]})))" + construct_call = f"common_hal_{module}_{driverclass.lower()}_construct_from_device(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]})))" + + if driver == "i2s": + i2sout_instance_names.append(instance_name) zephyr_binding_objects.append( f"""{buffer_decl} -static {busio_type}_obj_t {instance_name}_obj; +static {obj_type}_obj_t {instance_name}_obj; static mp_obj_t {singleton_ptr} = mp_const_none; static mp_obj_t {c_function_name}(void) {{ if ({singleton_ptr} != mp_const_none) {{ @@ -732,6 +758,18 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa zephyr_binding_objects = "\n".join(zephyr_binding_objects) zephyr_binding_labels = "\n".join(zephyr_binding_labels) + # Generate i2sout_reset() that stops all board I2SOut instances + if i2sout_instance_names: + stop_calls = "\n ".join( + f"common_hal_audiobusio_i2sout_stop(&{name}_obj);" for name in i2sout_instance_names + ) + i2sout_reset_func = f""" +void i2sout_reset(void) {{ + {stop_calls} +}}""" + else: + i2sout_reset_func = "" + zephyr_display_header = "" zephyr_display_object = "" zephyr_display_board_entry = "" @@ -857,6 +895,7 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa {zephyr_binding_objects} {zephyr_display_object} +{i2sout_reset_func} static const mp_rom_map_elem_t mcu_pin_globals_table[] = {{ {mcu_pin_mapping} diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 9b4dcccb53e4d..7eaeb8989147e 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -44,3 +44,7 @@ CONFIG_FRAME_POINTER=n CONFIG_NET_HOSTNAME_ENABLE=y CONFIG_NET_HOSTNAME_DYNAMIC=y CONFIG_NET_HOSTNAME="circuitpython" + +CONFIG_I2S=y +CONFIG_DYNAMIC_THREAD=y +CONFIG_DYNAMIC_THREAD_PREFER_ALLOC=y diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index c40475177e000..b36637bb81c9c 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -9,6 +9,10 @@ #include "mpconfigboard.h" #include "supervisor/shared/tick.h" +#if CIRCUITPY_AUDIOBUSIO_I2SOUT +#include "common-hal/audiobusio/I2SOut.h" +#endif + #include #include #include @@ -147,6 +151,10 @@ void reset_cpu(void) { } void reset_port(void) { + #if CIRCUITPY_AUDIOBUSIO_I2SOUT + i2sout_reset(); + #endif + #if defined(CONFIG_ARCH_POSIX) native_sim_reset_port_count++; if (native_sim_vm_runs != INT32_MAX && diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py index cec867a97037c..03451048324de 100644 --- a/ports/zephyr-cp/tests/conftest.py +++ b/ports/zephyr-cp/tests/conftest.py @@ -318,7 +318,13 @@ def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp # native_sim vm-runs includes the boot VM setup run. realtime_flag = "-rt" if use_realtime else "-no-rt" cmd.extend( - (realtime_flag, "-display_headless", "-wait_uart", f"--vm-runs={code_py_runs + 1}") + ( + realtime_flag, + "-display_headless", + "-i2s_earless", + "-wait_uart", + f"--vm-runs={code_py_runs + 1}", + ) ) if flash_erase_block_size is not None: diff --git a/ports/zephyr-cp/tests/test_audiobusio.py b/ports/zephyr-cp/tests/test_audiobusio.py new file mode 100644 index 0000000000000..5a899139c2210 --- /dev/null +++ b/ports/zephyr-cp/tests/test_audiobusio.py @@ -0,0 +1,216 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries LLC +# SPDX-License-Identifier: MIT + +"""Test audiobusio I2SOut functionality on native_sim.""" + +from pathlib import Path + +import pytest +from perfetto.trace_processor import TraceProcessor + + +I2S_PLAY_CODE = """\ +import array +import math +import audiocore +import board +import time + +# Generate a 440 Hz sine wave, 16-bit signed stereo at 16000 Hz +sample_rate = 16000 +length = sample_rate // 440 # ~36 samples per period +values = [] +for i in range(length): + v = int(math.sin(math.pi * 2 * i / length) * 30000) + values.append(v) # left + values.append(v) # right + +sample = audiocore.RawSample( + array.array("h", values), + sample_rate=sample_rate, + channel_count=2, +) + +dac = board.I2S0() +print("playing") +dac.play(sample, loop=True) +time.sleep(0.5) +dac.stop() +print("stopped") +print("done") +""" + + +def parse_i2s_trace(trace_file: Path, track_name: str) -> list[tuple[int, int]]: + """Parse I2S counter trace from Perfetto trace file.""" + tp = TraceProcessor(file_path=str(trace_file)) + result = tp.query( + f""" + SELECT c.ts, c.value + FROM counter c + JOIN track t ON c.track_id = t.id + WHERE t.name LIKE "%{track_name}" + ORDER BY c.ts + """ + ) + return [(int(row.ts), int(row.value)) for row in result] + + +@pytest.mark.duration(10) +@pytest.mark.circuitpy_drive({"code.py": I2S_PLAY_CODE}) +def test_i2s_play_and_stop(circuitpython): + """Test I2SOut play and stop produce expected output and correct waveform traces.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "playing" in output + assert "stopped" in output + assert "done" in output + + # Check that Perfetto trace has I2S counter tracks with data + left_trace = parse_i2s_trace(circuitpython.trace_file, "Left") + right_trace = parse_i2s_trace(circuitpython.trace_file, "Right") + + # Should have counter events (initial zero + audio data) + assert len(left_trace) > 10, f"Expected many Left channel events, got {len(left_trace)}" + assert len(right_trace) > 10, f"Expected many Right channel events, got {len(right_trace)}" + + # Verify timestamps are spread out (not all the same) + left_timestamps = [ts for ts, _ in left_trace] + assert left_timestamps[-1] > left_timestamps[1], "Timestamps should increase over time" + time_span_ns = left_timestamps[-1] - left_timestamps[1] + # We play for 0.5s, so span should be at least 100ms + assert time_span_ns > 100_000_000, f"Expected >100ms time span, got {time_span_ns / 1e6:.1f}ms" + + # Audio data should contain non-zero values (sine wave) + # Skip the initial zero value + left_values = [v for _, v in left_trace if v != 0] + right_values = [v for _, v in right_trace if v != 0] + assert len(left_values) > 5, "Left channel has too few non-zero values" + assert len(right_values) > 5, "Right channel has too few non-zero values" + + # Sine wave should have both positive and negative values + assert any(v > 0 for v in left_values), "Left channel has no positive values" + assert any(v < 0 for v in left_values), "Left channel has no negative values" + + # Verify amplitude is in the expected range (we generate with amplitude 30000) + max_left = max(left_values) + min_left = min(left_values) + assert max_left > 20000, f"Left max {max_left} too low, expected >20000" + assert min_left < -20000, f"Left min {min_left} too high, expected <-20000" + + # Left and right should match (we write the same value to both channels) + # Compare a subset of matching timestamps + left_by_ts = dict(left_trace) + right_by_ts = dict(right_trace) + common_ts = sorted(set(left_by_ts.keys()) & set(right_by_ts.keys())) + mismatches = 0 + for ts in common_ts[:100]: + if left_by_ts[ts] != right_by_ts[ts]: + mismatches += 1 + assert mismatches == 0, ( + f"{mismatches} L/R mismatches in first {min(100, len(common_ts))} common timestamps" + ) + + +I2S_PLAY_NO_STOP_CODE = """\ +import array +import math +import audiocore +import board +import time + +sample_rate = 16000 +length = sample_rate // 440 +values = [] +for i in range(length): + v = int(math.sin(math.pi * 2 * i / length) * 30000) + values.append(v) + values.append(v) + +sample = audiocore.RawSample( + array.array("h", values), + sample_rate=sample_rate, + channel_count=2, +) + +dac = board.I2S0() +dac.play(sample, loop=True) +print("playing") +time.sleep(0.2) +# Exit without calling dac.stop() — reset_port should clean up +print("exiting") +""" + + +@pytest.mark.duration(15) +@pytest.mark.code_py_runs(2) +@pytest.mark.circuitpy_drive({"code.py": I2S_PLAY_NO_STOP_CODE}) +def test_i2s_stops_on_code_exit(circuitpython): + """Test I2S is stopped by reset_port when code.py exits without explicit stop.""" + # First run: plays audio then exits without stopping + circuitpython.serial.wait_for("exiting") + circuitpython.serial.wait_for("Press any key to enter the REPL") + # Trigger soft reload + circuitpython.serial.write("\x04") + + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + # Should see "playing" and "exiting" at least twice (once per run) + assert output.count("playing") >= 2 + assert output.count("exiting") >= 2 + + +I2S_PAUSE_RESUME_CODE = """\ +import array +import math +import audiocore +import board +import time + +sample_rate = 16000 +length = sample_rate // 440 +values = [] +for i in range(length): + v = int(math.sin(math.pi * 2 * i / length) * 30000) + values.append(v) + values.append(v) + +sample = audiocore.RawSample( + array.array("h", values), + sample_rate=sample_rate, + channel_count=2, +) + +dac = board.I2S0() +dac.play(sample, loop=True) +print("playing") +time.sleep(0.2) + +dac.pause() +print("paused") +assert dac.paused +time.sleep(0.1) + +dac.resume() +print("resumed") +assert not dac.paused +time.sleep(0.2) + +dac.stop() +print("done") +""" + + +@pytest.mark.duration(10) +@pytest.mark.circuitpy_drive({"code.py": I2S_PAUSE_RESUME_CODE}) +def test_i2s_pause_resume(circuitpython): + """Test I2SOut pause and resume work correctly.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "playing" in output + assert "paused" in output + assert "resumed" in output + assert "done" in output diff --git a/shared-bindings/audiobusio/I2SOut.c b/shared-bindings/audiobusio/I2SOut.c index 9aaf7421c653c..952a00e2903ee 100644 --- a/shared-bindings/audiobusio/I2SOut.c +++ b/shared-bindings/audiobusio/I2SOut.c @@ -144,6 +144,8 @@ static void check_for_deinit(audiobusio_i2sout_obj_t *self) { //| //| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. //| +//| Mono samples will be converted to stereo by copying value to both the left channel and the right channel. +//| //| The sample itself should consist of 8 bit or 16 bit samples.""" //| ... //| diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index b27eafb71a98c..212686a092ee9 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -99,11 +99,11 @@ static inline uint32_t mult16signed(uint32_t val, int32_t mul[2]) { int32_t hi, lo; enum { bits = 16 }; // saturate to 16 bits enum { shift = 15 }; // shift is done automatically - asm volatile ("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul[0]), "r" (val)); - asm volatile ("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul[1]), "r" (val)); - asm volatile ("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); - asm volatile ("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); - asm volatile ("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack + __asm__ volatile ("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul[0]), "r" (val)); + __asm__ volatile ("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul[1]), "r" (val)); + __asm__ volatile ("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); + __asm__ volatile ("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); + __asm__ volatile ("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack return val; #else uint32_t result = 0; diff --git a/tests/circuitpython-manual/audiobusio/i2s_sample_loop.py b/tests/circuitpython-manual/audiobusio/i2s_sample_loop.py index 75ed4c7ae6f30..9778ac2b82025 100644 --- a/tests/circuitpython-manual/audiobusio/i2s_sample_loop.py +++ b/tests/circuitpython-manual/audiobusio/i2s_sample_loop.py @@ -1,35 +1,73 @@ +# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: MIT import audiocore -import audiobusio import board import digitalio import array +import struct import time import math -import rp2pio -import adafruit_pioasm - -time.sleep(10) trigger = digitalio.DigitalInOut(board.D4) trigger.switch_to_output(True) # Generate one period of sine wav. -length = 8000 // 440 +sample_names = [ + "mono unsigned 8 bit", + "stereo unsigned 8 bit", + "mono signed 8 bit", + "stereo signed 8 bit", + "mono unsigned 16 bit", + "stereo unsigned 16 bit", + "mono signed 16 bit", + "stereo signed 16 bit", +] +sample_config = { + "mono unsigned 8 bit": {"format": "B", "channel_count": 1}, + "stereo unsigned 8 bit": {"format": "B", "channel_count": 2}, + "mono signed 8 bit": {"format": "b", "channel_count": 1}, + "stereo signed 8 bit": {"format": "b", "channel_count": 2}, + "mono unsigned 16 bit": {"format": "H", "channel_count": 1}, + "stereo unsigned 16 bit": {"format": "H", "channel_count": 2}, + "mono signed 16 bit": {"format": "h", "channel_count": 1}, + "stereo signed 16 bit": {"format": "h", "channel_count": 2}, +} -# signed 16 bit -s16 = array.array("h", [0] * length) -for i in range(length): - s16[i] = int(math.sin(math.pi * 2 * i / length) * (2**15)) - print(s16[i]) +for sample_rate in [8000, 16000, 32000, 44100]: + print(f"{sample_rate / 1000} kHz") + length = sample_rate // 440 -sample = audiocore.RawSample(s16, sample_rate=8000) + samples = [] -dac = audiobusio.I2SOut(bit_clock=board.D10, word_select=board.D11, data=board.D12) + for name in sample_names: + config = sample_config[name] + format = config["format"] + channel_count = config["channel_count"] + length = sample_rate // 440 + values = [] + for i in range(length): + range = 2 ** (struct.calcsize(format) * 8 - 1) - 1 + value = int(math.sin(math.pi * 2 * i / length) * range) + if "unsigned" in name: + value += range + values.append(value) + if channel_count == 2: + values.append(value) + sample = audiocore.RawSample( + array.array(format, values), sample_rate=sample_rate, channel_count=channel_count + ) + samples.append(sample) -trigger.value = False -dac.play(sample, loop=True) -time.sleep(1) -dac.stop() -trigger.value = True + dac = board.I2S0() + for sample, name in zip(samples, sample_names): + print(" ", name) + trigger.value = False + dac.play(sample, loop=True) + time.sleep(1) + dac.stop() + time.sleep(0.1) + trigger.value = True + print() print("done") From 321b0bf566237184509d31fba5c90d5a31f665d0 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Mon, 30 Mar 2026 15:53:00 -0700 Subject: [PATCH 481/517] Add NVM support for Zephyr port Implement nvm.ByteArray using Zephyr flash_area API, auto-detect NVM partition size from device tree, and add 8KB NVM partition to feather nrf52840 matching the non-Zephyr Nordic port layout. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../autogen_board_info.toml | 2 +- .../adafruit_feather_nrf52840_uf2.overlay | 22 ++++ .../native/native_sim/autogen_board_info.toml | 2 +- ports/zephyr-cp/boards/native_sim.overlay | 7 +- .../common-hal/microcontroller/__init__.c | 6 +- ports/zephyr-cp/common-hal/nvm/ByteArray.c | 103 ++++++++++++++++++ ports/zephyr-cp/common-hal/nvm/ByteArray.h | 13 +++ ports/zephyr-cp/cptools/zephyr2cp.py | 5 + ports/zephyr-cp/mpconfigport.h | 3 + ports/zephyr-cp/tests/test_nvm.py | 73 +++++++++++++ 10 files changed, 229 insertions(+), 7 deletions(-) create mode 100644 ports/zephyr-cp/common-hal/nvm/ByteArray.c create mode 100644 ports/zephyr-cp/common-hal/nvm/ByteArray.h create mode 100644 ports/zephyr-cp/tests/test_nvm.py diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 1672ab0b461c7..762f1166996da 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -73,7 +73,7 @@ microcontroller = true mipidsi = false msgpack = false neopixel_write = false -nvm = false +nvm = true # Zephyr board has nvm onewireio = false os = true paralleldisplaybus = false diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay index a61cbf2047de2..e01ebd6df1f51 100644 --- a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay @@ -17,6 +17,28 @@ /delete-node/ partitions; }; +/delete-node/ &storage_partition; +/delete-node/ &code_partition; + +&flash0 { + partitions { + code_partition: partition@26000 { + label = "Application"; + reg = <0x00026000 0x000c4000>; + }; + + storage_partition: partition@ea000 { + label = "storage"; + reg = <0x000ea000 0x00008000>; + }; + + nvm_partition: partition@f2000 { + label = "nvm"; + reg = <0x000f2000 0x00002000>; + }; + }; +}; + &uart0 { status = "okay"; }; diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 51a9f1b347621..7224381ec28e4 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -73,7 +73,7 @@ microcontroller = true mipidsi = false msgpack = false neopixel_write = false -nvm = false +nvm = true # Zephyr board has nvm onewireio = false os = true paralleldisplaybus = false diff --git a/ports/zephyr-cp/boards/native_sim.overlay b/ports/zephyr-cp/boards/native_sim.overlay index 2a07108627fc9..aee9d17f9d099 100644 --- a/ports/zephyr-cp/boards/native_sim.overlay +++ b/ports/zephyr-cp/boards/native_sim.overlay @@ -31,7 +31,12 @@ circuitpy_partition: partition@0 { label = "circuitpy"; - reg = <0x00000000 DT_SIZE_K(2048)>; + reg = <0x00000000 DT_SIZE_K(2040)>; + }; + + nvm_partition: partition@1fe000 { + label = "nvm"; + reg = <0x001fe000 0x00002000>; }; }; }; diff --git a/ports/zephyr-cp/common-hal/microcontroller/__init__.c b/ports/zephyr-cp/common-hal/microcontroller/__init__.c index 6ebf5f4c36818..be33cd26c9ace 100644 --- a/ports/zephyr-cp/common-hal/microcontroller/__init__.c +++ b/ports/zephyr-cp/common-hal/microcontroller/__init__.c @@ -11,7 +11,7 @@ #include "common-hal/microcontroller/Pin.h" #include "common-hal/microcontroller/Processor.h" -// #include "shared-bindings/nvm/ByteArray.h" +#include "shared-bindings/nvm/ByteArray.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Processor.h" @@ -93,14 +93,12 @@ const mcu_processor_obj_t common_hal_mcu_processor_obj = { }, }; -#if CIRCUITPY_NVM && CIRCUITPY_INTERNAL_NVM_SIZE > 0 +#if CIRCUITPY_NVM // The singleton nvm.ByteArray object. const nvm_bytearray_obj_t common_hal_mcu_nvm_obj = { .base = { .type = &nvm_bytearray_type, }, - .start_address = (uint8_t *)CIRCUITPY_INTERNAL_NVM_START_ADDR, - .len = CIRCUITPY_INTERNAL_NVM_SIZE, }; #endif diff --git a/ports/zephyr-cp/common-hal/nvm/ByteArray.c b/ports/zephyr-cp/common-hal/nvm/ByteArray.c new file mode 100644 index 0000000000000..b8f552d677389 --- /dev/null +++ b/ports/zephyr-cp/common-hal/nvm/ByteArray.c @@ -0,0 +1,103 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "common-hal/nvm/ByteArray.h" +#include "shared-bindings/nvm/ByteArray.h" + +#include + +#include +#include + +#define NVM_PARTITION nvm_partition + +#if FIXED_PARTITION_EXISTS(NVM_PARTITION) + +static const struct flash_area *nvm_area = NULL; +static size_t nvm_erase_size = 0; + +static bool ensure_nvm_open(void) { + if (nvm_area != NULL) { + return true; + } + int rc = flash_area_open(FIXED_PARTITION_ID(NVM_PARTITION), &nvm_area); + if (rc != 0) { + return false; + } + + const struct device *dev = flash_area_get_device(nvm_area); + struct flash_pages_info info; + flash_get_page_info_by_offs(dev, nvm_area->fa_off, &info); + nvm_erase_size = info.size; + + return true; +} + +uint32_t common_hal_nvm_bytearray_get_length(const nvm_bytearray_obj_t *self) { + if (!ensure_nvm_open()) { + return 0; + } + return nvm_area->fa_size; +} + +bool common_hal_nvm_bytearray_set_bytes(const nvm_bytearray_obj_t *self, + uint32_t start_index, uint8_t *values, uint32_t len) { + if (!ensure_nvm_open()) { + return false; + } + + uint32_t address = start_index; + while (len > 0) { + uint32_t page_offset = address % nvm_erase_size; + uint32_t page_start = address - page_offset; + uint32_t write_len = MIN(len, nvm_erase_size - page_offset); + + uint8_t *buffer = m_malloc(nvm_erase_size); + if (buffer == NULL) { + return false; + } + + // Read the full erase page. + int rc = flash_area_read(nvm_area, page_start, buffer, nvm_erase_size); + if (rc != 0) { + m_free(buffer); + return false; + } + + // Modify the relevant bytes. + memcpy(buffer + page_offset, values, write_len); + + // Erase the page. + rc = flash_area_erase(nvm_area, page_start, nvm_erase_size); + if (rc != 0) { + m_free(buffer); + return false; + } + + // Write the page back. + rc = flash_area_write(nvm_area, page_start, buffer, nvm_erase_size); + m_free(buffer); + if (rc != 0) { + return false; + } + + address += write_len; + values += write_len; + len -= write_len; + } + return true; +} + +void common_hal_nvm_bytearray_get_bytes(const nvm_bytearray_obj_t *self, + uint32_t start_index, uint32_t len, uint8_t *values) { + if (!ensure_nvm_open()) { + return; + } + flash_area_read(nvm_area, start_index, values, len); +} + +#endif // FIXED_PARTITION_EXISTS(NVM_PARTITION) diff --git a/ports/zephyr-cp/common-hal/nvm/ByteArray.h b/ports/zephyr-cp/common-hal/nvm/ByteArray.h new file mode 100644 index 0000000000000..9c771aaa3a950 --- /dev/null +++ b/ports/zephyr-cp/common-hal/nvm/ByteArray.h @@ -0,0 +1,13 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} nvm_bytearray_obj_t; diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index c123d90ce7816..d8372919f299d 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -893,4 +893,9 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa board_info["flash_count"] = len(flashes) board_info["rotaryio"] = bool(ioports) board_info["usb_num_endpoint_pairs"] = usb_num_endpoint_pairs + + # Detect NVM partition from the device tree. + nvm_node = device_tree.label2node.get("nvm_partition") + board_info["nvm"] = nvm_node is not None + return board_info diff --git a/ports/zephyr-cp/mpconfigport.h b/ports/zephyr-cp/mpconfigport.h index 491b5293e2ebc..491c03592c01d 100644 --- a/ports/zephyr-cp/mpconfigport.h +++ b/ports/zephyr-cp/mpconfigport.h @@ -17,6 +17,9 @@ #define CIRCUITPY_DEBUG_TINYUSB 0 +// NVM size is determined at runtime from the Zephyr partition table. +#define CIRCUITPY_INTERNAL_NVM_SIZE 1 + // Disable native _Float16 handling for host builds. #define MICROPY_FLOAT_USE_NATIVE_FLT16 (0) diff --git a/ports/zephyr-cp/tests/test_nvm.py b/ports/zephyr-cp/tests/test_nvm.py new file mode 100644 index 0000000000000..5de304afc1bdc --- /dev/null +++ b/ports/zephyr-cp/tests/test_nvm.py @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test NVM functionality on native_sim.""" + +import pytest + + +NVM_BASIC_CODE = """\ +import microcontroller + +nvm = microcontroller.nvm +print(f"nvm length: {len(nvm)}") + +# Write some bytes +nvm[0] = 42 +nvm[1] = 99 +print(f"nvm[0]: {nvm[0]}") +print(f"nvm[1]: {nvm[1]}") + +# Write a slice +nvm[2:5] = b"\\x01\\x02\\x03" +print(f"nvm[2:5]: {list(nvm[2:5])}") + +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": NVM_BASIC_CODE}) +def test_nvm_read_write(circuitpython): + """Test basic NVM read and write operations.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "nvm length: 8192" in output + assert "nvm[0]: 42" in output + assert "nvm[1]: 99" in output + assert "nvm[2:5]: [1, 2, 3]" in output + assert "done" in output + + +NVM_PERSIST_CODE = """\ +import microcontroller + +nvm = microcontroller.nvm +value = nvm[0] +print(f"nvm[0]: {value}") + +if value == 255: + # First run: write a marker + nvm[0] = 123 + print("wrote marker") +else: + print(f"marker found: {value}") + +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": NVM_PERSIST_CODE}) +@pytest.mark.code_py_runs(2) +def test_nvm_persists_across_reload(circuitpython): + """Test that NVM data persists across soft reloads.""" + circuitpython.serial.wait_for("wrote marker") + # Trigger soft reload + circuitpython.serial.write("\x04") + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "nvm[0]: 255" in output + assert "wrote marker" in output + assert "marker found: 123" in output + assert "done" in output From 870cc04dcb9737592e3e2c3bf6af635f9a39fd99 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 1 Apr 2026 12:21:44 -0700 Subject: [PATCH 482/517] Enable dynamic thread stack alloc --- ports/zephyr-cp/prj.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 7eaeb8989147e..308333922f3f6 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -47,4 +47,5 @@ CONFIG_NET_HOSTNAME="circuitpython" CONFIG_I2S=y CONFIG_DYNAMIC_THREAD=y +CONFIG_DYNAMIC_THREAD_ALLOC=y CONFIG_DYNAMIC_THREAD_PREFER_ALLOC=y From 57a07fd5f248aa36aba5e5499baf516e27c4f91b Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 1 Apr 2026 13:12:58 -0700 Subject: [PATCH 483/517] Use newer zephyr with uninit fix --- ports/zephyr-cp/zephyr-config/west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr-cp/zephyr-config/west.yml b/ports/zephyr-cp/zephyr-config/west.yml index fd3eebc66f172..82509b40cefb6 100644 --- a/ports/zephyr-cp/zephyr-config/west.yml +++ b/ports/zephyr-cp/zephyr-config/west.yml @@ -8,6 +8,6 @@ manifest: path: modules/bsim_hw_models/nrf_hw_models - name: zephyr url: https://github.com/adafruit/zephyr - revision: a768573cc42b18bc2e8b819d4686e52cdb9c848e + revision: d991bfc190507849d510326b24ba7b7a6c51a0e6 clone-depth: 100 import: true From ee527d3b47f1a06dd060832dca81de7a9204c96c Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 1 Apr 2026 15:31:54 -0700 Subject: [PATCH 484/517] Free the slab buffer on error --- ports/zephyr-cp/common-hal/audiobusio/I2SOut.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c index 5e2e1fac8a474..e858552c524c0 100644 --- a/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c +++ b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c @@ -131,6 +131,7 @@ static void audio_thread_func(void *self_in, void *unused1, void *unused2) { int ret = i2s_write(self->i2s_dev, next_buffer, self->block_size); if (ret < 0) { printk("i2s_write failed: %d\n", ret); + k_mem_slab_free(&self->mem_slab, next_buffer); // Error writing, stop playback self->playing = false; break; @@ -210,6 +211,7 @@ void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self, ret = i2s_write(self->i2s_dev, buf, block_size); if (ret < 0) { printk("i2s_write failed: %d\n", ret); + k_mem_slab_free(&self->mem_slab, buf); common_hal_audiobusio_i2sout_stop(self); raise_zephyr_error(ret); } From fa583096f7180259778f71c6930c57c66695b64b Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 2 Apr 2026 18:12:19 +0200 Subject: [PATCH 485/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 1 + locale/el.po | 1 + locale/hi.po | 1 + locale/ko.po | 1 + locale/ru.po | 1 + locale/tr.po | 1 + 6 files changed, 6 insertions(+) diff --git a/locale/cs.po b/locale/cs.po index 0adef977617cd..94a03f2e8b822 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -2435,6 +2435,7 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c #: ports/zephyr-cp/common-hal/busio/I2C.c #: ports/zephyr-cp/common-hal/busio/SPI.c #: ports/zephyr-cp/common-hal/busio/UART.c diff --git a/locale/el.po b/locale/el.po index df024f2b1fe71..03a28bf863d77 100644 --- a/locale/el.po +++ b/locale/el.po @@ -2439,6 +2439,7 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c #: ports/zephyr-cp/common-hal/busio/I2C.c #: ports/zephyr-cp/common-hal/busio/SPI.c #: ports/zephyr-cp/common-hal/busio/UART.c diff --git a/locale/hi.po b/locale/hi.po index 48a8c6c26e16f..18e4eee167c38 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -2413,6 +2413,7 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c #: ports/zephyr-cp/common-hal/busio/I2C.c #: ports/zephyr-cp/common-hal/busio/SPI.c #: ports/zephyr-cp/common-hal/busio/UART.c diff --git a/locale/ko.po b/locale/ko.po index 63c64a6952d69..9f32555246864 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -2487,6 +2487,7 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c #: ports/zephyr-cp/common-hal/busio/I2C.c #: ports/zephyr-cp/common-hal/busio/SPI.c #: ports/zephyr-cp/common-hal/busio/UART.c diff --git a/locale/ru.po b/locale/ru.po index a7f7a35d58b7a..8f222fb253449 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -2472,6 +2472,7 @@ msgstr "Неподдерживаемый тип сокета" msgid "Update failed" msgstr "Обновление не удалось" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c #: ports/zephyr-cp/common-hal/busio/I2C.c #: ports/zephyr-cp/common-hal/busio/SPI.c #: ports/zephyr-cp/common-hal/busio/UART.c diff --git a/locale/tr.po b/locale/tr.po index 00f1c82065ac5..c35a97623c600 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -2435,6 +2435,7 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c #: ports/zephyr-cp/common-hal/busio/I2C.c #: ports/zephyr-cp/common-hal/busio/SPI.c #: ports/zephyr-cp/common-hal/busio/UART.c From ea772281519c10e9940b9a4da2326ccb6e8de725 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 3 Apr 2026 17:20:22 -0400 Subject: [PATCH 486/517] atmel-samd building and passing smoke tets --- extmod/vfs_fat.c | 71 ++++++++++++++++++ ports/stm/mpconfigport_nanbox.h | 25 ------- py/asmxtensa.h | 3 +- py/circuitpy_mpconfig.h | 6 +- py/emitcommon.c | 4 ++ py/malloc.c | 18 +++-- py/misc.h | 8 +-- py/mpconfig.h | 6 +- py/mpprint.c | 124 ++++++++++++++++---------------- py/obj.h | 24 ++++--- py/objarray.c | 47 ++++++------ py/objlist.c | 5 +- py/objmodule.c | 1 + py/objmodule.h | 3 + py/parsenum.c | 4 ++ py/runtime.c | 2 + py/runtime.h | 14 ++-- py/sequence.c | 4 +- tests/frozen/frozentest.mpy | Bin 200 -> 196 bytes 19 files changed, 224 insertions(+), 145 deletions(-) delete mode 100644 ports/stm/mpconfigport_nanbox.h diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index cb5fb649a1e5b..95ed79eb18bd5 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -455,6 +455,77 @@ static mp_obj_t vfs_fat_umount(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_umount_obj, vfs_fat_umount); +// CIRCUITPY-CHANGE +static mp_obj_t vfs_fat_utime(mp_obj_t vfs_in, mp_obj_t path_in, mp_obj_t times_in) { + mp_obj_fat_vfs_t *self = MP_OBJ_TO_PTR(vfs_in); + const char *path = mp_obj_str_get_str(path_in); + if (!mp_obj_is_tuple_compatible(times_in)) { + mp_raise_type_arg(&mp_type_TypeError, times_in); + } + + mp_obj_t *otimes; + mp_obj_get_array_fixed_n(times_in, 2, &otimes); + + // Validate that both elements of the tuple are int and discard the second one + int time[2]; + time[0] = mp_obj_get_int(otimes[0]); + time[1] = mp_obj_get_int(otimes[1]); + timeutils_struct_time_t tm; + timeutils_seconds_since_epoch_to_struct_time(time[0], &tm); + + FILINFO fno; + fno.fdate = (WORD)(((tm.tm_year - 1980) * 512U) | tm.tm_mon * 32U | tm.tm_mday); + fno.ftime = (WORD)(tm.tm_hour * 2048U | tm.tm_min * 32U | tm.tm_sec / 2U); + FRESULT res = f_utime(&self->fatfs, path, &fno); + if (res != FR_OK) { + mp_raise_OSError_fresult(res); + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_3(fat_vfs_utime_obj, vfs_fat_utime); + +static mp_obj_t vfs_fat_getreadonly(mp_obj_t self_in) { + fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(!filesystem_is_writable_by_python(self)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getreadonly_obj, vfs_fat_getreadonly); + +static MP_PROPERTY_GETTER(fat_vfs_readonly_obj, + (mp_obj_t)&fat_vfs_getreadonly_obj); + +#if MICROPY_FATFS_USE_LABEL +static mp_obj_t vfs_fat_getlabel(mp_obj_t self_in) { + fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); + char working_buf[12]; + FRESULT res = f_getlabel(&self->fatfs, working_buf, NULL); + if (res != FR_OK) { + mp_raise_OSError_fresult(res); + } + return mp_obj_new_str(working_buf, strlen(working_buf)); +} +static MP_DEFINE_CONST_FUN_OBJ_1(fat_vfs_getlabel_obj, vfs_fat_getlabel); + +static mp_obj_t vfs_fat_setlabel(mp_obj_t self_in, mp_obj_t label_in) { + fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); + verify_fs_writable(self); + const char *label_str = mp_obj_str_get_str(label_in); + FRESULT res = f_setlabel(&self->fatfs, label_str); + if (res != FR_OK) { + if (res == FR_WRITE_PROTECTED) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Read-only filesystem")); + } + mp_raise_OSError_fresult(res); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_setlabel_obj, vfs_fat_setlabel); + +static MP_PROPERTY_GETSET(fat_vfs_label_obj, + (mp_obj_t)&fat_vfs_getlabel_obj, + (mp_obj_t)&fat_vfs_setlabel_obj); +#endif + static const mp_rom_map_elem_t fat_vfs_locals_dict_table[] = { // CIRCUITPY-CHANGE: correct name #if FF_FS_REENTRANT diff --git a/ports/stm/mpconfigport_nanbox.h b/ports/stm/mpconfigport_nanbox.h deleted file mode 100644 index 164850112e01f..0000000000000 --- a/ports/stm/mpconfigport_nanbox.h +++ /dev/null @@ -1,25 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George -// -// SPDX-License-Identifier: MIT - -#pragma once - -// select nan-boxing object model -#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_D) - -// native emitters don't work with nan-boxing -#define MICROPY_EMIT_X86 (0) -#define MICROPY_EMIT_X64 (0) -#define MICROPY_EMIT_THUMB (0) -#define MICROPY_EMIT_ARM (0) - -#include - -typedef int64_t mp_int_t; -typedef uint64_t mp_uint_t; -#define UINT_FMT "%llu" -#define INT_FMT "%lld" - -#include diff --git a/py/asmxtensa.h b/py/asmxtensa.h index 559b3cacd5d45..ed98c9d730516 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -311,7 +311,8 @@ void asm_xtensa_l32r(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t label); #define ASM_XTENSA_REG_TEMPORARY ASM_XTENSA_REG_A6 #define ASM_XTENSA_REG_TEMPORARY_WIN ASM_XTENSA_REG_A12 -#if GENERIC_ASM_API +// CIRCUITPY-CHANGE: prevent #if warning +#if defined(GENERIC_ASM_API) && GENERIC_ASM_API // The following macros provide a (mostly) arch-independent API to // generate native code, and are used by the native emitter. diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index fc74144dc807e..e83c61752556e 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -17,6 +17,10 @@ // Always 1: defined in circuitpy_mpconfig.mk // #define CIRCUITPY (1) +#ifndef MP_SSIZE_MAX +#define MP_SSIZE_MAX (0x7fffffff) +#endif + // REPR_C encodes qstrs, 31-bit ints, and 30-bit floats in a single 32-bit word. #ifndef MICROPY_OBJ_REPR #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) @@ -303,12 +307,10 @@ typedef long mp_off_t; #ifdef LONGINT_IMPL_MPZ #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MP_SSIZE_MAX (0x7fffffff) #endif #ifdef LONGINT_IMPL_LONGLONG #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) -#define MP_SSIZE_MAX (0x7fffffff) #endif #ifndef MICROPY_PY_REVERSE_SPECIAL_METHODS diff --git a/py/emitcommon.c b/py/emitcommon.c index 1f701db80a0a9..1369c544c54f5 100644 --- a/py/emitcommon.c +++ b/py/emitcommon.c @@ -79,12 +79,16 @@ static bool strictly_equal(mp_obj_t a, mp_obj_t b) { #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_COMP_CONST_FLOAT if (a_type == &mp_type_float) { mp_float_t a_val = mp_obj_float_get(a); + // CIRCUITPY-CHANGE: ignore float equal warning + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" if (a_val == (mp_float_t)0.0) { // Although 0.0 == -0.0, they are not strictly_equal and // must be stored as two different constants in .mpy files mp_float_t b_val = mp_obj_float_get(b); return signbit(a_val) == signbit(b_val); } + #pragma GCC diagnostic pop } #endif return true; diff --git a/py/malloc.c b/py/malloc.c index c11fd5071452f..36eb10b7b85e7 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -70,6 +70,7 @@ #error MICROPY_ENABLE_FINALISER requires MICROPY_ENABLE_GC #endif +// CIRCUITPY-CHANGE: Add selective collect support to malloc to optimize GC for large buffers #if MICROPY_ENABLE_SELECTIVE_COLLECT #error MICROPY_ENABLE_SELECTIVE_COLLECT requires MICROPY_ENABLE_GC #endif @@ -125,32 +126,35 @@ void *m_malloc_helper(size_t num_bytes, uint8_t flags) { } void *m_malloc(size_t num_bytes) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: use helper return m_malloc_helper(num_bytes, M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); } void *m_malloc_maybe(size_t num_bytes) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: use helper return m_malloc_helper(num_bytes, M_MALLOC_COLLECT); } #if MICROPY_ENABLE_FINALISER void *m_malloc_with_finaliser(size_t num_bytes) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: use helper return m_malloc_helper(num_bytes, M_MALLOC_COLLECT | M_MALLOC_WITH_FINALISER); +} #endif void *m_malloc0(size_t num_bytes) { - // CIRCUITPY-CHANGE - return m_malloc_helper(num_bytes, M_MALLOC_ENSURE_ZEROED | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); + // CIRCUITPY-CHANGE: use helper + return m_malloc_helper(num_bytes, + (MICROPY_GC_CONSERVATIVE_CLEAR ? 0 : M_MALLOC_ENSURE_ZEROED) + | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); } -// CIRCUITPY-CHANGE: selective collect +// CIRCUITPY-CHANGE: add selective collect void *m_malloc_without_collect(size_t num_bytes) { return m_malloc_helper(num_bytes, M_MALLOC_RAISE_ERROR); } -// CIRCUITPY-CHANGE: selective collect +// CIRCUITPY-CHANGE: add selective collect void *m_malloc_maybe_without_collect(size_t num_bytes) { return m_malloc_helper(num_bytes, 0); diff --git a/py/misc.h b/py/misc.h index b1bdb9517a4fa..d5d7950574f90 100644 --- a/py/misc.h +++ b/py/misc.h @@ -35,7 +35,8 @@ #include #include #include -#if __cplusplus // Required on at least one compiler to get ULLONG_MAX +// CIRCUITPY-CHANGE: #ifdef instead of #if +#ifdef __cplusplus // Required on at least one compiler to get ULLONG_MAX #include #else #include @@ -84,7 +85,8 @@ typedef unsigned int uint; #if defined(_MSC_VER) || defined(__cplusplus) #define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)1) #else -// CIRCUITPY-CHANGE: defined()#if defined(__clang__) +// CIRCUITPY-CHANGE: defined() +#if defined(__clang__) #pragma GCC diagnostic ignored "-Wgnu-folding-constant" #endif #define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)sizeof(char[1 - 2 * !(cond)])) @@ -456,8 +458,6 @@ static inline uint32_t mp_popcount(uint32_t x) { #define mp_clzll(x) __builtin_clzll(x) #define mp_ctz(x) __builtin_ctz(x) #define mp_check(x) (x) -// CIRCUITPY-CHANGE: defined() -#if defined __has_builtin #if __has_builtin(__builtin_popcount) #define mp_popcount(x) __builtin_popcount(x) #else diff --git a/py/mpconfig.h b/py/mpconfig.h index e970ea3ba7b0e..0ef98009205ab 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2384,7 +2384,6 @@ typedef time_t mp_timestamp_t; #ifndef MP_NORETURN #define MP_NORETURN __attribute__((noreturn)) #endif -#endif // INT_FMT #if !MICROPY_PREVIEW_VERSION_2 #define NORETURN MP_NORETURN @@ -2435,6 +2434,11 @@ typedef time_t mp_timestamp_t; #define MP_COLD __attribute__((cold)) #endif +// CIRCUITPY-CHANGE: avoid undefined warnings +#ifndef MICROPY_HAL_HAS_STDIO_MODE_SWITCH +#define MICROPY_HAL_HAS_STDIO_MODE_SWITCH (0) +#endif + // To annotate that code is unreachable #ifndef MP_UNREACHABLE #if defined(__GNUC__) diff --git a/py/mpprint.c b/py/mpprint.c index 605b8544f7f62..b09ee398b6d05 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -408,14 +408,6 @@ int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, unsigned int } #endif -// CIRCUITPY-CHANGE -static int print_str_common(const mp_print_t *print, const char *str, int prec, size_t len, int flags, int fill, int width) { - if (prec >= 0 && (size_t)prec < len) { - len = prec; - } - return mp_print_strn(print, str, len, flags, fill, width); -} - int mp_printf(const mp_print_t *print, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -542,27 +534,19 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { qstr qst = va_arg(args, qstr); size_t len; const char *str = (const char *)qstr_data(qst, &len); - // CIRCUITPY-CHANGE - chrs += print_str_common(print, str, prec, len, flags, fill, width); - break; - } - // CIRCUITPY-CHANGE: new code to print compressed strings - case 'S': { - mp_rom_error_text_t arg = va_arg(args, mp_rom_error_text_t); - size_t len_with_nul = decompress_length(arg); - size_t len = len_with_nul - 1; - char str[len_with_nul]; - decompress(arg, str); - chrs += print_str_common(print, str, prec, len, flags, fill, width); + if (prec >= 0 && (size_t)prec < len) { + len = prec; + } + chrs += mp_print_strn(print, str, len, flags, fill, width); break; } case 's': { const char *str = va_arg(args, const char *); #ifndef NDEBUG // With debugging enabled, catch printing of null string pointers - if (str == NULL) { - // CIRCUITPY-CHANGE - str = "(null)"; + if (prec != 0 && str == NULL) { + chrs += mp_print_strn(print, "(null)", 6, flags, fill, width); + break; } #endif size_t len = strlen(str); @@ -572,42 +556,57 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { chrs += mp_print_strn(print, str, len, flags, fill, width); break; } - // CIRCUITPY-CHANGE: separate from p and P - case 'd': { - mp_int_t val; - if (long_arg) { - val = va_arg(args, long int); - } else { - val = va_arg(args, int); - } - chrs += mp_print_int(print, val, 1, 10, 'a', flags, fill, width); - break; - } + case 'd': + case 'p': + case 'P': case 'u': case 'x': case 'X': { - int base = 16 - ((*fmt + 1) & 6); // maps char u/x/X to base 10/16/16 - char fmt_c = (*fmt & 0xf0) - 'P' + 'A'; // maps char u/x/X to char a/a/A + char fmt_chr = *fmt; mp_uint_t val; - if (long_arg) { - val = va_arg(args, unsigned long int); + if (fmt_chr == 'p' || fmt_chr == 'P') { + val = va_arg(args, uintptr_t); + } + #if SUPPORT_LL_FORMAT + else if (long_long_arg) { + val = va_arg(args, unsigned long long); + } + #endif + #if SUPPORT_L_FORMAT + else if (long_arg) { + if (sizeof(long) != sizeof(mp_uint_t) && fmt_chr == 'd') { + val = va_arg(args, long); + } else { + val = va_arg(args, unsigned long); + } + } + #endif + else { + if (sizeof(int) != sizeof(mp_uint_t) && fmt_chr == 'd') { + val = va_arg(args, int); + } else { + val = va_arg(args, unsigned); + } + } + int base; + // Map format char x/p/X/P to a/a/A/A for hex letters. + // It doesn't matter what d/u map to. + char fmt_c = (fmt_chr & 0xf0) - 'P' + 'A'; + if (fmt_chr == 'd' || fmt_chr == 'u') { + base = 10; } else { - val = va_arg(args, unsigned int); + base = 16; } - chrs += mp_print_int(print, val, 0, base, fmt_c, flags, fill, width); + if (fmt_chr == 'p' || fmt_chr == 'P') { + #if SUPPORT_INT_BASE_PREFIX + chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags | PF_FLAG_SHOW_PREFIX, fill, width); + #else + chrs += mp_print_strn(print, "0x", 2, flags, fill, width); + #endif + } + chrs += mp_print_int(print, val, fmt_chr == 'd', base, fmt_c, flags, fill, width); break; } - case 'p': - case 'P': // don't bother to handle upcase for 'P' - // Use unsigned long int to work on both ILP32 and LP64 systems - // CIRCUITPY-CHANGE: print 0x prefix - #if SUPPORT_INT_BASE_PREFIX - chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags | PF_FLAG_SHOW_PREFIX, fill, width); - #else - print->print_strn(print->data, "0x", 2); - chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags, fill, width) + 2; - #endif - break; #if MICROPY_PY_BUILTINS_FLOAT case 'e': case 'E': @@ -624,18 +623,21 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { break; } #endif - // Because 'l' is eaten above, another 'l' means %ll. We need to support - // this length specifier for OBJ_REPR_D (64-bit NaN boxing). - // TODO Either enable this unconditionally, or provide a specific config var. - #if (MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D) || defined(_WIN64) - case 'l': { - unsigned long long int arg_value = va_arg(args, unsigned long long int); - ++fmt; - assert(*fmt == 'u' || *fmt == 'd' || !"unsupported fmt char"); - chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width); + + // CIRCUITPY-CHANGE: new format code to print compressed strings + case 'S': { + mp_rom_error_text_t arg = va_arg(args, mp_rom_error_text_t); + size_t len_with_nul = decompress_length(arg); + size_t len = len_with_nul - 1; + char str[len_with_nul]; + decompress(arg, str); + if (prec >= 0 && (size_t)prec < len) { + len = prec; + } + chrs += mp_print_strn(print, str, len, flags, fill, width); break; } - #endif + default: // if it's not %% then it's an unsupported format character assert(*fmt == '%' || !"unsupported fmt char"); diff --git a/py/obj.h b/py/obj.h index e85505ff3ae70..bddb86605e391 100644 --- a/py/obj.h +++ b/py/obj.h @@ -871,7 +871,7 @@ extern const mp_obj_type_t mp_type_bytearray; extern const mp_obj_type_t mp_type_memoryview; extern const mp_obj_type_t mp_type_float; extern const mp_obj_type_t mp_type_complex; -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add traceback support extern const mp_obj_type_t mp_type_traceback; extern const mp_obj_type_t mp_type_tuple; extern const mp_obj_type_t mp_type_list; @@ -933,7 +933,7 @@ extern const mp_obj_type_t mp_type_ImportError; extern const mp_obj_type_t mp_type_IndentationError; extern const mp_obj_type_t mp_type_IndexError; extern const mp_obj_type_t mp_type_KeyboardInterrupt; -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add ReloadException extern const mp_obj_type_t mp_type_ReloadException; extern const mp_obj_type_t mp_type_KeyError; extern const mp_obj_type_t mp_type_LookupError; @@ -941,9 +941,9 @@ extern const mp_obj_type_t mp_type_MemoryError; extern const mp_obj_type_t mp_type_NameError; extern const mp_obj_type_t mp_type_NotImplementedError; extern const mp_obj_type_t mp_type_OSError; -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add ConnectionError extern const mp_obj_type_t mp_type_ConnectionError; -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add BrokenPipeError extern const mp_obj_type_t mp_type_BrokenPipeError; extern const mp_obj_type_t mp_type_OverflowError; extern const mp_obj_type_t mp_type_RuntimeError; @@ -951,6 +951,8 @@ extern const mp_obj_type_t mp_type_StopAsyncIteration; extern const mp_obj_type_t mp_type_StopIteration; extern const mp_obj_type_t mp_type_SyntaxError; extern const mp_obj_type_t mp_type_SystemExit; +// CIRCUITPY-CHANGE: add TimeoutError +extern const mp_obj_type_t mp_type_TimeoutError; extern const mp_obj_type_t mp_type_TypeError; extern const mp_obj_type_t mp_type_UnicodeError; extern const mp_obj_type_t mp_type_ValueError; @@ -1075,10 +1077,10 @@ mp_obj_t mp_obj_new_str_from_utf8_vstr(vstr_t *vstr); // input data must be vali #endif mp_obj_t mp_obj_new_bytes_from_vstr(vstr_t *vstr); mp_obj_t mp_obj_new_bytes(const byte *data, size_t len); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine mp_obj_t mp_obj_new_bytes_of_zeros(size_t len); mp_obj_t mp_obj_new_bytearray(size_t n, const void *items); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine mp_obj_t mp_obj_new_bytearray_of_zeros(size_t n); mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items); #if MICROPY_PY_BUILTINS_FLOAT @@ -1114,7 +1116,7 @@ mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items); const mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in); const char *mp_obj_get_type_str(mp_const_obj_t o_in); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine #define mp_obj_get_type_qstr(o_in) (mp_obj_get_type((o_in))->name) bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo); // arguments should be type objects mp_obj_t mp_obj_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type); @@ -1122,7 +1124,7 @@ mp_obj_t mp_obj_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); void mp_obj_print(mp_obj_t o, mp_print_kind_t kind); void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine void mp_obj_print_exception_with_limit(const mp_print_t *print, mp_obj_t exc, mp_int_t limit); bool mp_obj_is_true(mp_obj_t arg); @@ -1187,7 +1189,7 @@ bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type); void mp_obj_exception_clear_traceback(mp_obj_t self_in); void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block); void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine mp_obj_t mp_obj_exception_get_traceback_obj(mp_obj_t self_in); mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in); mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); @@ -1264,7 +1266,7 @@ void mp_obj_tuple_del(mp_obj_t self_in); mp_int_t mp_obj_tuple_hash(mp_obj_t self_in); // list -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: public routine mp_obj_t mp_obj_list_clear(mp_obj_t self_in); mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg); mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value); @@ -1360,7 +1362,7 @@ typedef struct _mp_rom_obj_static_class_method_t { } mp_rom_obj_static_class_method_t; // property -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: extra args const mp_obj_t *mp_obj_property_get(mp_obj_t self_in, size_t *n_proxy); // sequence helpers diff --git a/py/objarray.c b/py/objarray.c index 5b63f693698f9..de6e158b05751 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -300,7 +300,7 @@ static void memoryview_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in); dest[0] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->typecode & TYPECODE_MASK, NULL)); } - // CIRCUITPY-CHANGE: prevent warning + // CIRCUITPY-CHANGE: add MICROPY_CPYTHON_COMPAT #if MICROPY_PY_BUILTINS_BYTES_HEX || MICROPY_CPYTHON_COMPAT else { // Need to forward to locals dict. @@ -500,35 +500,37 @@ static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) { return mp_const_none; } - // convert byte count to element count - size_t len = arg_bufinfo.len / sz; + size_t sz = mp_binary_get_size('@', self->typecode, NULL); - // make sure we have enough room to extend - // TODO: alloc policy; at the moment we go conservative - if (self->free < len) { - self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz); - self->free = 0; - } else { - self->free -= len; - } + // convert byte count to element count + size_t len = arg_bufinfo.len / sz; - // extend - mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte); - self->len += len; - } else { - // Otherwise argument must be an iterable of items to append - mp_obj_t iterable = mp_getiter(arg_in, NULL); - mp_obj_t item; - while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - array_append(self_in, item); + // make sure we have enough room to extend + // TODO: alloc policy; at the moment we go conservative + if (self->free < len) { + self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz); + self->free = 0; + + if (self_in == arg_in) { + // Get arg_bufinfo again in case self->items has moved + // + // (Note not possible to handle case that arg_in is a memoryview into self) + mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ); } + } else { + self->free -= len; } + + // extend + mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte); + self->len += len; + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj, array_extend); #endif -// CIRCUITPY-CHANGE: buffer_finder used belo +// CIRCUITPY-CHANGE: buffer_finder used below #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_CPYTHON_COMPAT static mp_obj_t buffer_finder(size_t n_args, const mp_obj_t *args, int direction, bool is_index) { mp_check_self(mp_obj_is_type(args[0], &mp_type_bytearray)); @@ -564,12 +566,9 @@ static mp_obj_t buffer_finder(size_t n_args, const mp_obj_t *args, int direction } else { return MP_OBJ_NEW_SMALL_INT(-1); } - } else { - self->free -= len; } return MP_OBJ_NEW_SMALL_INT(p - (const byte *)haystack_bufinfo.buf); } - // CIRCUITPY-CHANGE: provides find, rfind, index static mp_obj_t buffer_find(size_t n_args, const mp_obj_t *args) { return buffer_finder(n_args, args, 1, false); diff --git a/py/objlist.c b/py/objlist.c index beb647917d9bd..83bade273bf6f 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -221,7 +221,7 @@ static mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { if (value == MP_OBJ_NULL) { // delete // CIRCUITPY-CHANGE: handle subclassing - mp_obj_t args[2] = {MP_OBJ_FROM_PTR(self), index}; + mp_obj_t args[2] = {MP_OBJ_FROM_PTR(self_in), index}; list_pop(2, args); return mp_const_none; } else if (value == MP_OBJ_SENTINEL) { @@ -276,12 +276,11 @@ static mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { } // CIRCUITPY-CHANGE: provide version for C use outside this file -inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self_in, size_t index) { +inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index) { if (self->len == 0) { // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_IndexError_varg(MP_ERROR_TEXT("pop from empty %q"), MP_QSTR_list); } - size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); mp_obj_t ret = self->items[index]; self->len -= 1; memmove(self->items + index, self->items + index + 1, (self->len - index) * sizeof(mp_obj_t)); diff --git a/py/objmodule.c b/py/objmodule.c index 60b0368abac58..84b6a10f9abd4 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -208,6 +208,7 @@ mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible) { // the old behaviour of the u-prefix being used to force a built-in // import. // CIRCUITPY-CHANGE: Don't look for `ufoo`. + #endif return MP_OBJ_NULL; } diff --git a/py/objmodule.h b/py/objmodule.h index 78e4da3cac690..221392ccce431 100644 --- a/py/objmodule.h +++ b/py/objmodule.h @@ -32,6 +32,9 @@ // Only include module definitions when not doing qstr extraction, because the // qstr extraction stage also generates this module definition header file. #include "genhdr/moduledefs.h" +// CIRCUITPY-CHANGE: avoid undef warning +#else +#define MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES (0) #endif extern const mp_map_t mp_builtin_module_map; diff --git a/py/parsenum.c b/py/parsenum.c index 91340fd2a18d7..c52f040e1a210 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -226,9 +226,13 @@ typedef enum { // Helper to compute `num * (10.0 ** dec_exp)` mp_large_float_t mp_decimal_exp(mp_large_float_t num, int dec_exp) { + // CIRCUITPY-CHANGE: ignore float equal warning + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" if (dec_exp == 0 || num == (mp_large_float_t)(0.0)) { return num; } + #pragma GCC diagnostic pop #if MICROPY_FLOAT_FORMAT_IMPL == MICROPY_FLOAT_FORMAT_IMPL_EXACT diff --git a/py/runtime.c b/py/runtime.c index 0b62500cc2a10..81abc3de87570 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -1929,6 +1929,8 @@ MP_COLD MP_NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } +#endif + // CIRCUITPY-CHANGE: added MP_COLD MP_NORETURN void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; diff --git a/py/runtime.h b/py/runtime.h index 8bdd1982b96fc..22ed9c3eaf9b8 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -26,17 +26,23 @@ #ifndef MICROPY_INCLUDED_PY_RUNTIME_H #define MICROPY_INCLUDED_PY_RUNTIME_H +#include + #include "py/mpstate.h" #include "py/pystack.h" #include "py/cstack.h" +// CIRCUITPY-CHANGE +#include "supervisor/linker.h" +#include "supervisor/shared/translate/translate.h" + +// For use with mp_call_function_1_from_nlr_jump_callback. // Initialize an nlr_jump_callback_node_call_function_1_t struct for use with // nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback); #define MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, f, a) \ - nlr_jump_callback_node_call_function_1_t ctx = { \ - .func = (void (*)(void *))(f), \ - .arg = (a), \ - } + nlr_jump_callback_node_call_function_1_t ctx; \ + ctx.func = (void (*)(void *))(f); \ + ctx.arg = (a) typedef enum { MP_VM_RETURN_NORMAL, diff --git a/py/sequence.c b/py/sequence.c index 490f6d9c67253..33cc55eac4060 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -36,11 +36,11 @@ // CIRCUITPY-CHANGE: detect sequence overflow // Detect when a multiply causes an overflow. size_t mp_seq_multiply_len(size_t item_sz, size_t len) { - size_t new_len; + mp_int_t new_len; if (mp_mul_mp_int_t_overflow(item_sz, len, &new_len)) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); } - return new_len; + return (size_t) new_len; } // Implements backend of sequence * integer operation. Assumes elements are diff --git a/tests/frozen/frozentest.mpy b/tests/frozen/frozentest.mpy index c16da5d20fb73f0e09e3136812783ff305a25b0f..7f1163956bafeacef9bda191aa3376fbf589c350 100644 GIT binary patch delta 7 OcmX@Xc!Y5x(-8m**aGGN delta 12 TcmX@Yc!H7X|Gx=Gn79}KBo_qk From 4d7a17612e48fb0a4b66612d43009275ab93e56c Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 3 Apr 2026 22:32:06 -0400 Subject: [PATCH 487/517] fix ports/unix --- ports/unix/Makefile | 2 +- ports/unix/coverage.c | 73 ++++++++++++++++++++++++++------------ ports/unix/coveragecpp.cpp | 18 ++++++++-- ports/unix/main.c | 8 +++-- ports/unix/mphalport.h | 15 +++++++- py/bc.h | 2 +- py/mpconfig.h | 5 --- py/obj.h | 2 +- shared/runtime/pyexec.c | 3 +- 9 files changed, 91 insertions(+), 37 deletions(-) diff --git a/ports/unix/Makefile b/ports/unix/Makefile index fe73ae5278bf8..32e3b44b2273a 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -50,7 +50,7 @@ INC += -I$(BUILD) # compiler settings CWARN = -Wall -Werror // CIRCUITPY-CHANGE: add -Wno-missing-field-initializers -CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion +CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Wno-missing-field-initializers CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) # Force the use of 64-bits for file sizes in C library functions on 32-bit platforms. diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index ba20dba3595cd..49426f0f3e865 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -185,6 +185,8 @@ static void pairheap_test(size_t nops, int *ops) { mp_printf(&mp_plat_print, "\n"); } +// CIRCUITPY-CHANGE: not turned on in CircuitPython +#if MICROPY_SCHEDULER_STATIC_NODES static mp_sched_node_t mp_coverage_sched_node; static bool coverage_sched_function_continue; @@ -196,6 +198,7 @@ static void coverage_sched_function(mp_sched_node_t *node) { mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); } } +#endif // function to run extra tests for things that can't be checked by scripts static mp_obj_t extra_coverage(void) { @@ -589,6 +592,22 @@ static mp_obj_t extra_coverage(void) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } + // mp_obj_get_uint from a non-int object (should raise exception) + if (nlr_push(&nlr) == 0) { + mp_obj_get_uint(mp_const_none); + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + + // mp_obj_int_get_ll from a non-int object (should raise exception) + if (nlr_push(&nlr) == 0) { + mp_obj_get_ll(mp_const_none); + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + // call mp_obj_new_exception_args (it's a part of the public C API and not used in the core) mp_obj_print_exception(&mp_plat_print, mp_obj_new_exception_args(&mp_type_ValueError, 0, NULL)); } @@ -598,26 +617,6 @@ static mp_obj_t extra_coverage(void) { mp_emitter_warning(MP_PASS_CODE_SIZE, "test"); } - // format float - { - mp_printf(&mp_plat_print, "# format float\n"); - - // format with inadequate buffer size - char buf[5]; - mp_format_float(1, buf, sizeof(buf), 'g', 0, '+'); - mp_printf(&mp_plat_print, "%s\n", buf); - - // format with just enough buffer so that precision must be - // set from 0 to 1 twice - char buf2[8]; - mp_format_float(1, buf2, sizeof(buf2), 'g', 0, '+'); - mp_printf(&mp_plat_print, "%s\n", buf2); - - // format where precision is trimmed to avoid buffer overflow - mp_format_float(1, buf2, sizeof(buf2), 'e', 0, '+'); - mp_printf(&mp_plat_print, "%s\n", buf2); - } - // binary { mp_printf(&mp_plat_print, "# binary\n"); @@ -641,14 +640,26 @@ static mp_obj_t extra_coverage(void) { fun_bc.context = &context; fun_bc.child_table = NULL; fun_bc.bytecode = (const byte *)"\x01"; // just needed for n_state + #if MICROPY_PY_SYS_SETTRACE + struct _mp_raw_code_t rc = {}; + fun_bc.rc = &rc; + #endif mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, state, mp_obj_t, 1); code_state->fun_bc = &fun_bc; code_state->ip = (const byte *)"\x00"; // just needed for an invalid opcode code_state->sp = &code_state->state[0]; code_state->exc_sp_idx = 0; code_state->old_globals = NULL; + #if MICROPY_STACKLESS + code_state->prev = NULL; + #endif + #if MICROPY_PY_SYS_SETTRACE + code_state->prev_state = NULL; + code_state->frame = NULL; + #endif + mp_vm_return_kind_t ret = mp_execute_bytecode(code_state, MP_OBJ_NULL); - mp_printf(&mp_plat_print, "%d %d\n", ret, mp_obj_get_type(code_state->state[0]) == &mp_type_NotImplementedError); + mp_printf(&mp_plat_print, "%d %d\n", (int)ret, mp_obj_get_type(code_state->state[0]) == &mp_type_NotImplementedError); } // scheduler @@ -705,9 +716,25 @@ static mp_obj_t extra_coverage(void) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } mp_handle_pending(true); + + // CIRCUITPY-CHANGE: not turned on in CircuitPython + #if MICROPY_SCHEDULER_STATIC_NODES + coverage_sched_function_continue = true; + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + for (int i = 0; i < 3; ++i) { + mp_printf(&mp_plat_print, "loop\n"); + mp_handle_pending(true); + } + // Clear this flag to prevent the function scheduling itself again + coverage_sched_function_continue = false; + // Will only run the first time through this loop, then not scheduled again + for (int i = 0; i < 3; ++i) { + mp_handle_pending(true); + } + #endif } - // CIRCUITPY-CHANGE: ringbuf is different + // CIRCUITPY-CHANGE: ringbuf is quite different // ringbuf { #define RINGBUF_SIZE 99 @@ -719,7 +746,7 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# ringbuf\n"); // Single-byte put/get with empty ringbuf. - mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_free(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); ringbuf_put(&ringbuf, 22); mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_get(&ringbuf)); diff --git a/ports/unix/coveragecpp.cpp b/ports/unix/coveragecpp.cpp index 4d21398142d0f..8ba308f6468f3 100644 --- a/ports/unix/coveragecpp.cpp +++ b/ports/unix/coveragecpp.cpp @@ -1,6 +1,20 @@ extern "C" { -// CIRCUITPY-CHANGE: do not include everything: it causes compilation warnings -#include "py/obj.h" +// Include the complete public API to verify everything compiles as C++. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include } // Invoke all (except one, see below) public API macros which initialize structs to make sure diff --git a/ports/unix/main.c b/ports/unix/main.c index 89937008e0531..25444df41ce79 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -269,14 +269,18 @@ static inline int convert_pyexec_result(int ret) { } static int do_file(const char *file) { - return convert_pyexec_result(pyexec_file(file)); + // CIRCUITPY-CHANGE: pyexec_file result arg + pyexec_result_t pyexec_result; + return convert_pyexec_result(pyexec_file(file, &pyexec_result)); } static int do_str(const char *str) { vstr_t vstr; vstr.buf = (char *)str; vstr.len = strlen(str); - int ret = pyexec_vstr(&vstr, true); + // CIRCUITPY-CHANGE: pyexec_vstr result arg + pyexec_result_t pyexec_result; + int ret = pyexec_vstr(&vstr, true, &pyexec_result); return convert_pyexec_result(ret); } diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index afac25bf86529..d9cd05b3de82b 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -25,7 +25,6 @@ */ #include #include -// CIRCUITPY-CHANGE: extra include #include #ifndef CHAR_CTRL_C @@ -38,6 +37,20 @@ #define MICROPY_END_ATOMIC_SECTION(x) (void)x; mp_thread_unix_end_atomic_section() #endif +// In lieu of a WFI(), slow down polling from being a tight loop. +// +// Note that we don't delay for the full TIMEOUT_MS, as execution +// can't be woken from the delay. +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) \ + do { \ + MP_THREAD_GIL_EXIT(); \ + mp_hal_delay_us(500); \ + MP_THREAD_GIL_ENTER(); \ + } while (0) + +// The port provides `mp_hal_stdio_mode_raw()` and `mp_hal_stdio_mode_orig()`. +#define MICROPY_HAL_HAS_STDIO_MODE_SWITCH (1) + // CIRCUITPY-CHANGE: mp_hal_set_interrupt_char(int) instead of char void mp_hal_set_interrupt_char(int c); bool mp_hal_is_interrupted(void); diff --git a/py/bc.h b/py/bc.h index c299560ef2b79..3b7a6fe1c960b 100644 --- a/py/bc.h +++ b/py/bc.h @@ -304,7 +304,7 @@ static inline void mp_module_context_alloc_tables(mp_module_context_t *context, size_t nq = (n_qstr * sizeof(qstr_short_t) + sizeof(mp_uint_t) - 1) / sizeof(mp_uint_t); size_t no = n_obj; // CIRCUITPY-CHANGE - mp_uint_t *mem = m_malloc_items(nq + no); + mp_uint_t *mem = (mp_uint_t *)m_malloc_items(nq + no); context->constants.qstr_table = (qstr_short_t *)mem; context->constants.obj_table = (mp_obj_t *)(mem + nq); #else diff --git a/py/mpconfig.h b/py/mpconfig.h index 0ef98009205ab..522f6150dfd61 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -2434,11 +2434,6 @@ typedef time_t mp_timestamp_t; #define MP_COLD __attribute__((cold)) #endif -// CIRCUITPY-CHANGE: avoid undefined warnings -#ifndef MICROPY_HAL_HAS_STDIO_MODE_SWITCH -#define MICROPY_HAL_HAS_STDIO_MODE_SWITCH (0) -#endif - // To annotate that code is unreachable #ifndef MP_UNREACHABLE #if defined(__GNUC__) diff --git a/py/obj.h b/py/obj.h index bddb86605e391..eb7143bd43f62 100644 --- a/py/obj.h +++ b/py/obj.h @@ -408,7 +408,7 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; {.base = {.type = &mp_type_fun_builtin_var}, .sig = MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, false), .fun = {.var = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, n_args_min, fun_name) \ const mp_obj_fun_builtin_var_t obj_name = \ - {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun.kw = fun_name} + {.base = {.type = &mp_type_fun_builtin_var}, .sig = MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun = {.kw = fun_name}} // CIRCUITPY-CHANGE #define MP_DEFINE_CONST_PROP_GET(obj_name, fun_name) \ diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index b554199eeec6b..65edb58e6b62c 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -570,7 +570,8 @@ MP_REGISTER_ROOT_POINTER(vstr_t * repl_line); #else // MICROPY_REPL_EVENT_DRIVEN -#if !MICROPY_HAL_HAS_STDIO_MODE_SWITCH +// CIRCUITPY-CHANGE: avoid warnings +#if defined(MICROPY_HAL_HAS_STDIO_MODE_SWITCH) && !MICROPY_HAL_HAS_STDIO_MODE_SWITCH // If the port doesn't need any stdio mode switching calls then provide trivial ones. static inline void mp_hal_stdio_mode_raw(void) { } From d28f54587c4db5778ff63ce7b0dc4f70eaa71e6f Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 1 Apr 2026 13:36:16 -0700 Subject: [PATCH 488/517] Add Zephyr build for the Feather nrf52840 Sense --- .../autogen_board_info.toml | 120 ++++++++++++++++++ .../circuitpython.toml | 4 + .../autogen_board_info.toml | 2 +- .../circuitpython.toml | 1 + ...t_feather_nrf52840_nrf52840_sense_uf2.conf | 10 ++ ...eather_nrf52840_nrf52840_sense_uf2.overlay | 46 +++++++ ports/zephyr-cp/boards/board_aliases.cmake | 1 + .../zephyr-cp/cptools/build_circuitpython.py | 21 +-- ports/zephyr-cp/cptools/zephyr2cp.py | 4 +- 9 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf create mode 100644 ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..cfcae40090624 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Adafruit Industries LLC Feather Bluefruit Sense" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..eacb0f9607df9 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/circuitpython.toml @@ -0,0 +1,4 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] +USB_VID=0x239A +USB_PID=0x8088 +NAME="Feather Bluefruit Sense" diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 7b1fb9a0d6f55..4f56f4d203e1e 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -1,5 +1,5 @@ # This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. -name = "Adafruit Industries LLC Feather nRF52840 (Express, Sense)" +name = "Adafruit Industries LLC Feather nRF52840 Express" [modules] __future__ = true diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml index e1a16cb74aa46..c4d1099a77e1d 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml @@ -1,3 +1,4 @@ CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] USB_VID=0x239A USB_PID=0x802A +NAME="Feather nRF52840 Express" diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf new file mode 100644 index 0000000000000..20176b34be052 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf @@ -0,0 +1,10 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_USE_DT_CODE_PARTITION=y + +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay new file mode 100644 index 0000000000000..e01ebd6df1f51 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay @@ -0,0 +1,46 @@ +/ { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + zephyr,bt-mon-uart = &uart0; + zephyr,bt-c2h-uart = &uart0; + }; +}; + +&zephyr_udc0 { + /delete-node/ board_cdc_acm_uart; +}; + + +&gd25q16 { + /delete-node/ partitions; +}; + +/delete-node/ &storage_partition; +/delete-node/ &code_partition; + +&flash0 { + partitions { + code_partition: partition@26000 { + label = "Application"; + reg = <0x00026000 0x000c4000>; + }; + + storage_partition: partition@ea000 { + label = "storage"; + reg = <0x000ea000 0x00008000>; + }; + + nvm_partition: partition@f2000 { + label = "nvm"; + reg = <0x000f2000 0x00002000>; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index e973eac72d4a5..e20a7b0cc62d7 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -28,6 +28,7 @@ endmacro() cp_board_alias(pca10056 nrf52840dk/nrf52840) cp_board_alias(adafruit_feather_nrf52840_zephyr adafruit_feather_nrf52840/nrf52840/uf2) +cp_board_alias(adafruit_feather_nrf52840_sense_zephyr adafruit_feather_nrf52840/nrf52840/sense/uf2) cp_board_alias(renesas_ek_ra6m5 ek_ra6m5) cp_board_alias(renesas_ek_ra8d1 ek_ra8d1) cp_board_alias(renesas_da14695_dk_usb da14695_dk_usb) diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 63a2ed9635142..772c805a50143 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -362,6 +362,11 @@ async def build_circuitpython(): genhdr = builddir / "genhdr" genhdr.mkdir(exist_ok=True, parents=True) version_header = genhdr / "mpversion.h" + mpconfigboard_fn = board_tools.find_mpconfigboard(portdir, board) + mpconfigboard = {"USB_VID": 0x1209, "USB_PID": 0x000C, "USB_INTERFACE_NAME": "CircuitPython"} + if mpconfigboard_fn is not None and mpconfigboard_fn.exists(): + with mpconfigboard_fn.open("rb") as f: + mpconfigboard.update(tomllib.load(f)) async with asyncio.TaskGroup() as tg: tg.create_task( cpbuild.run_command( @@ -379,11 +384,9 @@ async def build_circuitpython(): ) board_autogen_task = tg.create_task( - zephyr_dts_to_cp_board(zephyr_board, portdir, builddir, zephyrbuilddir) + zephyr_dts_to_cp_board(zephyr_board, portdir, builddir, zephyrbuilddir, mpconfigboard) ) board_info = board_autogen_task.result() - mpconfigboard_fn = board_tools.find_mpconfigboard(portdir, board) - mpconfigboard = {"USB_VID": 0x1209, "USB_PID": 0x000C, "USB_INTERFACE_NAME": "CircuitPython"} if mpconfigboard_fn is None: mpconfigboard_fn = ( portdir / "boards" / board_info["vendor_id"] / board / "circuitpython.toml" @@ -391,9 +394,6 @@ async def build_circuitpython(): logging.warning( f"Could not find board config at: boards/{board_info['vendor_id']}/{board}" ) - elif mpconfigboard_fn.exists(): - with mpconfigboard_fn.open("rb") as f: - mpconfigboard.update(tomllib.load(f)) autogen_board_info_fn = mpconfigboard_fn.parent / "autogen_board_info.toml" @@ -402,6 +402,9 @@ async def build_circuitpython(): circuitpython_flags.append(f"-DCIRCUITPY_CREATOR_ID=0x{creator_id:08x}") circuitpython_flags.append(f"-DCIRCUITPY_CREATION_ID=0x{creation_id:08x}") + vendor = mpconfigboard.get("VENDOR", board_info["vendor"]) + name = mpconfigboard.get("NAME", board_info["name"]) + enabled_modules, module_reasons = determine_enabled_modules(board_info, portdir, srcdir) web_workflow_enabled = board_info.get("wifi", False) or board_info.get("hostnetwork", False) @@ -459,8 +462,8 @@ async def build_circuitpython(): f"-DUSB_INTERFACE_NAME='\"{mpconfigboard['USB_INTERFACE_NAME']}\"'" ) for macro, limit, value in ( - ("USB_PRODUCT", 16, board_info["name"]), - ("USB_MANUFACTURER", 8, board_info["vendor"]), + ("USB_PRODUCT", 16, name), + ("USB_MANUFACTURER", 8, vendor), ): circuitpython_flags.append(f"-D{macro}='\"{value}\"'") circuitpython_flags.append(f"-D{macro}_{limit}='\"{value[:limit]}\"'") @@ -512,7 +515,7 @@ async def build_circuitpython(): "This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info." ) ) - autogen_board_info.add("name", board_info["vendor"] + " " + board_info["name"]) + autogen_board_info.add("name", vendor + " " + name) autogen_modules = tomlkit.table() autogen_board_info.add("modules", autogen_modules) for module in sorted( diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index 08a16da0cf6fa..d40501f1d1d00 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -433,7 +433,7 @@ def find_ram_regions(device_tree): @cpbuild.run_in_thread -def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa: C901 +def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfigboard=None): # noqa: C901 board_dir = builddir / "board" # Auto generate board files from device tree. @@ -486,6 +486,8 @@ def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir): # noqa soc_name = board_yaml["socs"][0]["name"] board_info["soc"] = soc_name board_name = board_yaml["full_name"] + if mpconfigboard and "NAME" in mpconfigboard: + board_name = mpconfigboard["NAME"] board_info["name"] = board_name # board_id_yaml = zephyr_board_dir / (zephyr_board_dir.name + ".yaml") # board_id_yaml = yaml.safe_load(board_id_yaml.read_text()) From 5df11054c6382209fc4d8650bfaec844c4953a3a Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 6 Apr 2026 21:54:11 -0400 Subject: [PATCH 489/517] fix some tests; allow running tests selectivly with --- Makefile | 3 ++- ports/unix/main.c | 9 ++++++++- shared/runtime/pyexec.c | 2 +- tests/basics/io_buffered_writer.py | 1 - 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c2aebc61f1c0b..175a1b6a3025f 100644 --- a/Makefile +++ b/Makefile @@ -375,5 +375,6 @@ coverage-fresh: make -j -C ports/unix VARIANT=coverage .PHONY: run-tests +# If TESTS="abc.py def.py" is specified as an arg, run only those tests. Otherwise, run all tests. run-tests: - cd tests; MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py + cd tests; MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py $(TESTS) diff --git a/ports/unix/main.c b/ports/unix/main.c index 25444df41ce79..81dc531d6bfde 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -768,7 +768,14 @@ MP_NOINLINE int main_(int argc, char **argv) { #endif // printf("total bytes = %d\n", m_get_total_bytes_allocated()); - return ret & 0xff; + + // CIRCUITPY-CHANGE: handle PYEXEC_EXCEPTION + if (ret & PYEXEC_EXCEPTION) { + // Return exit status code 1 so the invoker knows there was an uncaught exception. + return 1; + } else { + return ret & 0xff; + } } void nlr_jump_fail(void *val) { diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 65edb58e6b62c..530e5d27b906f 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -118,7 +118,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input // source is a lexer, parse and compile the script qstr source_name = lex->source_name; // CIRCUITPY-CHANGE - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ if (input_kind == MP_PARSE_FILE_INPUT) { mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); } diff --git a/tests/basics/io_buffered_writer.py b/tests/basics/io_buffered_writer.py index 31d3eb489c452..3cfee0103f777 100644 --- a/tests/basics/io_buffered_writer.py +++ b/tests/basics/io_buffered_writer.py @@ -1,7 +1,6 @@ try: import io -try: io.BytesIO io.BufferedWriter except (AttributeError, ImportError): From 8a115a2ac1c3a06fef28652d4e429d9e9cedf0ad Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 7 Apr 2026 09:26:30 -0500 Subject: [PATCH 490/517] board def for feather rp2040 --- .../autogen_board_info.toml | 120 ++++++++++++++++++ .../feather_rp2040_zephyr/circuitpython.toml | 1 + .../boards/adafruit_feather_rp2040.conf | 1 + .../boards/adafruit_feather_rp2040.overlay | 34 +++++ ports/zephyr-cp/boards/board_aliases.cmake | 1 + 5 files changed, 157 insertions(+) create mode 100644 ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml create mode 100644 ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/circuitpython.toml create mode 100644 ports/zephyr-cp/boards/adafruit_feather_rp2040.conf create mode 100644 ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..116a604f857ab --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Adafruit Industries LLC Feather RP2040" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = false +adafruit_pixelbuf = false +aesio = false +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = false +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = false +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..9d3c229ed1b45 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] diff --git a/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf b/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf new file mode 100644 index 0000000000000..81c492692304b --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf @@ -0,0 +1 @@ +CONFIG_GPIO=y \ No newline at end of file diff --git a/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay b/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay new file mode 100644 index 0000000000000..ce9083dd62d42 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay @@ -0,0 +1,34 @@ +&flash0 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Reserved memory for the second stage bootloader */ + second_stage_bootloader: partition@0 { + label = "second_stage_bootloader"; + reg = <0x00000000 0x100>; + read-only; + }; + + code_partition: partition@100 { + label = "code-partition"; + reg = <0x100 (0x180000 - 0x100)>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(2) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index e973eac72d4a5..d1064e55e8f55 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -28,6 +28,7 @@ endmacro() cp_board_alias(pca10056 nrf52840dk/nrf52840) cp_board_alias(adafruit_feather_nrf52840_zephyr adafruit_feather_nrf52840/nrf52840/uf2) +cp_board_alias(adafruit_feather_rp2040_zephyr adafruit_feather_rp2040/rp2040) cp_board_alias(renesas_ek_ra6m5 ek_ra6m5) cp_board_alias(renesas_ek_ra8d1 ek_ra8d1) cp_board_alias(renesas_da14695_dk_usb da14695_dk_usb) From e9d6e54365fd7e72f219fc0bd4ff18f61a9a90b6 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 7 Apr 2026 11:02:41 -0500 Subject: [PATCH 491/517] update feather rp2040 zephry board autogen for nvm --- .../adafruit/feather_rp2040_zephyr/autogen_board_info.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml index 116a604f857ab..e2fdd50530406 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -73,7 +73,7 @@ microcontroller = true mipidsi = false msgpack = false neopixel_write = false -nvm = false +nvm = true # Zephyr board has nvm onewireio = false os = true paralleldisplaybus = false From e834f0b17fa15975cbcd7b35ff57616cc1f6adae Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 7 Apr 2026 11:08:06 -0500 Subject: [PATCH 492/517] eof newline --- ports/zephyr-cp/boards/adafruit_feather_rp2040.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf b/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf index 81c492692304b..91c3c15b37d1e 100644 --- a/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf +++ b/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf @@ -1 +1 @@ -CONFIG_GPIO=y \ No newline at end of file +CONFIG_GPIO=y From be90f6040cde656b0ee897cfd05c6efd3aadce2b Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 7 Apr 2026 13:46:15 -0500 Subject: [PATCH 493/517] enable msgpack in zephyr port and add test for it. fix dict order issue in msgpack unpack. --- .../autogen_board_info.toml | 2 +- .../native/native_sim/autogen_board_info.toml | 4 +- .../zephyr-cp/cptools/build_circuitpython.py | 1 + ports/zephyr-cp/tests/test_msgpack.py | 137 ++++++++++++++++++ shared-module/msgpack/__init__.c | 8 +- 5 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 ports/zephyr-cp/tests/test_msgpack.py diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml index e2fdd50530406..05cc100c6e55a 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = true # Zephyr board has nvm onewireio = false diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 327c4cbb7e5c6..4b657282ef609 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -56,7 +56,7 @@ i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false imagecapture = false -ipaddress = false +ipaddress = true # Zephyr networking enabled is31fl3741 = false jpegio = false keypad = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = true # Zephyr board has nvm onewireio = false diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 63a2ed9635142..73918a06a5abb 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -60,6 +60,7 @@ "errno", "io", "math", + "msgpack", ] # Flags that don't match with with a *bindings module. Some used by adafruit_requests MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math"] diff --git a/ports/zephyr-cp/tests/test_msgpack.py b/ports/zephyr-cp/tests/test_msgpack.py new file mode 100644 index 0000000000000..09bed35209c0a --- /dev/null +++ b/ports/zephyr-cp/tests/test_msgpack.py @@ -0,0 +1,137 @@ +# SPDX-FileCopyrightText: 2026 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test the msgpack module.""" + +import pytest + + +ROUNDTRIP_CODE = """\ +import msgpack +from io import BytesIO + +obj = {"list": [True, False, None, 1, 3.125], "str": "blah"} +b = BytesIO() +msgpack.pack(obj, b) +encoded = b.getvalue() +print(f"encoded_len: {len(encoded)}") +print(f"encoded_hex: {encoded.hex()}") + +b.seek(0) +decoded = msgpack.unpack(b) +print(f"decoded: {decoded}") +print(f"match: {decoded == obj}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": ROUNDTRIP_CODE}) +def test_msgpack_roundtrip(circuitpython): + """Pack and unpack a dict containing the basic msgpack types.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "match: True" in output + assert "done" in output + + +USE_LIST_CODE = """\ +import msgpack +from io import BytesIO + +b = BytesIO() +msgpack.pack([1, 2, 3], b) + +b.seek(0) +as_list = msgpack.unpack(b) +print(f"as_list: {as_list} type={type(as_list).__name__}") + +b.seek(0) +as_tuple = msgpack.unpack(b, use_list=False) +print(f"as_tuple: {as_tuple} type={type(as_tuple).__name__}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": USE_LIST_CODE}) +def test_msgpack_use_list(circuitpython): + """use_list=False should return a tuple instead of a list.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "as_list: [1, 2, 3] type=list" in output + assert "as_tuple: (1, 2, 3) type=tuple" in output + assert "done" in output + + +EXTTYPE_CODE = """\ +from msgpack import pack, unpack, ExtType +from io import BytesIO + +class MyClass: + def __init__(self, val): + self.value = val + +data = MyClass(b"my_value") + +def encoder(obj): + if isinstance(obj, MyClass): + return ExtType(1, obj.value) + return f"no encoder for {obj}" + +def decoder(code, data): + if code == 1: + return MyClass(data) + return f"no decoder for type {code}" + +buf = BytesIO() +pack(data, buf, default=encoder) +buf.seek(0) +decoded = unpack(buf, ext_hook=decoder) +print(f"decoded_type: {type(decoded).__name__}") +print(f"decoded_value: {decoded.value}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": EXTTYPE_CODE}) +def test_msgpack_exttype(circuitpython): + """ExtType with a custom encoder/decoder should round-trip.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "decoded_type: MyClass" in output + assert "decoded_value: b'my_value'" in output + assert "done" in output + + +EXTTYPE_PROPS_CODE = """\ +from msgpack import ExtType + +e = ExtType(5, b"hello") +print(f"code: {e.code}") +print(f"data: {e.data}") + +e.code = 10 +print(f"new_code: {e.code}") + +try: + ExtType(128, b"x") +except (ValueError, OverflowError) as ex: + print(f"range_error: {type(ex).__name__}") + +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": EXTTYPE_PROPS_CODE}) +def test_msgpack_exttype_properties(circuitpython): + """ExtType exposes code/data as read/write properties and rejects out-of-range codes.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "code: 5" in output + assert "data: b'hello'" in output + assert "new_code: 10" in output + assert "range_error:" in output + assert "done" in output diff --git a/shared-module/msgpack/__init__.c b/shared-module/msgpack/__init__.c index a98fb02de39dd..7c3e664bb6c1e 100644 --- a/shared-module/msgpack/__init__.c +++ b/shared-module/msgpack/__init__.c @@ -394,7 +394,9 @@ static mp_obj_t unpack(msgpack_stream_t *s, mp_obj_t ext_hook, bool use_list) { size_t len = code & 0b1111; mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(len)); for (size_t i = 0; i < len; i++) { - mp_obj_dict_store(d, unpack(s, ext_hook, use_list), unpack(s, ext_hook, use_list)); + mp_obj_t key = unpack(s, ext_hook, use_list); + mp_obj_t value = unpack(s, ext_hook, use_list); + mp_obj_dict_store(d, key, value); } return MP_OBJ_FROM_PTR(d); } @@ -462,7 +464,9 @@ static mp_obj_t unpack(msgpack_stream_t *s, mp_obj_t ext_hook, bool use_list) { size_t len = read_size(s, code - 0xde + 1); mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(len)); for (size_t i = 0; i < len; i++) { - mp_obj_dict_store(d, unpack(s, ext_hook, use_list), unpack(s, ext_hook, use_list)); + mp_obj_t key = unpack(s, ext_hook, use_list); + mp_obj_t value = unpack(s, ext_hook, use_list); + mp_obj_dict_store(d, key, value); } return MP_OBJ_FROM_PTR(d); } From 13b1a5edcdc9d4950589c5d489870f872debf51f Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 8 Apr 2026 20:44:12 -0400 Subject: [PATCH 494/517] redo pyexec.c to be more like upstream; fix a bunch of tests --- ports/unix/main.c | 9 +- ports/unix/mpconfigport.h | 1 + py/objarray.c | 4 +- shared/runtime/pyexec.c | 194 +++++++++++------- tests/cmdline/repl_autocomplete.py.exp | 6 +- tests/cmdline/repl_autocomplete_underscore.py | 10 +- .../repl_autocomplete_underscore.py.exp | 30 +-- tests/cmdline/repl_autoindent.py.exp | 16 +- tests/cmdline/repl_basic.py.exp | 6 +- tests/cmdline/repl_cont.py.exp | 6 +- tests/cmdline/repl_emacs_keys.py.exp | 6 +- tests/cmdline/repl_inspect.py.exp | 6 +- tests/cmdline/repl_lock.py.exp | 4 +- tests/cmdline/repl_micropyinspect.py.exp | 6 +- tests/cmdline/repl_paste.py.exp | 94 ++++----- tests/cmdline/repl_sys_ps1_ps2.py.exp | 4 +- tests/cmdline/repl_words_move.py.exp | 10 +- tests/micropython/opt_level_lineno.py | 14 +- 18 files changed, 243 insertions(+), 183 deletions(-) diff --git a/ports/unix/main.c b/ports/unix/main.c index 81dc531d6bfde..e42400fe5f9a3 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -92,7 +92,8 @@ static void stderr_print_strn(void *env, const char *str, size_t len) { const mp_print_t mp_stderr_print = {NULL, stderr_print_strn}; -#define FORCED_EXIT (0x100) +// CIRCUITPY-CHANGE: be consistent about using PYEXEC_FORCED_EXIT +// #define FORCED_EXIT (0x100) // If exc is SystemExit, return value where FORCED_EXIT bit set, // and lower 8 bits are SystemExit value. For all other exceptions, // return 1. @@ -105,7 +106,8 @@ static int handle_uncaught_exception(mp_obj_base_t *exc) { if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, &val)) { val = 1; } - return FORCED_EXIT | (val & 255); + // CIRCUITPY-CHANGE: be consistent about using PYEXEC_FORCED_EXIT + return PYEXEC_FORCED_EXIT | (val & 255); } // Report all other exceptions @@ -236,7 +238,8 @@ static int do_repl(void) { int ret = execute_from_lexer(LEX_SRC_STR, line, MP_PARSE_SINGLE_INPUT, true); free(line); - if (ret & FORCED_EXIT) { + // CIRCUITPY-CHANGE: be consistent about using PYEXEC_FORCED_EXIT + if (ret & PYEXEC_FORCED_EXIT) { return ret; } } diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 991db97f921f2..815be76b4e90c 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -44,6 +44,7 @@ // CIRCUITPY-CHANGE #define CIRCUITPY_MICROPYTHON_ADVANCED (1) #define MICROPY_PY_ASYNC_AWAIT (1) +#define MICROPY_PY_DOUBLE_TYPECODE (1) #define MICROPY_PY_UCTYPES (0) #ifndef MICROPY_CONFIG_ROM_LEVEL diff --git a/py/objarray.c b/py/objarray.c index de6e158b05751..1e259a20ac8cb 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -807,7 +807,7 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_DEFINE_CONST_OBJ_TYPE( mp_type_bytearray, MP_QSTR_bytearray, - MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER | MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE, make_new, bytearray_make_new, print, array_print, iter, array_iterator_new, @@ -846,7 +846,7 @@ MP_DEFINE_CONST_DICT(memoryview_locals_dict, memoryview_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( mp_type_memoryview, MP_QSTR_memoryview, - MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER | MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE, make_new, memoryview_make_new, iter, array_iterator_new, unary_op, array_unary_op, diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 530e5d27b906f..09f1796c5d4d6 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -43,7 +43,7 @@ #include "extmod/modplatform.h" #include "genhdr/mpversion.h" -// CIRCUITPY-CHANGE: atexit support +// CIRCUITPY-CHANGE: add atexit support #if CIRCUITPY_ATEXIT #include "shared-module/atexit/__init__.h" #endif @@ -84,57 +84,71 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input nlr_buf_t nlr; nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { - // CIRCUITPY-CHANGE - mp_obj_t module_fun = mp_const_none; - // CIRCUITPY-CHANGE + #if MICROPY_PYEXEC_ENABLE_VM_ABORT + nlr_set_abort(&nlr); + #endif + + // CIRCUITPY-CHANGE: move declaration for easier handling of atexit #if. + // Also make it possible to determine if module_fun was set. + mp_obj_t module_fun = NULL; + + // CIRCUITPY-CHANGE: add atexit support #if CIRCUITPY_ATEXIT - if (!(exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT)) + if (exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT) { + atexit_callback_t *callback = (atexit_callback_t *)source; + mp_call_function_n_kw(callback->func, callback->n_pos, callback->n_kw, callback->args); + } else #endif - // CIRCUITPY-CHANGE: multiple code changes - { - #if MICROPY_MODULE_FROZEN_MPY - if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { - // source is a raw_code object, create the function - const mp_frozen_module_t *frozen = source; - mp_module_context_t *ctx = m_new_obj(mp_module_context_t); - ctx->module.globals = mp_globals_get(); - ctx->constants = frozen->constants; - module_fun = mp_make_function_from_proto_fun(frozen->proto_fun, ctx, NULL); - } else - #endif - { - #if MICROPY_ENABLE_COMPILER - mp_lexer_t *lex; - if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { - const vstr_t *vstr = source; - lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); - } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { - lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source); - } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { - lex = mp_lexer_new_from_file(qstr_from_str(source)); - } else { - lex = (mp_lexer_t *)source; - } - // source is a lexer, parse and compile the script - qstr source_name = lex->source_name; - // CIRCUITPY-CHANGE - #if MICROPY_MODULE___FILE__ - if (input_kind == MP_PARSE_FILE_INPUT) { - mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); - } - #endif - mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); - #else - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported")); - #endif + #if MICROPY_MODULE_FROZEN_MPY + if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { + // source is a raw_code object, create the function + const mp_frozen_module_t *frozen = source; + mp_module_context_t *ctx = m_new_obj(mp_module_context_t); + ctx->module.globals = mp_globals_get(); + ctx->constants = frozen->constants; + module_fun = mp_make_function_from_proto_fun(frozen->proto_fun, ctx, NULL); + } else + #endif + { + #if MICROPY_ENABLE_COMPILER + mp_lexer_t *lex; + if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { + const vstr_t *vstr = source; + lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); + } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { + lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source); + } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { + lex = mp_lexer_new_from_file(qstr_from_str(source)); + } else { + lex = (mp_lexer_t *)source; } - - // If the code was loaded from a file, collect any garbage before running. + // source is a lexer, parse and compile the script + qstr source_name = lex->source_name; + #if MICROPY_MODULE___FILE__ if (input_kind == MP_PARSE_FILE_INPUT) { - gc_collect(); + mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); + } + #endif + mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); + #if defined(MICROPY_UNIX_COVERAGE) + // allow to print the parse tree in the coverage build + if (mp_verbose_flag >= 3) { + printf("----------------\n"); + mp_parse_node_print(&mp_plat_print, parse_tree.root, 0); + printf("----------------\n"); } + #endif + module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); + #else + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported")); + #endif + } + + // CIRCUITPY-CHANGE: garbage collect after loading + // If the code was loaded from a file, collect any garbage before running. + if (input_kind == MP_PARSE_FILE_INPUT) { + gc_collect(); } // execute code @@ -144,22 +158,19 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input #if MICROPY_REPL_INFO start = mp_hal_ticks_ms(); #endif - // CIRCUITPY-CHANGE - #if CIRCUITPY_ATEXIT - if (exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT) { - atexit_callback_t *callback = (atexit_callback_t *)source; - mp_call_function_n_kw(callback->func, callback->n_pos, callback->n_kw, callback->args); - } else + #if MICROPY_PYEXEC_COMPILE_ONLY + if (!mp_compile_only) #endif - // CIRCUITPY-CHANGE - if (module_fun != mp_const_none) { - mp_call_function_0(module_fun); + { + // CIRCUITPY-CHANGE: if atexit function was called, there is nothing to call. + if (module_fun != NULL) { + mp_call_function_0(module_fun); + } } mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(true); // handle any pending exceptions (and any callbacks) nlr_pop(); - // CIRCUITPY-CHANGE - ret = 0; + ret = PYEXEC_NORMAL_EXIT; if (exec_flags & EXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } @@ -178,36 +189,66 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_hal_stdout_tx_strn("\x04", 1); } - // check for SystemExit - - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: Name and use some values. // nlr.ret_val is an exception object. mp_obj_t exception_obj = (mp_obj_t)nlr.ret_val; + const mp_obj_type_t *exception_obj_type = mp_obj_get_type(exception_obj); - if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exception_obj)), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { - // at the moment, the value of SystemExit is unused + #if MICROPY_PYEXEC_ENABLE_VM_ABORT + if (nlr.ret_val == NULL) { // abort + ret = PYEXEC_ABORT; + } else + #endif + + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exception_obj_type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { // system exit + #if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING + // None is an exit value of 0; an int is its value; anything else is 1 + mp_obj_t val = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val)); + if (val != mp_const_none) { + if (mp_obj_is_int(val)) { + ret = (int)mp_obj_int_get_truncated(val); + } else { + mp_obj_print_helper(MICROPY_ERROR_PRINTER, val, PRINT_STR); + mp_print_str(MICROPY_ERROR_PRINTER, "\n"); + ret = PYEXEC_UNHANDLED_EXCEPTION; + } + } else { + ret = PYEXEC_NORMAL_EXIT; + } + // Set PYEXEC_FORCED_EXIT flag so REPL knows to exit + ret |= PYEXEC_FORCED_EXIT; + #else ret = PYEXEC_FORCED_EXIT; - // CIRCUITPY-CHANGE - #if CIRCUITPY_ALARM - } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exception_obj)), MP_OBJ_FROM_PTR(&mp_type_DeepSleepRequest))) { + #endif + // CIRCUITPY-CHANGE: support DeepSleepRequest + #if CIRCUITPY_ALARM + } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exception_obj_type), MP_OBJ_FROM_PTR(&mp_type_DeepSleepRequest))) { ret = PYEXEC_DEEP_SLEEP; - #endif + #endif + // CIRCUITPY-CHANGE: supprt ReloadException } else if (exception_obj == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception))) { ret = PYEXEC_RELOAD; - } else { - mp_obj_print_exception(&mp_plat_print, exception_obj); - ret = PYEXEC_EXCEPTION; + } else { // other exception + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + ret = PYEXEC_UNHANDLED_EXCEPTION; + #if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_KeyboardInterrupt))) { // keyboard interrupt + ret = PYEXEC_KEYBOARD_INTERRUPT; + } + #endif } - } + + // CIRCUITPY_CHANGE: Fill in result out argument. if (result != NULL) { result->return_code = ret; - #if CIRCUITPY_ALARM // Don't set the exception object if we exited for deep sleep. - if (ret != 0 && ret != PYEXEC_DEEP_SLEEP) { - #else - if (ret != 0) { + if (ret != 0 + #if CIRCUITPY_ALARM + && ret != PYEXEC_DEEP_SLEEP #endif + ) + { mp_obj_t return_value = (mp_obj_t)nlr.ret_val; result->exception = return_value; result->exception_line = -1; @@ -224,6 +265,10 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input } } + #if MICROPY_PYEXEC_ENABLE_VM_ABORT + nlr_set_abort(NULL); + #endif + #if MICROPY_REPL_INFO // display debugging info if wanted if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) { @@ -776,6 +821,7 @@ int pyexec_friendly_repl(void) { if (ret & (PYEXEC_FORCED_EXIT | PYEXEC_RELOAD)) { return ret; } + mp_hal_stdio_mode_raw(); } } diff --git a/tests/cmdline/repl_autocomplete.py.exp b/tests/cmdline/repl_autocomplete.py.exp index 2e2397bb028d3..7840e52e8a4a2 100644 --- a/tests/cmdline/repl_autocomplete.py.exp +++ b/tests/cmdline/repl_autocomplete.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # tests for autocompletion >>> import sys >>> not_exist. @@ -11,4 +11,4 @@ Use \.\+ >>> i.lower('ABC') 'abc' >>> None. ->>> +>>> \$ diff --git a/tests/cmdline/repl_autocomplete_underscore.py b/tests/cmdline/repl_autocomplete_underscore.py index 98bbb699200d3..e685a7fe7ff02 100644 --- a/tests/cmdline/repl_autocomplete_underscore.py +++ b/tests/cmdline/repl_autocomplete_underscore.py @@ -7,18 +7,18 @@ def __init__(self): self.public_attr = 1 self._private_attr = 2 self.__very_private = 3 - + def public_method(self): pass - + def _private_method(self): pass - + @property def public_property(self): return 42 - - @property + + @property def _private_property(self): return 99 diff --git a/tests/cmdline/repl_autocomplete_underscore.py.exp b/tests/cmdline/repl_autocomplete_underscore.py.exp index f9720ef233180..98e6c2aeb05a3 100644 --- a/tests/cmdline/repl_autocomplete_underscore.py.exp +++ b/tests/cmdline/repl_autocomplete_underscore.py.exp @@ -1,41 +1,41 @@ -MicroPython \.\+ version -Type "help()" for more information. + +Adafruit CircuitPython \.\+ version >>> # Test REPL autocompletion filtering of underscore attributes ->>> +>>> \$ >>> # Start paste mode ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === class TestClass: === def __init__(self): === self.public_attr = 1 === self._private_attr = 2 === self.__very_private = 3 -=== +=== \$ === def public_method(self): === pass -=== +=== \$ === def _private_method(self): === pass -=== +=== \$ === @property === def public_property(self): === return 42 -=== -=== @property +=== \$ +=== @property === def _private_property(self): === return 99 -=== -=== +=== \$ +=== \$ >>> # Paste executed ->>> +>>> \$ >>> # Create an instance >>> obj = TestClass() ->>> +>>> \$ >>> # Test tab completion on the instance >>> # The tab character after `obj.` and 'a' below triggers the completions >>> obj.public_ public_attr public_method public_property >>> obj.public_attr 1 ->>> +>>> \$ diff --git a/tests/cmdline/repl_autoindent.py.exp b/tests/cmdline/repl_autoindent.py.exp index 9ff83a92870e8..04f00686b208d 100644 --- a/tests/cmdline/repl_autoindent.py.exp +++ b/tests/cmdline/repl_autoindent.py.exp @@ -1,22 +1,22 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # tests for autoindent >>> if 1: ... print(1) -... -... -... +... \$ +... \$ +... \$ 1 >>> if 0: ...  print(2) ... else: ... print(3) -... +... \$ 3 >>> if 0: ... print(4) ... else: ... print(5) -... +... \$ 5 ->>> +>>> \$ diff --git a/tests/cmdline/repl_basic.py.exp b/tests/cmdline/repl_basic.py.exp index 26442b6445589..5bdcc9d6d3246 100644 --- a/tests/cmdline/repl_basic.py.exp +++ b/tests/cmdline/repl_basic.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # basic REPL tests >>> print(1) 1 @@ -7,4 +7,4 @@ Use \.\+ 1 >>> 2 2 ->>> +>>> \$ diff --git a/tests/cmdline/repl_cont.py.exp b/tests/cmdline/repl_cont.py.exp index 6eed4a3e02c47..41f2436ac18a0 100644 --- a/tests/cmdline/repl_cont.py.exp +++ b/tests/cmdline/repl_cont.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # check REPL allows to continue input >>> 1 \\\\ ... + 2 @@ -54,4 +54,4 @@ two >>> if1 = 2 >>> print(if1) 2 ->>> +>>> \$ diff --git a/tests/cmdline/repl_emacs_keys.py.exp b/tests/cmdline/repl_emacs_keys.py.exp index 2e8667a8e6ca9..4979f8bbfc3ab 100644 --- a/tests/cmdline/repl_emacs_keys.py.exp +++ b/tests/cmdline/repl_emacs_keys.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # REPL tests of GNU-ish readline navigation >>> # history buffer navigation >>> 1 @@ -16,4 +16,4 @@ Use \.\+ >>> t = 121 >>> \.\+ 'foobar' ->>> +>>> \$ diff --git a/tests/cmdline/repl_inspect.py.exp b/tests/cmdline/repl_inspect.py.exp index 8ece5ffc37f5c..30257f31aab93 100644 --- a/tests/cmdline/repl_inspect.py.exp +++ b/tests/cmdline/repl_inspect.py.exp @@ -1,6 +1,6 @@ test -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # cmdline: -i -c print("test") >>> # -c option combined with -i option results in REPL ->>> +>>> \$ diff --git a/tests/cmdline/repl_lock.py.exp b/tests/cmdline/repl_lock.py.exp index d921fd6ae8e99..e6d63fcf20044 100644 --- a/tests/cmdline/repl_lock.py.exp +++ b/tests/cmdline/repl_lock.py.exp @@ -1,5 +1,5 @@ -MicroPython \.\+ version -Type "help()" for more information. + +Adafruit CircuitPython \.\+ version >>> import micropython >>> micropython.heap_lock() >>> 1+1 diff --git a/tests/cmdline/repl_micropyinspect.py.exp b/tests/cmdline/repl_micropyinspect.py.exp index 3c9cbc030c0b5..36bef37c62c6b 100644 --- a/tests/cmdline/repl_micropyinspect.py.exp +++ b/tests/cmdline/repl_micropyinspect.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # cmdline: cmdline/repl_micropyinspect >>> # setting MICROPYINSPECT environment variable before program exit triggers REPL ->>> +>>> \$ diff --git a/tests/cmdline/repl_paste.py.exp b/tests/cmdline/repl_paste.py.exp index 2b837f85cf993..cc5ac2f08007d 100644 --- a/tests/cmdline/repl_paste.py.exp +++ b/tests/cmdline/repl_paste.py.exp @@ -1,21 +1,21 @@ -MicroPython \.\+ version -Type "help()" for more information. + +Adafruit CircuitPython \.\+ version >>> # Test REPL paste mode functionality ->>> +>>> \$ >>> # Basic paste mode with a simple function ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === def hello(): === print('Hello from paste mode!') === hello() -=== +=== \$ Hello from paste mode! ->>> +>>> \$ >>> # Paste mode with multiple indentation levels ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === def calculate(n): === if n > 0: === for i in range(n): @@ -25,109 +25,109 @@ paste mode; Ctrl-C to cancel, Ctrl-D to finish === print(f'Odd: {i}') === else: === print('n must be positive') -=== +=== \$ === calculate(5) -=== +=== \$ Even: 0 Odd: 1 Even: 2 Odd: 3 Even: 4 ->>> +>>> \$ >>> # Paste mode with blank lines ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === def function_with_blanks(): === print('First line') -=== +=== \$ === print('After blank line') -=== -=== +=== \$ +=== \$ === print('After two blank lines') -=== +=== \$ === function_with_blanks() -=== +=== \$ First line After blank line After two blank lines ->>> +>>> \$ >>> # Paste mode with class definition and multiple methods ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === class TestClass: === def __init__(self, value): === self.value = value -=== +=== \$ === def display(self): === print(f'Value is: {self.value}') -=== +=== \$ === def double(self): === self.value *= 2 === return self.value -=== +=== \$ === obj = TestClass(21) === obj.display() === print(f'Doubled: {obj.double()}') === obj.display() -=== +=== \$ Value is: 21 Doubled: 42 Value is: 42 ->>> +>>> \$ >>> # Paste mode with exception handling ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === try: === x = 1 / 0 === except ZeroDivisionError: === print('Caught division by zero') === finally: === print('Finally block executed') -=== +=== \$ Caught division by zero Finally block executed ->>> +>>> \$ >>> # Cancel paste mode with Ctrl-C ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === print('This should not execute') -=== ->>> ->>> +=== \$ +>>> \$ +>>> \$ >>> # Normal REPL still works after cancelled paste >>> print('Back to normal REPL') Back to normal REPL ->>> +>>> \$ >>> # Paste mode with syntax error ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === def bad_syntax(: === print('Missing parameter') -=== +=== \$ Traceback (most recent call last): File "", line 2 SyntaxError: invalid syntax ->>> +>>> \$ >>> # Paste mode with runtime error ->>> +>>> \$ paste mode; Ctrl-C to cancel, Ctrl-D to finish -=== +=== \$ === def will_error(): === undefined_variable -=== +=== \$ === will_error() -=== +=== \$ Traceback (most recent call last): File "", line 5, in File "", line 3, in will_error NameError: name 'undefined_variable' isn't defined ->>> +>>> \$ >>> # Final test to show REPL is still functioning >>> 1 + 2 + 3 6 ->>> +>>> \$ diff --git a/tests/cmdline/repl_sys_ps1_ps2.py.exp b/tests/cmdline/repl_sys_ps1_ps2.py.exp index 452a54fe5ae9b..d4bcf7a44d592 100644 --- a/tests/cmdline/repl_sys_ps1_ps2.py.exp +++ b/tests/cmdline/repl_sys_ps1_ps2.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # test changing ps1/ps2 >>> import sys >>> sys.ps1 = "PS1" diff --git a/tests/cmdline/repl_words_move.py.exp b/tests/cmdline/repl_words_move.py.exp index ba5c3648cff98..41ad3d8e73351 100644 --- a/tests/cmdline/repl_words_move.py.exp +++ b/tests/cmdline/repl_words_move.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # word movement >>> # backward-word, start in word >>> \.\+ @@ -19,7 +19,7 @@ Use \.\+ >>> # forward-word on eol. if cursor is moved, this will result in a SyntaxError >>> \.\+ 6 ->>> +>>> \$ >>> # kill word >>> # backward-kill-word, start in word >>> \.\+ @@ -33,7 +33,7 @@ Use \.\+ >>> # forward-kill-word, don't start in word >>> \.\+ 3 ->>> +>>> \$ >>> # extra move/kill shortcuts >>> # ctrl-left >>> \.\+ @@ -44,4 +44,4 @@ Use \.\+ >>> # ctrl-w >>> \.\+ 1 ->>> +>>> \$ diff --git a/tests/micropython/opt_level_lineno.py b/tests/micropython/opt_level_lineno.py index ebb404c59fc2d..4ca76625de49a 100644 --- a/tests/micropython/opt_level_lineno.py +++ b/tests/micropython/opt_level_lineno.py @@ -10,5 +10,15 @@ # the expected output is that any line is printed as "line 1" micropython.opt_level(3) -# CIRCUITPY-CHANGE: use traceback.print_exception() instead of sys.print_exception() -exec("try:\n xyz\nexcept NameError as er:\n import traceback\n traceback.print_exception(er)") +# force bytecode emitter, because native emitter doesn't store line numbers +exec(""" +@micropython.bytecode +def f(): + try: + xyz + except NameError as er: + # CIRCUITPY-CHANGE: use traceback.print_exception() instead of sys.print_exception() + import traceback + traceback.print_exception(er) +f() +""") From c5e9b89dd6a9d9bc758c4d615225ca0ad7f5d202 Mon Sep 17 00:00:00 2001 From: Bernhard Bablok Date: Thu, 9 Apr 2026 13:04:43 +0200 Subject: [PATCH 495/517] added support for Pimoroni Badger2350 --- .../pimoroni_badger2350/badger2350-shared.h | 15 ++ .../boards/pimoroni_badger2350/board.c | 225 ++++++++++++++++++ .../boards/pimoroni_badger2350/link.ld | 1 + .../pimoroni_badger2350/mpconfigboard.h | 21 ++ .../pimoroni_badger2350/mpconfigboard.mk | 38 +++ .../pico-sdk-configboard.h | 9 + .../boards/pimoroni_badger2350/pins.c | 131 ++++++++++ 7 files changed, 440 insertions(+) create mode 100644 ports/raspberrypi/boards/pimoroni_badger2350/badger2350-shared.h create mode 100644 ports/raspberrypi/boards/pimoroni_badger2350/board.c create mode 100644 ports/raspberrypi/boards/pimoroni_badger2350/link.ld create mode 100644 ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.h create mode 100644 ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.mk create mode 100644 ports/raspberrypi/boards/pimoroni_badger2350/pico-sdk-configboard.h create mode 100644 ports/raspberrypi/boards/pimoroni_badger2350/pins.c diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/badger2350-shared.h b/ports/raspberrypi/boards/pimoroni_badger2350/badger2350-shared.h new file mode 100644 index 0000000000000..4cc67efd4df7e --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/badger2350-shared.h @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/digitalio/DigitalInOut.h" + +extern digitalio_digitalinout_obj_t i2c_power_en_pin_obj; +extern const mp_obj_fun_builtin_fixed_t set_update_speed_obj; +extern const mp_obj_fun_builtin_fixed_t get_reset_state_obj; +extern const mp_obj_fun_builtin_fixed_t on_reset_pressed_obj; diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/board.c b/ports/raspberrypi/boards/pimoroni_badger2350/board.c new file mode 100644 index 0000000000000..21e0e3f1ba5c2 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/board.c @@ -0,0 +1,225 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Bob Abeles +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" + +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/board.h" +#include "supervisor/board.h" +#include "badger2350-shared.h" + +#include "hardware/gpio.h" +#include "hardware/structs/iobank0.h" + +digitalio_digitalinout_obj_t i2c_power_en_pin_obj; +static volatile uint32_t reset_button_state = 0; + +// Forward declaration to satisfy -Wmissing-prototypes +static void preinit_button_state(void) __attribute__((constructor(101))); + + +// pin definitions +// Button pin definitions for Badger2350 +#define SW_A_PIN 7 +#define SW_B_PIN 9 +#define SW_C_PIN 10 +#define SW_DOWN_PIN 6 +#define SW_UP_PIN 11 + +static const uint8_t _sw_pin_nrs[] = { + SW_A_PIN, SW_B_PIN, SW_C_PIN, SW_DOWN_PIN, SW_UP_PIN +}; + +// Mask of all front button pins +#define SW_MASK ((1 << SW_A_PIN) | (1 << SW_B_PIN) | (1 << SW_C_PIN) | \ + (1 << SW_DOWN_PIN) | (1 << SW_UP_PIN)) + +// This function runs BEFORE main() via constructor attribute! +// This is the key to fast button state detection. +// Priority 101 = runs very early + +static void preinit_button_state(void) { + // Configure button pins as inputs with pull-downs using direct register access + // This is faster than SDK functions and works before full init + + for (size_t i = 0; i < sizeof(_sw_pin_nrs); i++) { + uint8_t pin_nr = _sw_pin_nrs[i]; + // Set as input + sio_hw->gpio_oe_clr = 1u << pin_nr; + // enable pull-ups + pads_bank0_hw->io[pin_nr] = PADS_BANK0_GPIO0_IE_BITS | + PADS_BANK0_GPIO0_PUE_BITS; + // Set GPIO function + iobank0_hw->io[pin_nr].ctrl = 5; // SIO function + } + + // Small delay for pins to settle (just a few cycles) + for (volatile int i = 0; i < 100; i++) { + __asm volatile ("nop"); + } + + // Capture button states NOW - before anything else runs + reset_button_state = ~sio_hw->gpio_in & SW_MASK; +} + +static mp_obj_t _get_reset_state(void) { + return mp_obj_new_int(reset_button_state); +} +MP_DEFINE_CONST_FUN_OBJ_0(get_reset_state_obj,_get_reset_state); + +static mp_obj_t _on_reset_pressed(mp_obj_t pin_in) { + mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(pin_in); + return mp_obj_new_bool( + (reset_button_state & (1<number)) != 0); +} +MP_DEFINE_CONST_FUN_OBJ_1(on_reset_pressed_obj,_on_reset_pressed); + +// The display uses an SSD1680 control chip. +uint8_t _start_sequence[] = { + 0x12, 0x80, 0x00, 0x14, // soft reset and wait 20ms + 0x11, 0x00, 0x01, 0x03, // Ram data entry mode + 0x3c, 0x00, 0x01, 0x03, // border color + 0x2c, 0x00, 0x01, 0x28, // Set vcom voltage + 0x03, 0x00, 0x01, 0x17, // Set gate voltage + 0x04, 0x00, 0x03, 0x41, 0xae, 0x32, // Set source voltage + 0x4e, 0x00, 0x01, 0x00, // ram x count + 0x4f, 0x00, 0x02, 0x00, 0x00, // ram y count + 0x01, 0x00, 0x03, 0x07, 0x01, 0x00, // set display size + 0x32, 0x00, 0x99, // Update waveforms + + // offset 44 + 0x40, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L0 + 0xA0, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L1 + 0xA8, 0x65, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L2 + 0xAA, 0x65, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L4 + + // offset 104 + 0x02, 0x00, 0x00, 0x05, 0x0A, 0x00, 0x03, // Group0 (with default speed==0) + // offset 111 + 0x19, 0x19, 0x00, 0x02, 0x00, 0x00, 0x03, // Group1 (with default speed==0) + // offset 118 + 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x03, // Group2 (with default speed==0) + + // offset 125 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group4 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group5 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group6 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group9 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group10 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group11 + 0x44, 0x42, 0x22, 0x22, 0x23, 0x32, 0x00, // Config + 0x00, 0x00, // FR, XON + + 0x22, 0x00, 0x01, 0xc7, // display update mode + +}; + +const uint8_t _stop_sequence[] = { + 0x10, 0x00, 0x01, 0x01 // DSM deep sleep mode 1 +}; + +const uint8_t _refresh_sequence[] = { + 0x20, 0x00, 0x00 // ADUS +}; + +// Change update speed. This changes the repeat-count in the LUTs +// Pimoroni uses: 0 == slow ... 3 == fast +// and calculates the LUT repeat count as 3-speed + +#define SPEED_OFFSET_1 110 +#define SPEED_OFFSET_2 117 +#define SPEED_OFFSET_3 124 + +static mp_obj_t _set_update_speed(mp_obj_t speed_in) { + mp_int_t speed = mp_obj_get_int(speed_in); + uint8_t count = (uint8_t)3 - (uint8_t)(speed & 3); + _start_sequence[SPEED_OFFSET_1] = count; + _start_sequence[SPEED_OFFSET_2] = count; + _start_sequence[SPEED_OFFSET_3] = count; + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(set_update_speed_obj,_set_update_speed); + +void board_init(void) { + // Drive the I2C_POWER_EN pin high + i2c_power_en_pin_obj.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct( + &i2c_power_en_pin_obj, &pin_GPIO27); + common_hal_digitalio_digitalinout_switch_to_output( + &i2c_power_en_pin_obj, true, DRIVE_MODE_PUSH_PULL); + common_hal_digitalio_digitalinout_never_reset(&i2c_power_en_pin_obj); + + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct(spi, &pin_GPIO18, &pin_GPIO19, NULL, false); + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + common_hal_fourwire_fourwire_construct(bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO20), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO21), // EPD_RST Reset + 12000000, // Baudrate + 0, // Polarity + 0); // Phase + + // create and configure display + epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; + display->base.type = &epaperdisplay_epaperdisplay_type; + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = _start_sequence; + args.start_sequence_len = sizeof(_start_sequence); + args.stop_sequence = _stop_sequence; + args.stop_sequence_len = sizeof(_stop_sequence); + args.width = 264; + args.height = 176; + args.ram_width = 250; + args.ram_height = 296; + args.rotation = 270; + args.set_column_window_command = 0x44; + args.set_row_window_command = 0x45; + args.set_current_column_command = 0x4e; + args.set_current_row_command = 0x4f; + args.write_black_ram_command = 0x24; + args.write_color_ram_command = 0x26; + args.color_bits_inverted = true; + args.refresh_sequence = _refresh_sequence; + args.refresh_sequence_len = sizeof(_refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO16; + args.busy_state = true; + args.seconds_per_frame = 3.0; + args.grayscale = true; + args.two_byte_sequence_length = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); +} + +void board_deinit(void) { + epaperdisplay_epaperdisplay_obj_t *display = &displays[0].epaper_display; + if (display->base.type == &epaperdisplay_epaperdisplay_type) { + while (common_hal_epaperdisplay_epaperdisplay_get_busy(display)) { + RUN_BACKGROUND_TASKS; + } + } + common_hal_displayio_release_displays(); +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/link.ld b/ports/raspberrypi/boards/pimoroni_badger2350/link.ld new file mode 100644 index 0000000000000..e814bead4c51e --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/link.ld @@ -0,0 +1 @@ +firmware_size = 1532k; diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.h new file mode 100644 index 0000000000000..6cffd190ad606 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.h @@ -0,0 +1,21 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Bernhard Bablok +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "Pimoroni Badger 2350" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1) + +#define MICROPY_HW_LED_STATUS (&pin_GPIO0) + +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO4) +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO5) + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19) + +#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO8) diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.mk new file mode 100644 index 0000000000000..0157ccf149960 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.mk @@ -0,0 +1,38 @@ +USB_VID = 0x2E8A +USB_PID = 0x1100 +USB_PRODUCT = "Pimoroni Badger 2350" +USB_MANUFACTURER = "Pimoroni" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" + +CIRCUITPY__EVE = 1 + +CIRCUITPY_CYW43 = 1 +CIRCUITPY_SSL = 1 +CIRCUITPY_HASHLIB = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_MDNS = 1 +CIRCUITPY_SOCKETPOOL = 1 +CIRCUITPY_WIFI = 1 + +# PIO clock divider set to 2 (default), consider changing if TM2 gSPI +# becomes unreliable. +CFLAGS += \ + -DCYW43_PIN_WL_DYNAMIC=0 \ + -DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \ + -DCYW43_DEFAULT_PIN_WL_REG_ON=23 \ + -DCYW43_DEFAULT_PIN_WL_CLOCK=29 \ + -DCYW43_DEFAULT_PIN_WL_DATA_IN=24 \ + -DCYW43_DEFAULT_PIN_WL_DATA_OUT=24 \ + -DCYW43_DEFAULT_PIN_WL_CS=25 \ + -DCYW43_WL_GPIO_COUNT=3 \ + -DCYW43_PIO_CLOCK_DIV_INT=2 \ + -DCYW43_PIO_CLOCK_DIV_FRAC=0 +# Must be accompanied by a linker script change +CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' + +FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-pcf85063a diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/pico-sdk-configboard.h b/ports/raspberrypi/boards/pimoroni_badger2350/pico-sdk-configboard.h new file mode 100644 index 0000000000000..42e0612bf8ba6 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Bob Abeles +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/pins.c b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c new file mode 100644 index 0000000000000..64c1874e28424 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c @@ -0,0 +1,131 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Bob Abeles +// +// SPDX-License-Identifier: MIT + +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" +#include "badger2350-shared.h" + + +// LUT manipulation +static const mp_rom_map_elem_t lut_update_table[] = { + { MP_ROM_QSTR(MP_QSTR_SET_UPDATE_SPEED), (mp_obj_t)&set_update_speed_obj }, + { MP_ROM_QSTR(MP_QSTR_SPEED_SLOW), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_SPEED_FAST), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_SPEED_FASTER), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_SPEED_FASTEST), MP_ROM_INT(3) }, +}; +MP_DEFINE_CONST_DICT(lut_update_dict, lut_update_table); + +MP_DEFINE_CONST_OBJ_TYPE( + display_type, + MP_QSTR_display, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS | ~MP_TYPE_FLAG_BINDS_SELF, + locals_dict, &lut_update_dict +); + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_display), MP_ROM_PTR(&display_type) }, + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_LED0), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_LED1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_LED2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_LED3), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, + + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_SW_DOWN), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SW_A), MP_ROM_PTR(&pin_GPIO7) }, + + // GP8 is reserved for PSRAM chip select + + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_SW_B), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_SW_C), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_SW_UP), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_RTC_ALARM), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_SW_RESET), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_SW_INT), MP_ROM_PTR(&pin_GPIO15) }, + + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_INKY_BUSY), MP_ROM_PTR(&pin_GPIO16) }, + + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_INKY_CS), MP_ROM_PTR(&pin_GPIO17) }, + + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_INKY_DC), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_INKY_RST), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_SW_HOME), MP_ROM_PTR(&pin_GPIO22) }, + + // GP23, GP24, GP25, and GP29 are reserved for RM2 gSPI + + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_VBAT_SENSE), MP_ROM_PTR(&pin_GPIO26) }, + + // GP27 is the used for I2C power-enable, driven high by board.c + { MP_ROM_QSTR(MP_QSTR_I2C_POWER_EN), MP_ROM_PTR(&i2c_power_en_pin_obj) }, + + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_SENSE_1V1), MP_ROM_PTR(&pin_GPIO28) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + + // Pins accessed though the RM2 module (CYW43439) + // CYW0, CYW1 is unconnected + { MP_ROM_QSTR(MP_QSTR_CHARGE_STAT), MP_ROM_PTR(&pin_CYW2) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].epaper_display)}, + + // button-state on reset + { MP_ROM_QSTR(MP_QSTR_RESET_STATE), (mp_obj_t)&get_reset_state_obj }, + { MP_ROM_QSTR(MP_QSTR_ON_RESET_PRESSED), (mp_obj_t)&on_reset_pressed_obj }, + +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); From 3d8c75f350cc26a2ed4adf8e773c50e02b39ed10 Mon Sep 17 00:00:00 2001 From: Bernhard Bablok Date: Thu, 9 Apr 2026 13:31:36 +0200 Subject: [PATCH 496/517] fixed formatting --- .../boards/pimoroni_badger2350/board.c | 38 +++++++++---------- .../boards/pimoroni_badger2350/pins.c | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/board.c b/ports/raspberrypi/boards/pimoroni_badger2350/board.c index 21e0e3f1ba5c2..1da13b8033995 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2350/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2350/board.c @@ -41,7 +41,7 @@ static const uint8_t _sw_pin_nrs[] = { // Mask of all front button pins #define SW_MASK ((1 << SW_A_PIN) | (1 << SW_B_PIN) | (1 << SW_C_PIN) | \ - (1 << SW_DOWN_PIN) | (1 << SW_UP_PIN)) + (1 << SW_DOWN_PIN) | (1 << SW_UP_PIN)) // This function runs BEFORE main() via constructor attribute! // This is the key to fast button state detection. @@ -50,38 +50,38 @@ static const uint8_t _sw_pin_nrs[] = { static void preinit_button_state(void) { // Configure button pins as inputs with pull-downs using direct register access // This is faster than SDK functions and works before full init - + for (size_t i = 0; i < sizeof(_sw_pin_nrs); i++) { uint8_t pin_nr = _sw_pin_nrs[i]; // Set as input sio_hw->gpio_oe_clr = 1u << pin_nr; // enable pull-ups - pads_bank0_hw->io[pin_nr] = PADS_BANK0_GPIO0_IE_BITS | - PADS_BANK0_GPIO0_PUE_BITS; + pads_bank0_hw->io[pin_nr] = PADS_BANK0_GPIO0_IE_BITS | + PADS_BANK0_GPIO0_PUE_BITS; // Set GPIO function iobank0_hw->io[pin_nr].ctrl = 5; // SIO function } - + // Small delay for pins to settle (just a few cycles) for (volatile int i = 0; i < 100; i++) { __asm volatile ("nop"); } - + // Capture button states NOW - before anything else runs reset_button_state = ~sio_hw->gpio_in & SW_MASK; } static mp_obj_t _get_reset_state(void) { - return mp_obj_new_int(reset_button_state); + return mp_obj_new_int(reset_button_state); } -MP_DEFINE_CONST_FUN_OBJ_0(get_reset_state_obj,_get_reset_state); +MP_DEFINE_CONST_FUN_OBJ_0(get_reset_state_obj, _get_reset_state); static mp_obj_t _on_reset_pressed(mp_obj_t pin_in) { - mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(pin_in); - return mp_obj_new_bool( - (reset_button_state & (1<number)) != 0); + mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(pin_in); + return mp_obj_new_bool( + (reset_button_state & (1 << pin->number)) != 0); } -MP_DEFINE_CONST_FUN_OBJ_1(on_reset_pressed_obj,_on_reset_pressed); +MP_DEFINE_CONST_FUN_OBJ_1(on_reset_pressed_obj, _on_reset_pressed); // The display uses an SSD1680 control chip. uint8_t _start_sequence[] = { @@ -144,15 +144,15 @@ const uint8_t _refresh_sequence[] = { #define SPEED_OFFSET_3 124 static mp_obj_t _set_update_speed(mp_obj_t speed_in) { - mp_int_t speed = mp_obj_get_int(speed_in); - uint8_t count = (uint8_t)3 - (uint8_t)(speed & 3); - _start_sequence[SPEED_OFFSET_1] = count; - _start_sequence[SPEED_OFFSET_2] = count; - _start_sequence[SPEED_OFFSET_3] = count; - return mp_const_none; + mp_int_t speed = mp_obj_get_int(speed_in); + uint8_t count = (uint8_t)3 - (uint8_t)(speed & 3); + _start_sequence[SPEED_OFFSET_1] = count; + _start_sequence[SPEED_OFFSET_2] = count; + _start_sequence[SPEED_OFFSET_3] = count; + return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_1(set_update_speed_obj,_set_update_speed); +MP_DEFINE_CONST_FUN_OBJ_1(set_update_speed_obj, _set_update_speed); void board_init(void) { // Drive the I2C_POWER_EN pin high diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/pins.c b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c index 64c1874e28424..9ba93cd47cb1a 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2350/pins.c +++ b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c @@ -25,7 +25,7 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_QSTR_display, MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS | ~MP_TYPE_FLAG_BINDS_SELF, locals_dict, &lut_update_dict -); + ); static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS From a5cbf06ffaa07dd5a2244cf613fc757b050a0cd0 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 9 Apr 2026 12:21:35 -0500 Subject: [PATCH 497/517] enable aesio on zephyr port and add test for it. Add Containerfile and scripts for native_sim build. --- .../autogen_board_info.toml | 4 +- .../autogen_board_info.toml | 2 +- .../native/native_sim/autogen_board_info.toml | 2 +- .../nrf5340bsim/autogen_board_info.toml | 4 +- .../nordic/nrf5340dk/autogen_board_info.toml | 4 +- .../nordic/nrf54h20dk/autogen_board_info.toml | 4 +- .../nordic/nrf54l15dk/autogen_board_info.toml | 4 +- .../nordic/nrf7002dk/autogen_board_info.toml | 6 +- .../nxp/frdm_mcxn947/autogen_board_info.toml | 4 +- .../nxp/frdm_rw612/autogen_board_info.toml | 6 +- .../mimxrt1170_evk/autogen_board_info.toml | 4 +- .../autogen_board_info.toml | 9 +- .../rpi_pico2_zephyr/autogen_board_info.toml | 9 +- .../rpi_pico_w_zephyr/autogen_board_info.toml | 9 +- .../rpi_pico_zephyr/autogen_board_info.toml | 9 +- .../da14695_dk_usb/autogen_board_info.toml | 4 +- .../renesas/ek_ra6m5/autogen_board_info.toml | 4 +- .../renesas/ek_ra8d1/autogen_board_info.toml | 4 +- .../nucleo_n657x0_q/autogen_board_info.toml | 4 +- .../nucleo_u575zi_q/autogen_board_info.toml | 4 +- .../st/stm32h750b_dk/autogen_board_info.toml | 4 +- .../st/stm32h7b3i_dk/autogen_board_info.toml | 4 +- .../stm32wba65i_dk1/autogen_board_info.toml | 4 +- .../zephyr-cp/cptools/build_circuitpython.py | 1 + .../zephyr-cp/native_sim_build_Containerfile | 37 +++ .../native_sim_build_init_container.sh | 42 ++++ .../native_sim_build_run_container.sh | 23 ++ ports/zephyr-cp/tests/test_aesio.py | 223 ++++++++++++++++++ 28 files changed, 384 insertions(+), 54 deletions(-) create mode 100644 ports/zephyr-cp/native_sim_build_Containerfile create mode 100755 ports/zephyr-cp/native_sim_build_init_container.sh create mode 100755 ports/zephyr-cp/native_sim_build_run_container.sh create mode 100644 ports/zephyr-cp/tests/test_aesio.py diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 7b1fb9a0d6f55..17065b902923e 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = true # Zephyr board has nvm onewireio = false diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml index 05cc100c6e55a..3d7a2b29a3f50 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 4b657282ef609..5b8fcfd0deada 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 00f7178363943..9842ea3d88d9b 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 92cb4ef1a80bf..065a708c50510 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index 5d5f325af81aa..b8bad240351fa 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 9792f8aca74a6..22b92d49f49c9 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 62fa896c0ecb2..184ff34b7ef46 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -56,7 +56,7 @@ i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false imagecapture = false -ipaddress = false +ipaddress = true # Zephyr networking enabled is31fl3741 = false jpegio = false keypad = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index b121a1e82349a..6da4ebc66c5ce 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index e5b51390538ec..19b3b7cc0e108 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -56,7 +56,7 @@ i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false i2ctarget = false imagecapture = false -ipaddress = false +ipaddress = true # Zephyr networking enabled is31fl3741 = false jpegio = false keypad = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index 57ff5fe8c813e..702f5900eee79 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml index 8c1f7018f1242..10ca2517b6193 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio @@ -62,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -70,9 +71,9 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false -nvm = false +nvm = true # Zephyr board has nvm onewireio = false os = true paralleldisplaybus = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml index cdffc295701ec..15b36b725af01 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio @@ -62,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -70,9 +71,9 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false -nvm = false +nvm = true # Zephyr board has nvm onewireio = false os = true paralleldisplaybus = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml index d8cee739d6500..1f01990b5f291 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio @@ -62,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -70,9 +71,9 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false -nvm = false +nvm = true # Zephyr board has nvm onewireio = false os = true paralleldisplaybus = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml index 40fb5baf34a90..c8624cdde6c53 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,6 +24,7 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false bitmapfilter = true # Zephyr board has busio @@ -62,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -70,9 +71,9 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false -nvm = false +nvm = true # Zephyr board has nvm onewireio = false os = true paralleldisplaybus = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index e11d98ac9a161..ea9ed4c2577a8 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index 47a54e9632a0b..c28c03b2c8e30 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index c4a071b068831..1b0c652b7d562 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index f526b1e90fa80..c05bfa1d17eca 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 2c904026fba1c..d6a0a27e0cd43 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml index 3e8208d82c967..599ec4ec4d2b0 100644 --- a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index a85cd9e3cc5a8..c5505bee932a0 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index 36213878c2992..0fe013483482d 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = false onewireio = false diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 73918a06a5abb..3edbc1efdf6c8 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -61,6 +61,7 @@ "io", "math", "msgpack", + "aesio", ] # Flags that don't match with with a *bindings module. Some used by adafruit_requests MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math"] diff --git a/ports/zephyr-cp/native_sim_build_Containerfile b/ports/zephyr-cp/native_sim_build_Containerfile new file mode 100644 index 0000000000000..8b9db9dc55926 --- /dev/null +++ b/ports/zephyr-cp/native_sim_build_Containerfile @@ -0,0 +1,37 @@ +FROM ubuntu:24.04 +ENV DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC +ENV PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig + +RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y --no-install-recommends \ + git cmake ninja-build gperf ccache dfu-util device-tree-compiler \ + wget python3 python3-dev python3-pip python3-venv python3-setuptools \ + python3-tk python3-wheel xz-utils file make gcc \ + gcc-multilib g++-multilib libc6-dev-i386 \ + libsdl2-dev:i386 libsdl2-image-dev:i386 \ + libmagic1 mtools pkg-config ca-certificates unzip \ + protobuf-compiler sudo \ + && rm -rf /var/lib/apt/lists/* + +RUN useradd -m -s /bin/bash dev \ + && echo 'dev ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/dev \ + && chmod 440 /etc/sudoers.d/dev +USER dev +WORKDIR /home/dev + +RUN python3 -m venv /home/dev/.venv +ENV PATH="/home/dev/.venv/bin:${PATH}" +RUN pip install --no-cache-dir west pytest pyelftools pyyaml intelhex protobuf grpcio-tools + +# The repo is expected to be bind-mounted here at runtime: +# podman run -v ~/circuitpython:/home/dev/circuitpython:Z --userns=keep-id ... +# On first run, inside the container do: +# cd ~/circuitpython/ports/zephyr-cp +# west init -l zephyr-config +# west update +# west zephyr-export +# pip install -r zephyr/scripts/requirements.txt +# pip install -r ../../requirements-dev.txt +# west sdk install -t x86_64-zephyr-elf +# python ../../tools/ci_fetch_deps.py zephyr-cp +WORKDIR /home/dev/circuitpython/ports/zephyr-cp +CMD ["/bin/bash"] diff --git a/ports/zephyr-cp/native_sim_build_init_container.sh b/ports/zephyr-cp/native_sim_build_init_container.sh new file mode 100755 index 0000000000000..705fb3eb9ff99 --- /dev/null +++ b/ports/zephyr-cp/native_sim_build_init_container.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# One-time setup to run INSIDE the zephyr-cp-dev container on the first +# launch against a fresh bind-mounted circuitpython checkout. +# +# Usage (inside the container): +# cd ~/circuitpython/ports/zephyr-cp +# ./first_run.sh +# +# Safe to re-run; west/pip/etc. are idempotent. +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")" + +echo "==> west init" +if [ ! -d ../../.west ]; then + west init -l zephyr-config +else + echo " (already initialized, skipping)" +fi + +echo "==> west update" +west update + +echo "==> west zephyr-export" +west zephyr-export + +echo "==> pip install Zephyr requirements" +pip install -r zephyr/scripts/requirements.txt + +echo "==> pip install CircuitPython dev requirements" +pip install -r ../../requirements-dev.txt + +echo "==> west sdk install (x86_64-zephyr-elf)" +west sdk install -t x86_64-zephyr-elf + +echo "==> fetch port submodules" +git config --global --add safe.directory /home/dev/circuitpython +python ../../tools/ci_fetch_deps.py zephyr-cp + +echo +echo "First-run setup complete." +echo "You can now build with: make BOARD=native_native_sim" diff --git a/ports/zephyr-cp/native_sim_build_run_container.sh b/ports/zephyr-cp/native_sim_build_run_container.sh new file mode 100755 index 0000000000000..e30aca22b46a7 --- /dev/null +++ b/ports/zephyr-cp/native_sim_build_run_container.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Launch (or re-attach to) the zephyr-cp-dev container with the enclosing +# circuitpython checkout bind-mounted at /home/dev/circuitpython. Works from +# any CWD — the mount is resolved relative to this script's location. +# +# On first invocation, creates a persistent container named "zcp". On +# subsequent invocations, re-starts the same container so installed state +# (e.g. the Zephyr SDK under /home/dev/zephyr-sdk-*) survives across sessions. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +CONTAINER_NAME="zcp" +IMAGE="zephyr-cp-dev" + +if podman container exists "$CONTAINER_NAME"; then + exec podman start -ai "$CONTAINER_NAME" +else + exec podman run -it --name "$CONTAINER_NAME" \ + -v "$REPO_ROOT:/home/dev/circuitpython:Z" \ + --userns=keep-id \ + "$IMAGE" "$@" +fi diff --git a/ports/zephyr-cp/tests/test_aesio.py b/ports/zephyr-cp/tests/test_aesio.py new file mode 100644 index 0000000000000..3ae016ac8e346 --- /dev/null +++ b/ports/zephyr-cp/tests/test_aesio.py @@ -0,0 +1,223 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test the aesio module.""" + +import pytest + +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + + +KEY = b"Sixteen byte key" +PLAINTEXT = b"CircuitPython!!!" # 16 bytes + + +ECB_CODE = """\ +import aesio + +key = b'Sixteen byte key' +inp = b'CircuitPython!!!' +outp = bytearray(len(inp)) +cipher = aesio.AES(key, aesio.MODE_ECB) +cipher.encrypt_into(inp, outp) +print(f"ciphertext_hex: {outp.hex()}") + +decrypted = bytearray(len(outp)) +cipher.decrypt_into(bytes(outp), decrypted) +print(f"decrypted: {bytes(decrypted)}") +print(f"match: {bytes(decrypted) == inp}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": ECB_CODE}) +def test_aesio_ecb(circuitpython): + """AES-ECB round-trips and matches CPython cryptography's output.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + encryptor = Cipher(algorithms.AES(KEY), modes.ECB()).encryptor() + expected_hex = (encryptor.update(PLAINTEXT) + encryptor.finalize()).hex() + assert f"ciphertext_hex: {expected_hex}" in output + assert "match: True" in output + assert "done" in output + + +IV = b"InitializationVe" # 16 bytes +CBC_PLAINTEXT = b"CircuitPython!!!" * 2 # 32 bytes, multiple of 16 +CTR_PLAINTEXT = b"CircuitPython is fun to use!" # 28 bytes, arbitrary length + + +CBC_CODE = """\ +import aesio + +key = b'Sixteen byte key' +iv = b'InitializationVe' +inp = b'CircuitPython!!!' * 2 +outp = bytearray(len(inp)) +cipher = aesio.AES(key, aesio.MODE_CBC, iv) +print(f"mode: {cipher.mode}") +cipher.encrypt_into(inp, outp) +print(f"ciphertext_hex: {outp.hex()}") + +# Re-create cipher to reset IV state for decryption. +cipher2 = aesio.AES(key, aesio.MODE_CBC, iv) +decrypted = bytearray(len(outp)) +cipher2.decrypt_into(bytes(outp), decrypted) +print(f"match: {bytes(decrypted) == inp}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": CBC_CODE}) +def test_aesio_cbc(circuitpython): + """AES-CBC round-trips and matches CPython cryptography's output.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + encryptor = Cipher(algorithms.AES(KEY), modes.CBC(IV)).encryptor() + expected_hex = (encryptor.update(CBC_PLAINTEXT) + encryptor.finalize()).hex() + assert "mode: 2" in output + assert f"ciphertext_hex: {expected_hex}" in output + assert "match: True" in output + assert "done" in output + + +CTR_CODE = """\ +import aesio + +key = b'Sixteen byte key' +iv = b'InitializationVe' +inp = b'CircuitPython is fun to use!' +outp = bytearray(len(inp)) +cipher = aesio.AES(key, aesio.MODE_CTR, iv) +print(f"mode: {cipher.mode}") +cipher.encrypt_into(inp, outp) +print(f"ciphertext_hex: {outp.hex()}") + +cipher2 = aesio.AES(key, aesio.MODE_CTR, iv) +decrypted = bytearray(len(outp)) +cipher2.decrypt_into(bytes(outp), decrypted) +print(f"decrypted: {bytes(decrypted)}") +print(f"match: {bytes(decrypted) == inp}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": CTR_CODE}) +def test_aesio_ctr(circuitpython): + """AES-CTR handles arbitrary-length buffers and matches CPython output.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + encryptor = Cipher(algorithms.AES(KEY), modes.CTR(IV)).encryptor() + expected_hex = (encryptor.update(CTR_PLAINTEXT) + encryptor.finalize()).hex() + assert "mode: 6" in output + assert f"ciphertext_hex: {expected_hex}" in output + assert "match: True" in output + assert "done" in output + + +REKEY_CODE = """\ +import aesio + +key1 = b'Sixteen byte key' +key2 = b'Another 16 byte!' +inp = b'CircuitPython!!!' + +cipher = aesio.AES(key1, aesio.MODE_ECB) +out1 = bytearray(16) +cipher.encrypt_into(inp, out1) +print(f"ct1_hex: {out1.hex()}") + +cipher.rekey(key2) +out2 = bytearray(16) +cipher.encrypt_into(inp, out2) +print(f"ct2_hex: {out2.hex()}") +print(f"different: {bytes(out1) != bytes(out2)}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": REKEY_CODE}) +def test_aesio_rekey(circuitpython): + """rekey() switches the active key; ciphertexts match CPython for both keys.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + enc1 = Cipher(algorithms.AES(b"Sixteen byte key"), modes.ECB()).encryptor() + ct1 = (enc1.update(PLAINTEXT) + enc1.finalize()).hex() + enc2 = Cipher(algorithms.AES(b"Another 16 byte!"), modes.ECB()).encryptor() + ct2 = (enc2.update(PLAINTEXT) + enc2.finalize()).hex() + assert f"ct1_hex: {ct1}" in output + assert f"ct2_hex: {ct2}" in output + assert "different: True" in output + assert "done" in output + + +MODE_PROPERTY_CODE = """\ +import aesio + +key = b'Sixteen byte key' +iv = b'InitializationVe' +cipher = aesio.AES(key, aesio.MODE_ECB) +print(f"initial: {cipher.mode}") +print(f"ECB={aesio.MODE_ECB} CBC={aesio.MODE_CBC} CTR={aesio.MODE_CTR}") + +for name, m in (("ECB", aesio.MODE_ECB), ("CBC", aesio.MODE_CBC), ("CTR", aesio.MODE_CTR)): + cipher.mode = m + print(f"set_{name}: {cipher.mode}") + +try: + cipher.mode = 99 +except NotImplementedError as e: + print(f"bad_mode: NotImplementedError") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": MODE_PROPERTY_CODE}) +def test_aesio_mode_property(circuitpython): + """The mode property is readable, writable, and rejects unsupported values.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "initial: 1" in output + assert "ECB=1 CBC=2 CTR=6" in output + assert "set_ECB: 1" in output + assert "set_CBC: 2" in output + assert "set_CTR: 6" in output + assert "bad_mode: NotImplementedError" in output + assert "done" in output + + +KEY_LENGTHS_CODE = """\ +import aesio + +inp = b'CircuitPython!!!' +for key in (b'A' * 16, b'B' * 24, b'C' * 32): + cipher = aesio.AES(key, aesio.MODE_ECB) + out = bytearray(16) + cipher.encrypt_into(inp, out) + print(f"len{len(key)}: {out.hex()}") + +try: + aesio.AES(b'too short', aesio.MODE_ECB) +except ValueError: + print("bad_key: ValueError") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": KEY_LENGTHS_CODE}) +def test_aesio_key_lengths(circuitpython): + """AES-128/192/256 keys all work and match CPython; bad key length raises.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + for key in (b"A" * 16, b"B" * 24, b"C" * 32): + encryptor = Cipher(algorithms.AES(key), modes.ECB()).encryptor() + expected = (encryptor.update(PLAINTEXT) + encryptor.finalize()).hex() + assert f"len{len(key)}: {expected}" in output + assert "bad_key: ValueError" in output + assert "done" in output From b255a15053ca2cc2037705d8ce7625258bc06803 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Wed, 8 Apr 2026 22:23:16 -0400 Subject: [PATCH 498/517] fix more tests --- py/mpprint.c | 1 + py/objfun.c | 3 ++- tests/extmod/binascii_unhexlify.py | 2 +- tests/thread/disable_irq.py | 12 +++++++++--- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/py/mpprint.c b/py/mpprint.c index b09ee398b6d05..bb1f3d45e8ec9 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -597,6 +597,7 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { } else { base = 16; } + // CIRCUITPY-CHANGE: include "0x" for 'p' and 'P'. if (fmt_chr == 'p' || fmt_chr == 'P') { #if SUPPORT_INT_BASE_PREFIX chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags | PF_FLAG_SHOW_PREFIX, fill, width); diff --git a/py/objfun.c b/py/objfun.c index e6a923d59e886..34565cf633632 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -184,7 +184,8 @@ static mp_obj_t fun_bc_make_new(const mp_obj_type_t *type, size_t n_args, size_t static void fun_bc_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_fun_bc_t *o = MP_OBJ_TO_PTR(o_in); - mp_printf(print, "", mp_obj_fun_get_name(o_in), o); + // CIRCUITPY-CHANGE: %p already prints "0x", so don't include it explicitly. + mp_printf(print, "", mp_obj_fun_get_name(o_in), o); } #endif diff --git a/tests/extmod/binascii_unhexlify.py b/tests/extmod/binascii_unhexlify.py index fe1d50780eeac..b08704cba65b2 100644 --- a/tests/extmod/binascii_unhexlify.py +++ b/tests/extmod/binascii_unhexlify.py @@ -14,7 +14,7 @@ # CIRCUITPY-CHANGE # Unicode strings can be decoded -print(binascii.unhexlify("313233344142434461626364")) +print(unhexlify("313233344142434461626364")) try: a = unhexlify(b"0") # odd buffer length diff --git a/tests/thread/disable_irq.py b/tests/thread/disable_irq.py index 3f1ac74f30877..596e3e477b9c8 100644 --- a/tests/thread/disable_irq.py +++ b/tests/thread/disable_irq.py @@ -1,8 +1,14 @@ # Ensure that disabling IRQs creates mutual exclusion between threads # (also tests nesting of disable_irq across threads) -import machine -import time -import _thread + +# CIRCUITPY-CHANGE: no machine +try: + import machine + import time + import _thread +except ImportError: + print("SKIP") + raise SystemExit if not hasattr(machine, "disable_irq"): print("SKIP") From e76bc123763d5c3c110d4ca80a2c7a1e8ebdde56 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 9 Apr 2026 19:11:33 -0400 Subject: [PATCH 499/517] update asyncio library, which fixes tests --- frozen/Adafruit_CircuitPython_asyncio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frozen/Adafruit_CircuitPython_asyncio b/frozen/Adafruit_CircuitPython_asyncio index e69ac03dccfd8..1f9fee3c5d99d 160000 --- a/frozen/Adafruit_CircuitPython_asyncio +++ b/frozen/Adafruit_CircuitPython_asyncio @@ -1 +1 @@ -Subproject commit e69ac03dccfd87ccaf3655dc751331ff922f525f +Subproject commit 1f9fee3c5d99d60b069c4a366678b391c198d955 From b74c906fe0925fb82ebc19a59c8023df24c94134 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 9 Apr 2026 20:40:58 -0400 Subject: [PATCH 500/517] update pre-commit config;remove non-CircuitPython extmod files --- .pre-commit-config.yaml | 6 +- extmod/cyw43_config_common.h | 126 ------------------------- extmod/littlefs-include/lfs2_defines.h | 12 --- extmod/lwip-include/lwipopts_common.h | 114 ---------------------- 4 files changed, 3 insertions(+), 255 deletions(-) delete mode 100644 extmod/cyw43_config_common.h delete mode 100644 extmod/littlefs-include/lfs2_defines.h delete mode 100644 extmod/lwip-include/lwipopts_common.h diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4db04e1d0eddb..c39faf99f00d2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-yaml - id: end-of-file-fixer @@ -55,7 +55,7 @@ repos: language: python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.9.4 + rev: v0.15.7 hooks: # Run the linter. - id: ruff @@ -63,6 +63,6 @@ repos: # Run the formatter. - id: ruff-format - repo: https://github.com/tox-dev/pyproject-fmt - rev: "v2.5.0" + rev: "v2.21.0" hooks: - id: pyproject-fmt diff --git a/extmod/cyw43_config_common.h b/extmod/cyw43_config_common.h deleted file mode 100644 index 595af37d71318..0000000000000 --- a/extmod/cyw43_config_common.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2025 Damien P. George, Angus Gratton - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef MICROPY_INCLUDED_EXTMOD_CYW43_CONFIG_COMMON_H -#define MICROPY_INCLUDED_EXTMOD_CYW43_CONFIG_COMMON_H - -// The board-level config will be included here, so it can set some CYW43 values. -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "py/runtime.h" -#include "extmod/modnetwork.h" -#include "lwip/apps/mdns.h" -#include "pendsv.h" - -// This file is included at the top of port-specific cyw43_configport.h files, -// and holds the MicroPython-specific but non-port-specific parts. -// -// It's included into both MicroPython sources and the lib/cyw43-driver sources. - -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_NETUTILS (1) -#define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) - -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out - -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK - -#define CYW43_HOST_NAME mod_network_hostname_data - -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE -#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP -#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 -#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_config mp_hal_pin_config -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high - -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - -#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) - -// Note: this function is only called if CYW43_POST_POLL_HOOK is defined in cyw43_configport.h -void cyw43_post_poll_hook(void); - -#ifdef MICROPY_EVENT_POLL_HOOK -// Older style hook macros on some ports -#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK -#else -// Newer style hooks on other ports -#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() -#endif - -static inline void cyw43_delay_us(uint32_t us) { - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - } -} - -static inline void cyw43_delay_ms(uint32_t ms) { - // PendSV may be disabled via CYW43_THREAD_ENTER, so this delay is a busy loop. - uint32_t us = ms * 1000; - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - CYW43_EVENT_POLL_HOOK; - } -} - -#if LWIP_MDNS_RESPONDER == 1 - -// Hook for any additional TCP/IP initialization than needs to be done. -// Called after the netif specified by `itf` has been set up. -#ifndef CYW43_CB_TCPIP_INIT_EXTRA -#define CYW43_CB_TCPIP_INIT_EXTRA(self, itf) mdns_resp_add_netif(&self->netif[itf], mod_network_hostname_data) -#endif - -// Hook for any additional TCP/IP deinitialization than needs to be done. -// Called before the netif specified by `itf` is removed. -#ifndef CYW43_CB_TCPIP_DEINIT_EXTRA -#define CYW43_CB_TCPIP_DEINIT_EXTRA(self, itf) mdns_resp_remove_netif(&self->netif[itf]) -#endif - -#endif - -#endif // MICROPY_INCLUDED_EXTMOD_CYW43_CONFIG_COMMON_H diff --git a/extmod/littlefs-include/lfs2_defines.h b/extmod/littlefs-include/lfs2_defines.h deleted file mode 100644 index 4ae566f508afa..0000000000000 --- a/extmod/littlefs-include/lfs2_defines.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef LFS2_DEFINES_H -#define LFS2_DEFINES_H - -#include "py/mpconfig.h" - -#if MICROPY_PY_DEFLATE -// We reuse the CRC32 implementation from uzlib to save a few bytes -#include "lib/uzlib/uzlib.h" -#define LFS2_CRC(crc, buffer, size) uzlib_crc32(buffer, size, crc) -#endif - -#endif \ No newline at end of file diff --git a/extmod/lwip-include/lwipopts_common.h b/extmod/lwip-include/lwipopts_common.h deleted file mode 100644 index 8cb1acfe2ca62..0000000000000 --- a/extmod/lwip-include/lwipopts_common.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2025 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef MICROPY_INCLUDED_LWIPOPTS_COMMON_H -#define MICROPY_INCLUDED_LWIPOPTS_COMMON_H - -#include "py/mpconfig.h" - -// This is needed to access `next_timeout` via `sys_timeouts_get_next_timeout()`. -#define LWIP_TESTMODE 1 - -// This sys-arch protection is not needed. -// Ports either protect lwIP code with flags, or run it at PendSV priority. -#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) -#define SYS_ARCH_PROTECT(lev) do { } while (0) -#define SYS_ARCH_UNPROTECT(lev) do { } while (0) - -#define NO_SYS 1 -#define SYS_LIGHTWEIGHT_PROT 1 -#define MEM_ALIGNMENT 4 - -#define LWIP_CHKSUM_ALGORITHM 3 -#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 - -#define LWIP_ARP 1 -#define LWIP_ETHERNET 1 -#define LWIP_RAW 1 -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 -#define LWIP_STATS 0 -#define LWIP_NETIF_HOSTNAME 1 - -#define LWIP_DHCP 1 -#define LWIP_DHCP_CHECK_LINK_UP 1 -#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up -#define LWIP_DNS 1 -#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 -#define LWIP_MDNS_RESPONDER 1 -#define LWIP_IGMP 1 - -#if MICROPY_PY_LWIP_PPP -#define PPP_SUPPORT 1 -#define PAP_SUPPORT 1 -#define CHAP_SUPPORT 1 -#endif - -#define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER -#define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) - -// The mDNS responder requires 5 timers per IP version plus 2 others. Not having enough silently breaks it. -#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + (LWIP_MDNS_RESPONDER * (2 + (5 * (LWIP_IPV4 + LWIP_IPV6))))) - -#define SO_REUSE 1 -#define TCP_LISTEN_BACKLOG 1 - -// TCP memory settings. -// Default lwIP settings takes 15800 bytes; TCP d/l: 380k/s local, 7.2k/s remote; TCP u/l is very slow. -#ifndef MEM_SIZE - -#if 0 -// lwIP takes 19159 bytes; TCP d/l and u/l are around 320k/s on local network. -#define MEM_SIZE (5000) -#define TCP_WND (4 * TCP_MSS) -#define TCP_SND_BUF (4 * TCP_MSS) -#endif - -#if 1 -// lwIP takes 26700 bytes; TCP dl/ul are around 750/600 k/s on local network. -#define MEM_SIZE (8000) -#define TCP_MSS (800) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) -#endif - -#if 0 -// lwIP takes 45600 bytes; TCP dl/ul are around 1200/1000 k/s on local network. -#define MEM_SIZE (16000) -#define TCP_MSS (1460) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) -#endif - -#endif // MEM_SIZE - -// Needed for PPP. -#define sys_jiffies sys_now - -typedef uint32_t sys_prot_t; - -#endif // MICROPY_INCLUDED_LWIPOPTS_COMMON_H From dfa5f8e2282a6c6111ecacfd0355f958433a37d2 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Thu, 9 Apr 2026 20:41:08 -0400 Subject: [PATCH 501/517] fix precommit complaints --- lib/littlefs/lfs2.c | 4 +- locale/circuitpython.pot | 69 +++++++++-------- ports/espressif/tools/update_sdkconfig.py | 2 +- py/binary.c | 2 +- py/makeversionhdr.py | 19 ----- py/malloc.c | 4 +- py/mpprint.c | 10 +-- py/objint_longlong.c | 30 +++++++- py/objlist.c | 4 +- py/sequence.c | 2 +- pyproject.toml | 94 ++++++++++------------- shared/runtime/pyexec.c | 13 ++-- tests/basics/builtin_slice.py | 2 +- tests/basics/fun_code_full.py | 2 - tests/misc/rge_sm.py | 42 +++++----- tools/analyze_heap_dump.py | 2 +- tools/chart_code_size.py | 2 +- tools/ci.sh | 2 +- 18 files changed, 155 insertions(+), 150 deletions(-) diff --git a/lib/littlefs/lfs2.c b/lib/littlefs/lfs2.c index aec2ddab9b663..8d18b7d110521 100644 --- a/lib/littlefs/lfs2.c +++ b/lib/littlefs/lfs2.c @@ -643,7 +643,7 @@ static int lfs2_alloc_scan(lfs2_t *lfs2) { // // note we limit the lookahead buffer to at most the amount of blocks // checkpointed, this prevents the math in lfs2_alloc from underflowing - lfs2->lookahead.start = (lfs2->lookahead.start + lfs2->lookahead.next) + lfs2->lookahead.start = (lfs2->lookahead.start + lfs2->lookahead.next) % lfs2->block_count; lfs2->lookahead.next = 0; lfs2->lookahead.size = lfs2_min( @@ -5257,7 +5257,7 @@ static int lfs2_fs_grow_(lfs2_t *lfs2, lfs2_size_t block_count) { return 0; } - + #ifndef LFS2_SHRINKNONRELOCATING // shrinking is not supported LFS2_ASSERT(block_count >= lfs2->block_count); diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 2b67216cf506a..bd9fe69cb317a 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -78,6 +78,11 @@ msgid "" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" msgstr "" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "" @@ -259,10 +264,6 @@ msgstr "" msgid "%q out of range" msgstr "" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "" @@ -956,6 +957,14 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c msgid "ESP-IDF memory allocation failed" @@ -1020,10 +1029,6 @@ msgstr "" msgid "Failed to buffer the sample" msgstr "" -#: ports/zephyr-cp/common-hal/_bleio/Adapter.c -msgid "Failed to connect" -msgstr "" - #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c #: ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -2615,10 +2620,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2832,6 +2833,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2930,14 +2935,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3092,7 +3097,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3476,6 +3481,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3670,6 +3679,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3718,10 +3731,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3743,7 +3752,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -4102,6 +4111,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4111,6 +4124,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4375,10 +4392,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4400,10 +4413,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" diff --git a/ports/espressif/tools/update_sdkconfig.py b/ports/espressif/tools/update_sdkconfig.py index 209b976b881e9..d40614edb75d8 100644 --- a/ports/espressif/tools/update_sdkconfig.py +++ b/ports/espressif/tools/update_sdkconfig.py @@ -157,7 +157,7 @@ def sym_default(sym): default=False, help="Updates the sdkconfigs outside of the board directory.", ) -def update(debug, board, update_all): # noqa: C901: too complex +def update(debug, board, update_all): # noqa: C901 too complex """Updates related sdkconfig files based on the build directory version that was likely modified by menuconfig.""" diff --git a/py/binary.c b/py/binary.c index 24c3a5d42989c..180a13beec7bf 100644 --- a/py/binary.c +++ b/py/binary.c @@ -438,7 +438,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p val = fp_sp.i; break; } - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE #if MICROPY_PY_DOUBLE_TYPECODE case 'd': { union { diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 09d67d87f8ad2..94321ef0bad4d 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -27,25 +27,6 @@ def cannot_determine_version(): make fetch-tags""" ) - with open(os.path.join(repo_path, "py", "mpconfig.h")) as f: - for line in f: - if line.startswith("#define MICROPY_VERSION_MAJOR "): - ver_major = int(line.strip().split()[2]) - elif line.startswith("#define MICROPY_VERSION_MINOR "): - ver_minor = int(line.strip().split()[2]) - elif line.startswith("#define MICROPY_VERSION_MICRO "): - ver_micro = int(line.strip().split()[2]) - elif line.startswith("#define MICROPY_VERSION_PRERELEASE "): - ver_prerelease = int(line.strip().split()[2]) - git_tag = "v%d.%d.%d%s" % ( - ver_major, - ver_minor, - ver_micro, - "-preview" if ver_prerelease else "", - ) - return git_tag - return None - def make_version_header(repo_path, filename): # Get version info using git (required) diff --git a/py/malloc.c b/py/malloc.c index 36eb10b7b85e7..fa2878d5905dd 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -145,8 +145,8 @@ void *m_malloc_with_finaliser(size_t num_bytes) { void *m_malloc0(size_t num_bytes) { // CIRCUITPY-CHANGE: use helper return m_malloc_helper(num_bytes, - (MICROPY_GC_CONSERVATIVE_CLEAR ? 0 : M_MALLOC_ENSURE_ZEROED) - | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); + (MICROPY_GC_CONSERVATIVE_CLEAR ? 0 : M_MALLOC_ENSURE_ZEROED) + | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); } // CIRCUITPY-CHANGE: add selective collect diff --git a/py/mpprint.c b/py/mpprint.c index bb1f3d45e8ec9..62e80d1e8e85c 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -599,11 +599,11 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { } // CIRCUITPY-CHANGE: include "0x" for 'p' and 'P'. if (fmt_chr == 'p' || fmt_chr == 'P') { - #if SUPPORT_INT_BASE_PREFIX - chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags | PF_FLAG_SHOW_PREFIX, fill, width); - #else - chrs += mp_print_strn(print, "0x", 2, flags, fill, width); - #endif + #if SUPPORT_INT_BASE_PREFIX + chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags | PF_FLAG_SHOW_PREFIX, fill, width); + #else + chrs += mp_print_strn(print, "0x", 2, flags, fill, width); + #endif } chrs += mp_print_int(print, val, fmt_chr == 'd', base, fmt_c, flags, fill, width); break; diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 4ff6ee6cff8fe..f24aa0cc18664 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -45,6 +45,7 @@ const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; static void raise_long_long_overflow(void) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("result overflows long long storage")); +} // CIRCUITPY-CHANGE: bit_length mp_obj_t mp_obj_int_bit_length_impl(mp_obj_t self_in) { @@ -335,8 +336,33 @@ mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) { } mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { - // TODO: Check overflow - return mp_obj_int_get_truncated(self_in); + if (mp_obj_is_small_int(self_in)) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } else { + const mp_obj_int_t *self = self_in; + long long value = self->val; + mp_int_t truncated = (mp_int_t)value; + if ((long long)truncated == value) { + return truncated; + } + } + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("overflow converting long int to machine word")); +} + +mp_uint_t mp_obj_int_get_uint_checked(mp_const_obj_t self_in) { + if (mp_obj_is_small_int(self_in)) { + if (MP_OBJ_SMALL_INT_VALUE(self_in) >= 0) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } + } else { + const mp_obj_int_t *self = self_in; + long long value = self->val; + mp_uint_t truncated = (mp_uint_t)value; + if (value >= 0 && (long long)truncated == value) { + return truncated; + } + } + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("overflow converting long int to machine word")); } #if MICROPY_PY_BUILTINS_FLOAT diff --git a/py/objlist.c b/py/objlist.c index 83bade273bf6f..a29fc51b10472 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -173,8 +173,8 @@ static mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { static mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { #if MICROPY_PY_BUILTINS_SLICE if (mp_obj_is_type(index, &mp_type_slice)) { - // CIRCUITPY-CHANGE: handle subclassing - mp_obj_list_t *self = native_list(self_in); + // CIRCUITPY-CHANGE: handle subclassing + mp_obj_list_t *self = native_list(self_in); mp_bound_slice_t slice; bool fast = mp_seq_get_fast_slice_indexes(self->len, index, &slice); if (value == MP_OBJ_SENTINEL) { diff --git a/py/sequence.c b/py/sequence.c index 33cc55eac4060..6bbc62f0f7fb1 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -40,7 +40,7 @@ size_t mp_seq_multiply_len(size_t item_sz, size_t len) { if (mp_mul_mp_int_t_overflow(item_sz, len, &new_len)) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); } - return (size_t) new_len; + return (size_t)new_len; } // Implements backend of sequence * integer operation. Assumes elements are diff --git a/pyproject.toml b/pyproject.toml index e88137bb0f7bb..5f84ca104ac47 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,42 +1,26 @@ -[tool.codespell] -count = "" -ignore-regex = '\b[A-Z]{3}\b' -# CIRCUITPY-CHANGE: aranges -ignore-words-list = "ans,asend,aranges,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" -quiet-level = 3 -skip = """ -*/build*,\ -./.git,\ -./drivers/cc3100,\ -./lib,\ -./ports/cc3200/FreeRTOS,\ -./ports/cc3200/bootmgr/sl,\ -./ports/cc3200/hal,\ -./ports/cc3200/simplelink,\ -./ports/cc3200/telnet,\ -./ports/esp32/managed_components,\ -./ports/nrf/drivers/bluetooth/s1*,\ -./ports/stm32/usbhost,\ -./tests,\ -ACKNOWLEDGEMENTS,\ -""" - -# CIRCUITPY-CHANGES: additions and removals +# can be empty if no extra settings are needed, presence enables setuptools-scm [tool.ruff] +target-version = "py312" +line-length = 99 +# Include Python source files that don't end with .py +extend-include = [ "tools/cc1" ] # Exclude third-party code from linting and formatting extend-exclude = [ + # CIRCUITPY-CHANGES "extmod/ulab", "frozen", "lib", "ports/analog/msdk", "ports/atmel-samd/asf4", "ports/broadcom/peripherals", + "ports/espress/tools", "ports/espressif/esp-idf", "ports/espressif/esp-protocols", "ports/raspberrypi/sdk", "ports/silabs/gecko_sdk", "ports/silabs/tools/slc_cli_linux", "ports/stm/st_driver", + "tests/cpydiff/syntax_assign_expr.py", # intentionally incorrect CPython code "tools/adabot", "tools/bitmap_font", "tools/cc1", @@ -45,12 +29,20 @@ extend-exclude = [ "tools/python-semver", "tools/uf2", ] -# Include Python source files that don't end with .py -line-length = 99 -target-version = "py38" - -[tool.ruff.lint] -exclude = [ # Ruff finds Python SyntaxError in these files +# Exclude third-party code, and exclude the following tests: +# basics: needs careful attention before applying automatic formatting +# repl_: not real python files +# viper_args: uses f(*) +format.exclude = [ + "tests/*/repl_*.py", + "tests/basics/*.py", + "tests/cmdline/cmd_compile_only_error.py", + "tests/micropython/test_normalize_newlines.py", + "tests/micropython/viper_args.py", +] +lint.extend-select = [ "C9", "PLC" ] +lint.exclude = [ + # Ruff finds Python SyntaxError in these files "tests/cmdline/cmd_compile_only_error.py", "tests/cmdline/repl_autocomplete.py", "tests/cmdline/repl_autocomplete_underscore.py", @@ -64,8 +56,7 @@ exclude = [ # Ruff finds Python SyntaxError in these files "tests/feature_check/repl_words_move_check.py", "tests/micropython/viper_args.py", ] -extend-select = ["C9", "PLC"] -extend-ignore = [ +lint.extend-ignore = [ "E401", "E402", "E722", @@ -77,27 +68,24 @@ extend-ignore = [ "PLC0206", "PLC0415", # conditional imports are common in MicroPython ] -mccabe.max-complexity = 40 - -[tool.ruff.lint.per-file-ignores] -# Exclude all tests from linting. -"tests/**/*.py" = ["ALL"] -"ports/cc3200/tools/uniflash.py" = ["E711"] # manifest.py files are evaluated with some global names pre-defined -"**/manifest.py" = ["F821"] -"ports/**/boards/**/manifest_*.py" = ["F821"] +lint.per-file-ignores."**/manifest.py" = [ "F821" ] +lint.per-file-ignores."ports/**/boards/**/manifest_*.py" = [ "F821" ] +lint.per-file-ignores."ports/cc3200/tools/uniflash.py" = [ "E711" ] +# Exclude all tests from linting. +lint.per-file-ignores."tests/**/*.py" = [ "ALL" ] # Uses assignment expressions. -"tests/cpydiff/syntax_assign_expr.py" = ["F821"] +lint.per-file-ignores."tests/cpydiff/syntax_assign_expr.py" = [ "F821" ] +lint.mccabe.max-complexity = 40 -[tool.ruff.format] -# Exclude third-party code, and exclude the following tests: -# basics: needs careful attention before applying automatic formatting -# repl_: not real python files -# viper_args: uses f(*) -exclude = [ - "tests/basics/*.py", - "tests/*/repl_*.py", - "tests/cmdline/cmd_compile_only_error.py", - "tests/micropython/test_normalize_newlines.py", - "tests/micropython/viper_args.py", -] +[tool.codespell] +count = "" +ignore-regex = "\\b[A-Z]{3}\\b" +# CIRCUITPY-CHANGE: aranges +ignore-words-list = "ans,aranges,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" +quiet-level = 3 +skip = """\ + */build*,./.git,./drivers/cc3100,./lib,./ports/cc3200/FreeRTOS,./ports/cc3200/bootmgr/sl,./ports/cc3200/hal,./ports/c\ + c3200/simplelink,./ports/cc3200/telnet,./ports/esp32/managed_components,./ports/nrf/drivers/bluetooth/s1*,./ports/stm\ + 32/usbhost,./tests,ACKNOWLEDGEMENTS,\ + """ diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 09f1796c5d4d6..430892a00bceb 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -92,7 +92,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input // Also make it possible to determine if module_fun was set. mp_obj_t module_fun = NULL; - // CIRCUITPY-CHANGE: add atexit support + // CIRCUITPY-CHANGE: add atexit support #if CIRCUITPY_ATEXIT if (exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT) { atexit_callback_t *callback = (atexit_callback_t *)source; @@ -148,7 +148,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input // CIRCUITPY-CHANGE: garbage collect after loading // If the code was loaded from a file, collect any garbage before running. if (input_kind == MP_PARSE_FILE_INPUT) { - gc_collect(); + gc_collect(); } // execute code @@ -221,11 +221,11 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input ret = PYEXEC_FORCED_EXIT; #endif // CIRCUITPY-CHANGE: support DeepSleepRequest - #if CIRCUITPY_ALARM + #if CIRCUITPY_ALARM } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exception_obj_type), MP_OBJ_FROM_PTR(&mp_type_DeepSleepRequest))) { ret = PYEXEC_DEEP_SLEEP; - #endif - // CIRCUITPY-CHANGE: supprt ReloadException + #endif + // CIRCUITPY-CHANGE: support ReloadException } else if (exception_obj == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception))) { ret = PYEXEC_RELOAD; } else { // other exception @@ -247,8 +247,7 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input #if CIRCUITPY_ALARM && ret != PYEXEC_DEEP_SLEEP #endif - ) - { + ) { mp_obj_t return_value = (mp_obj_t)nlr.ret_val; result->exception = return_value; result->exception_line = -1; diff --git a/tests/basics/builtin_slice.py b/tests/basics/builtin_slice.py index 7f548ee335839..cbce3d14ed699 100644 --- a/tests/basics/builtin_slice.py +++ b/tests/basics/builtin_slice.py @@ -1,7 +1,7 @@ # test builtin slice # ensures that slices passed to user types are heap-allocated and can be -# safely stored as well as not overriden by subsequent slices. +# safely stored as well as not overridden by subsequent slices. # print slice class A: diff --git a/tests/basics/fun_code_full.py b/tests/basics/fun_code_full.py index e1c867939a2a7..538863631a6e0 100644 --- a/tests/basics/fun_code_full.py +++ b/tests/basics/fun_code_full.py @@ -36,5 +36,3 @@ def f(x, y): prev_end = end else: print("contiguous") - - diff --git a/tests/misc/rge_sm.py b/tests/misc/rge_sm.py index 56dad5749776e..33aa4edb7427a 100644 --- a/tests/misc/rge_sm.py +++ b/tests/misc/rge_sm.py @@ -47,26 +47,30 @@ def solve(self, finishtime): lambda *a: 41.0 / 96.0 / math.pi**2 * a[1] ** 3, # g1 lambda *a: -19.0 / 96.0 / math.pi**2 * a[2] ** 3, # g2 lambda *a: -42.0 / 96.0 / math.pi**2 * a[3] ** 3, # g3 - lambda *a: 1.0 - / 16.0 - / math.pi**2 - * ( - 9.0 / 2.0 * a[4] ** 3 - - 8.0 * a[3] ** 2 * a[4] - - 9.0 / 4.0 * a[2] ** 2 * a[4] - - 17.0 / 12.0 * a[1] ** 2 * a[4] + lambda *a: ( + 1.0 + / 16.0 + / math.pi**2 + * ( + 9.0 / 2.0 * a[4] ** 3 + - 8.0 * a[3] ** 2 * a[4] + - 9.0 / 4.0 * a[2] ** 2 * a[4] + - 17.0 / 12.0 * a[1] ** 2 * a[4] + ) ), # yt - lambda *a: 1.0 - / 16.0 - / math.pi**2 - * ( - 24.0 * a[5] ** 2 - + 12.0 * a[4] ** 2 * a[5] - - 9.0 * a[5] * (a[2] ** 2 + 1.0 / 3.0 * a[1] ** 2) - - 6.0 * a[4] ** 4 - + 9.0 / 8.0 * a[2] ** 4 - + 3.0 / 8.0 * a[1] ** 4 - + 3.0 / 4.0 * a[2] ** 2 * a[1] ** 2 + lambda *a: ( + 1.0 + / 16.0 + / math.pi**2 + * ( + 24.0 * a[5] ** 2 + + 12.0 * a[4] ** 2 * a[5] + - 9.0 * a[5] * (a[2] ** 2 + 1.0 / 3.0 * a[1] ** 2) + - 6.0 * a[4] ** 4 + + 9.0 / 8.0 * a[2] ** 4 + + 3.0 / 8.0 * a[1] ** 4 + + 3.0 / 4.0 * a[2] ** 2 * a[1] ** 2 + ) ), # lambda ) diff --git a/tools/analyze_heap_dump.py b/tools/analyze_heap_dump.py index ac1bb0c8ce592..a7be95db7d9f3 100755 --- a/tools/analyze_heap_dump.py +++ b/tools/analyze_heap_dump.py @@ -82,7 +82,7 @@ help="Draw the ownership graph of blocks on the heap", ) @click.option("--analyze-snapshots", default="last", type=click.Choice(["all", "last"])) -def do_all_the_things( # noqa: C901: too complex +def do_all_the_things( # noqa: C901 too complex ram_filename, bin_filename, map_filename, diff --git a/tools/chart_code_size.py b/tools/chart_code_size.py index cd62074fd0d83..f3d9c5bdc40f5 100644 --- a/tools/chart_code_size.py +++ b/tools/chart_code_size.py @@ -24,7 +24,7 @@ def parse_hex(h): @click.command() @click.argument("elf_filename") -def do_all_the_things(elf_filename): # noqa: C901: too complex +def do_all_the_things(elf_filename): # noqa: C901 too complex symbol = None last_address = 0 all_symbols = {} diff --git a/tools/ci.sh b/tools/ci.sh index 60e870ce65b74..132fbd8f81cf3 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -88,7 +88,7 @@ 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} - + 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" # Default GitHub pull request sets HEAD to a generated merge commit From 03c641a8568e5d2403f87b84f58a27c1458139df Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 9 Apr 2026 19:45:04 -0500 Subject: [PATCH 502/517] add DISABLED_MODULES mechanism for zephyr boards. disable aesio for norf7002dk --- ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 2 +- ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml | 1 + ports/zephyr-cp/cptools/build_circuitpython.py | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 184ff34b7ef46..5faed268cdb7d 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -10,7 +10,7 @@ _pixelmap = false _stage = false adafruit_bus_device = false adafruit_pixelbuf = false -aesio = true +aesio = false alarm = false analogbufio = false analogio = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml index 76b10578813bd..7fcb145588bdb 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml @@ -2,3 +2,4 @@ CIRCUITPY_BUILD_EXTENSIONS = ["elf"] USB_VID=0x239A USB_PID=0x8168 BLOBS=["nrf_wifi"] +DISABLED_MODULES=["aesio"] diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 3edbc1efdf6c8..32173914a2a7a 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -405,6 +405,8 @@ async def build_circuitpython(): circuitpython_flags.append(f"-DCIRCUITPY_CREATION_ID=0x{creation_id:08x}") enabled_modules, module_reasons = determine_enabled_modules(board_info, portdir, srcdir) + for m in mpconfigboard.get("DISABLED_MODULES", []): + enabled_modules.discard(m) web_workflow_enabled = board_info.get("wifi", False) or board_info.get("hostnetwork", False) From ce39a6a7df2044f9b7e71a0849fdb6ca3cbeec2e Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 9 Apr 2026 21:08:48 -0500 Subject: [PATCH 503/517] fix init_container script, fix container file ownership, add container info to readme. --- ports/zephyr-cp/README.md | 27 +++++++++++++++++++ .../zephyr-cp/native_sim_build_Containerfile | 5 +++- .../native_sim_build_init_container.sh | 5 ++-- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/ports/zephyr-cp/README.md b/ports/zephyr-cp/README.md index 28bbfbf298441..162ed90bcc8b7 100644 --- a/ports/zephyr-cp/README.md +++ b/ports/zephyr-cp/README.md @@ -28,6 +28,33 @@ make BOARD=nordic_nrf7002dk This uses Zephyr's cmake to generate Makefiles that then delegate to `tools/cpbuild/build_circuitpython.py` to build the CircuitPython bits in parallel. +## Native simulator build container + +Building the native sim requires `libsdl2-dev:i386` and other 32bit dependencies that +can cause conflicts on 64bit systems resulting in the removal of 64bit versions of critical +software such as the display manager and network manager. A Containerfile and a few scripts +are provided to set up a container to make the native sim build inside without affecting the +host system. + +The container automatically mounts this instance of the circuitpython repo inside at +`/home/dev/circuitpython`. Changes made in the repo inside the container and on the host PC +will sync automatically between host and container. + +To use the container file: + +1. Build the container with `podman build -t zephyr-cp-dev -f native_sim_build_Containerfile .` +2. Run/Start the container by running `./native_sim_build_run_container.sh` on the host PC. + The script will automatically run or start based on whether the container has been run before. +3. Init requirements inside the container with `./native_sim_build_init_container.sh` + +To delete the container and cleanup associated files: +```sh +podman ps -a --filter ancestor=zephyr-cp-dev -q | xargs -r podman rm -f +podman rmi zephyr-cp-dev +podman image prune -f +podman rm -f zcp +``` + ## Running the native simulator From `ports/zephyr-cp`, run: diff --git a/ports/zephyr-cp/native_sim_build_Containerfile b/ports/zephyr-cp/native_sim_build_Containerfile index 8b9db9dc55926..3cd1a677da351 100644 --- a/ports/zephyr-cp/native_sim_build_Containerfile +++ b/ports/zephyr-cp/native_sim_build_Containerfile @@ -12,7 +12,10 @@ RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y --no-in protobuf-compiler sudo \ && rm -rf /var/lib/apt/lists/* -RUN useradd -m -s /bin/bash dev \ +# Remove the default ubuntu:24.04 'ubuntu' user (UID 1000) so 'dev' can take +# UID 1000 — required for --userns=keep-id to map host UID 1000 to 'dev'. +RUN userdel -r ubuntu 2>/dev/null || true \ + && useradd -m -u 1000 -s /bin/bash dev \ && echo 'dev ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/dev \ && chmod 440 /etc/sudoers.d/dev USER dev diff --git a/ports/zephyr-cp/native_sim_build_init_container.sh b/ports/zephyr-cp/native_sim_build_init_container.sh index 705fb3eb9ff99..b1b1453d27c1d 100755 --- a/ports/zephyr-cp/native_sim_build_init_container.sh +++ b/ports/zephyr-cp/native_sim_build_init_container.sh @@ -9,10 +9,12 @@ # Safe to re-run; west/pip/etc. are idempotent. set -euo pipefail +git config --global --add safe.directory /home/dev/circuitpython + cd "$(dirname "${BASH_SOURCE[0]}")" echo "==> west init" -if [ ! -d ../../.west ]; then +if [ ! -d .west ]; then west init -l zephyr-config else echo " (already initialized, skipping)" @@ -34,7 +36,6 @@ echo "==> west sdk install (x86_64-zephyr-elf)" west sdk install -t x86_64-zephyr-elf echo "==> fetch port submodules" -git config --global --add safe.directory /home/dev/circuitpython python ../../tools/ci_fetch_deps.py zephyr-cp echo From f0d6f587bf67480825ac604d53bf98a0e8d885b0 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Thu, 9 Apr 2026 21:15:27 -0500 Subject: [PATCH 504/517] update init_container script usage example --- ports/zephyr-cp/native_sim_build_init_container.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ports/zephyr-cp/native_sim_build_init_container.sh b/ports/zephyr-cp/native_sim_build_init_container.sh index b1b1453d27c1d..19831e1fcc28a 100755 --- a/ports/zephyr-cp/native_sim_build_init_container.sh +++ b/ports/zephyr-cp/native_sim_build_init_container.sh @@ -4,7 +4,7 @@ # # Usage (inside the container): # cd ~/circuitpython/ports/zephyr-cp -# ./first_run.sh +# ./native_sim_build_init_container.sh # # Safe to re-run; west/pip/etc. are idempotent. set -euo pipefail From 2b6945ae8264b876e288e54a193ffb0a792a01ef Mon Sep 17 00:00:00 2001 From: Bernhard Bablok Date: Fri, 10 Apr 2026 08:18:20 +0200 Subject: [PATCH 505/517] define board.display as a module, not a type --- ports/raspberrypi/boards/pimoroni_badger2350/pins.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/pins.c b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c index 9ba93cd47cb1a..ff9feb9cce464 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2350/pins.c +++ b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c @@ -20,17 +20,15 @@ static const mp_rom_map_elem_t lut_update_table[] = { }; MP_DEFINE_CONST_DICT(lut_update_dict, lut_update_table); -MP_DEFINE_CONST_OBJ_TYPE( - display_type, - MP_QSTR_display, - MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS | ~MP_TYPE_FLAG_BINDS_SELF, - locals_dict, &lut_update_dict - ); +const mp_obj_module_t display_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&lut_update_dict, +}; static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - { MP_ROM_QSTR(MP_QSTR_display), MP_ROM_PTR(&display_type) }, + { MP_ROM_QSTR(MP_QSTR_display), MP_ROM_PTR(&display_module) }, { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO0) }, From d7ed38b92b111570d85e2fcbc55838a240280fb1 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 10 Apr 2026 12:15:13 -0400 Subject: [PATCH 506/517] fix build errors --- py/circuitpy_mpconfig.h | 1 + shared/netutils/dhcpserver.c | 315 ++++++++++++++++++++++++ shared/netutils/dhcpserver.h | 49 ++++ shared/netutils/netutils.c | 100 ++++++++ shared/netutils/netutils.h | 58 +++++ shared/netutils/trace.c | 170 +++++++++++++ tests/micropython/emg_exc.py.native.exp | 3 +- 7 files changed, 694 insertions(+), 2 deletions(-) create mode 100644 shared/netutils/dhcpserver.c create mode 100644 shared/netutils/dhcpserver.h create mode 100644 shared/netutils/netutils.c create mode 100644 shared/netutils/netutils.h create mode 100644 shared/netutils/trace.c diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index e83c61752556e..c02f0b4f87084 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -46,6 +46,7 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_PY_BLUETOOTH (0) #define MICROPY_PY_LWIP_SLIP (0) #define MICROPY_PY_OS_DUPTERM (0) +#define MICROPY_PY_PYEXEC_COMPILE_ONLY (0) #define MICROPY_ROM_TEXT_COMPRESSION (0) #define MICROPY_VFS_LFS1 (0) #define MICROPY_VFS_LFS2 (0) diff --git a/shared/netutils/dhcpserver.c b/shared/netutils/dhcpserver.c new file mode 100644 index 0000000000000..6d9cdb97d1fc6 --- /dev/null +++ b/shared/netutils/dhcpserver.c @@ -0,0 +1,315 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// For DHCP specs see: +// https://www.ietf.org/rfc/rfc2131.txt +// https://tools.ietf.org/html/rfc2132 -- DHCP Options and BOOTP Vendor Extensions + +#include +#include +#include "py/mperrno.h" +#include "py/mphal.h" +#include "lwip/opt.h" + +// CIRCUITPY-CHANGE: comment +// Used in CIRCUITPY without MICROPY_PY_LWIP + +#if LWIP_UDP + +#include "shared/netutils/dhcpserver.h" +#include "lwip/udp.h" + +#define DHCPDISCOVER (1) +#define DHCPOFFER (2) +#define DHCPREQUEST (3) +#define DHCPDECLINE (4) +#define DHCPACK (5) +#define DHCPNACK (6) +#define DHCPRELEASE (7) +#define DHCPINFORM (8) + +#define DHCP_OPT_PAD (0) +#define DHCP_OPT_SUBNET_MASK (1) +#define DHCP_OPT_ROUTER (3) +#define DHCP_OPT_DNS (6) +#define DHCP_OPT_HOST_NAME (12) +#define DHCP_OPT_REQUESTED_IP (50) +#define DHCP_OPT_IP_LEASE_TIME (51) +#define DHCP_OPT_MSG_TYPE (53) +#define DHCP_OPT_SERVER_ID (54) +#define DHCP_OPT_PARAM_REQUEST_LIST (55) +#define DHCP_OPT_MAX_MSG_SIZE (57) +#define DHCP_OPT_VENDOR_CLASS_ID (60) +#define DHCP_OPT_CLIENT_ID (61) +#define DHCP_OPT_END (255) + +#define PORT_DHCP_SERVER (67) +#define PORT_DHCP_CLIENT (68) + +#define DEFAULT_DNS MAKE_IP4(192, 168, 4, 1) +#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds + +#define MAC_LEN (6) +#define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) + +typedef struct { + uint8_t op; // message opcode + uint8_t htype; // hardware address type + uint8_t hlen; // hardware address length + uint8_t hops; + uint32_t xid; // transaction id, chosen by client + uint16_t secs; // client seconds elapsed + uint16_t flags; + uint8_t ciaddr[4]; // client IP address + uint8_t yiaddr[4]; // your IP address + uint8_t siaddr[4]; // next server IP address + uint8_t giaddr[4]; // relay agent IP address + uint8_t chaddr[16]; // client hardware address + uint8_t sname[64]; // server host name + uint8_t file[128]; // boot file name + uint8_t options[312]; // optional parameters, variable, starts with magic +} dhcp_msg_t; + +static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) { + // family is AF_INET + // type is SOCK_DGRAM + + *udp = udp_new(); + if (*udp == NULL) { + return -MP_ENOMEM; + } + + // Register callback + udp_recv(*udp, cb_udp_recv, (void *)cb_data); + + return 0; // success +} + +static void dhcp_socket_free(struct udp_pcb **udp) { + if (*udp != NULL) { + udp_remove(*udp); + *udp = NULL; + } +} + +static int dhcp_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) { + ip_addr_t addr; + IP_ADDR4(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); + // TODO convert lwIP errors to errno + return udp_bind(*udp, &addr, port); +} + +static int dhcp_socket_sendto(struct udp_pcb **udp, struct netif *netif, const void *buf, size_t len, uint32_t ip, uint16_t port) { + if (len > 0xffff) { + len = 0xffff; + } + + struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM); + if (p == NULL) { + return -MP_ENOMEM; + } + + memcpy(p->payload, buf, len); + + ip_addr_t dest; + IP_ADDR4(&dest, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff); + err_t err; + if (netif != NULL) { + err = udp_sendto_if(*udp, p, &dest, port, netif); + } else { + err = udp_sendto(*udp, p, &dest, port); + } + + pbuf_free(p); + + if (err != ERR_OK) { + return err; + } + + return len; +} + +static uint8_t *opt_find(uint8_t *opt, uint8_t cmd) { + for (int i = 0; i < 308 && opt[i] != DHCP_OPT_END;) { + if (opt[i] == cmd) { + return &opt[i]; + } + i += 2 + opt[i + 1]; + } + return NULL; +} + +static void opt_write_n(uint8_t **opt, uint8_t cmd, size_t n, const void *data) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = n; + memcpy(o, data, n); + *opt = o + n; +} + +static void opt_write_u8(uint8_t **opt, uint8_t cmd, uint8_t val) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = 1; + *o++ = val; + *opt = o; +} + +static void opt_write_u32(uint8_t **opt, uint8_t cmd, uint32_t val) { + uint8_t *o = *opt; + *o++ = cmd; + *o++ = 4; + *o++ = val >> 24; + *o++ = val >> 16; + *o++ = val >> 8; + *o++ = val; + *opt = o; +} + +static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) { + dhcp_server_t *d = arg; + (void)upcb; + (void)src_addr; + (void)src_port; + + // This is around 548 bytes + dhcp_msg_t dhcp_msg; + + #define DHCP_MIN_SIZE (240 + 3) + if (p->tot_len < DHCP_MIN_SIZE) { + goto ignore_request; + } + + size_t len = pbuf_copy_partial(p, &dhcp_msg, sizeof(dhcp_msg), 0); + if (len < DHCP_MIN_SIZE) { + goto ignore_request; + } + + dhcp_msg.op = DHCPOFFER; + memcpy(&dhcp_msg.yiaddr, &ip_2_ip4(&d->ip)->addr, 4); + + uint8_t *opt = (uint8_t *)&dhcp_msg.options; + opt += 4; // assume magic cookie: 99, 130, 83, 99 + + switch (opt[2]) { + case DHCPDISCOVER: { + int yi = DHCPS_MAX_IP; + for (int i = 0; i < DHCPS_MAX_IP; ++i) { + if (memcmp(d->lease[i].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { + // MAC match, use this IP address + yi = i; + break; + } + if (yi == DHCPS_MAX_IP) { + // Look for a free IP address + if (memcmp(d->lease[i].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { + // IP available + yi = i; + } + uint32_t expiry = d->lease[i].expiry << 16 | 0xffff; + if ((int32_t)(expiry - mp_hal_ticks_ms()) < 0) { + // IP expired, reuse it + memset(d->lease[i].mac, 0, MAC_LEN); + yi = i; + } + } + } + if (yi == DHCPS_MAX_IP) { + // No more IP addresses left + goto ignore_request; + } + dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; + opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPOFFER); + break; + } + + case DHCPREQUEST: { + uint8_t *o = opt_find(opt, DHCP_OPT_REQUESTED_IP); + if (o == NULL) { + // Should be NACK + goto ignore_request; + } + if (memcmp(o + 2, &ip_2_ip4(&d->ip)->addr, 3) != 0) { + // Should be NACK + goto ignore_request; + } + uint8_t yi = o[5] - DHCPS_BASE_IP; + if (yi >= DHCPS_MAX_IP) { + // Should be NACK + goto ignore_request; + } + if (memcmp(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN) == 0) { + // MAC match, ok to use this IP address + } else if (memcmp(d->lease[yi].mac, "\x00\x00\x00\x00\x00\x00", MAC_LEN) == 0) { + // IP unused, ok to use this IP address + memcpy(d->lease[yi].mac, dhcp_msg.chaddr, MAC_LEN); + } else { + // IP already in use + // Should be NACK + goto ignore_request; + } + d->lease[yi].expiry = (mp_hal_ticks_ms() + DEFAULT_LEASE_TIME_S * 1000) >> 16; + dhcp_msg.yiaddr[3] = DHCPS_BASE_IP + yi; + opt_write_u8(&opt, DHCP_OPT_MSG_TYPE, DHCPACK); + // CIRCUITPY-CHANGE: use LWIP_DEBUGF instead of printf + LWIP_DEBUGF(DHCP_DEBUG, ("DHCPS: client connected: MAC=%02x:%02x:%02x:%02x:%02x:%02x IP=%u.%u.%u.%u\n", + dhcp_msg.chaddr[0], dhcp_msg.chaddr[1], dhcp_msg.chaddr[2], dhcp_msg.chaddr[3], dhcp_msg.chaddr[4], dhcp_msg.chaddr[5], + dhcp_msg.yiaddr[0], dhcp_msg.yiaddr[1], dhcp_msg.yiaddr[2], dhcp_msg.yiaddr[3])); + break; + } + + default: + goto ignore_request; + } + + opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &ip_2_ip4(&d->ip)->addr); + opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &ip_2_ip4(&d->nm)->addr); + opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &ip_2_ip4(&d->ip)->addr); // aka gateway; can have multiple addresses + opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have multiple addresses + opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S); + *opt++ = DHCP_OPT_END; + struct netif *netif = ip_current_input_netif(); + dhcp_socket_sendto(&d->udp, netif, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, 0xffffffff, PORT_DHCP_CLIENT); + +ignore_request: + pbuf_free(p); +} + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm) { + ip_addr_copy(d->ip, *ip); + ip_addr_copy(d->nm, *nm); + memset(d->lease, 0, sizeof(d->lease)); + if (dhcp_socket_new_dgram(&d->udp, d, dhcp_server_process) != 0) { + return; + } + dhcp_socket_bind(&d->udp, 0, PORT_DHCP_SERVER); +} + +void dhcp_server_deinit(dhcp_server_t *d) { + dhcp_socket_free(&d->udp); +} + +#endif // MICROPY_PY_LWIP diff --git a/shared/netutils/dhcpserver.h b/shared/netutils/dhcpserver.h new file mode 100644 index 0000000000000..2349d2ea427f4 --- /dev/null +++ b/shared/netutils/dhcpserver.h @@ -0,0 +1,49 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H +#define MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H + +#include "lwip/ip_addr.h" + +#define DHCPS_BASE_IP (16) +#define DHCPS_MAX_IP (8) + +typedef struct _dhcp_server_lease_t { + uint8_t mac[6]; + uint16_t expiry; +} dhcp_server_lease_t; + +typedef struct _dhcp_server_t { + ip_addr_t ip; + ip_addr_t nm; + dhcp_server_lease_t lease[DHCPS_MAX_IP]; + struct udp_pcb *udp; +} dhcp_server_t; + +void dhcp_server_init(dhcp_server_t *d, ip_addr_t *ip, ip_addr_t *nm); +void dhcp_server_deinit(dhcp_server_t *d); + +#endif // MICROPY_INCLUDED_LIB_NETUTILS_DHCPSERVER_H diff --git a/shared/netutils/netutils.c b/shared/netutils/netutils.c new file mode 100644 index 0000000000000..cd1422f7c8058 --- /dev/null +++ b/shared/netutils/netutils.c @@ -0,0 +1,100 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include "py/runtime.h" +#include "shared/netutils/netutils.h" + +// Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. +mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian) { + char ip_str[16]; + mp_uint_t ip_len; + if (endian == NETUTILS_LITTLE) { + ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[3], ip[2], ip[1], ip[0]); + } else { + ip_len = snprintf(ip_str, 16, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + } + return mp_obj_new_str(ip_str, ip_len); +} + +// Takes an array with a raw IP address, and a port, and returns a net-address +// tuple such as ('192.168.0.1', 8080). +mp_obj_t netutils_format_inet_addr(uint8_t *ip, mp_uint_t port, netutils_endian_t endian) { + mp_obj_t tuple[2] = { + tuple[0] = netutils_format_ipv4_addr(ip, endian), + tuple[1] = mp_obj_new_int(port), + }; + return mp_obj_new_tuple(2, tuple); +} + +void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian) { + size_t addr_len; + const char *addr_str = mp_obj_str_get_data(addr_in, &addr_len); + if (addr_len == 0) { + // special case of no address given + memset(out_ip, 0, NETUTILS_IPV4ADDR_BUFSIZE); + return; + } + const char *s = addr_str; + const char *s_top; + // Scan for the end of valid address characters + for (s_top = addr_str; s_top < addr_str + addr_len; s_top++) { + if (!(*s_top == '.' || (*s_top >= '0' && *s_top <= '9'))) { + break; + } + } + for (mp_uint_t i = 3; ; i--) { + mp_uint_t val = 0; + for (; s < s_top && *s != '.'; s++) { + val = val * 10 + *s - '0'; + } + if (endian == NETUTILS_LITTLE) { + out_ip[i] = val; + } else { + out_ip[NETUTILS_IPV4ADDR_BUFSIZE - 1 - i] = val; + } + if (i == 0 && s == s_top) { + return; + } else if (i > 0 && s < s_top && *s == '.') { + s++; + } else { + mp_raise_ValueError(MP_ERROR_TEXT("invalid arguments")); + } + } +} + +// Takes an address of the form ('192.168.0.1', 8080), returns the port and +// puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). +mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian) { + mp_obj_t *addr_items; + mp_obj_get_array_fixed_n(addr_in, 2, &addr_items); + netutils_parse_ipv4_addr(addr_items[0], out_ip, endian); + return mp_obj_get_int(addr_items[1]); +} diff --git a/shared/netutils/netutils.h b/shared/netutils/netutils.h new file mode 100644 index 0000000000000..f82960ba800bb --- /dev/null +++ b/shared/netutils/netutils.h @@ -0,0 +1,58 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * 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_LIB_NETUTILS_NETUTILS_H +#define MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H + +#include "py/obj.h" + +#define NETUTILS_IPV4ADDR_BUFSIZE 4 + +#define NETUTILS_TRACE_IS_TX (0x0001) +#define NETUTILS_TRACE_PAYLOAD (0x0002) +#define NETUTILS_TRACE_NEWLINE (0x0004) + +typedef enum _netutils_endian_t { + NETUTILS_LITTLE, + NETUTILS_BIG, +} netutils_endian_t; + +// Takes an array with a raw IPv4 address and returns something like '192.168.0.1'. +mp_obj_t netutils_format_ipv4_addr(uint8_t *ip, netutils_endian_t endian); + +// Takes an array with a raw IP address, and a port, and returns a net-address +// tuple such as ('192.168.0.1', 8080). +mp_obj_t netutils_format_inet_addr(uint8_t *ip, mp_uint_t port, netutils_endian_t endian); + +void netutils_parse_ipv4_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); + +// Takes an address of the form ('192.168.0.1', 8080), returns the port and +// puts IP in out_ip (which must take at least IPADDR_BUF_SIZE bytes). +mp_uint_t netutils_parse_inet_addr(mp_obj_t addr_in, uint8_t *out_ip, netutils_endian_t endian); + +void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags); + +#endif // MICROPY_INCLUDED_LIB_NETUTILS_NETUTILS_H diff --git a/shared/netutils/trace.c b/shared/netutils/trace.c new file mode 100644 index 0000000000000..24af4d5ca315f --- /dev/null +++ b/shared/netutils/trace.c @@ -0,0 +1,170 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" +#include "shared/netutils/netutils.h" + +static uint32_t get_be16(const uint8_t *buf) { + return buf[0] << 8 | buf[1]; +} + +static uint32_t get_be32(const uint8_t *buf) { + return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; +} + +static void dump_hex_bytes(const mp_print_t *print, size_t len, const uint8_t *buf) { + for (size_t i = 0; i < len; ++i) { + mp_printf(print, " %02x", buf[i]); + } +} + +static const char *ethertype_str(uint16_t type) { + // A value between 0x0000 - 0x05dc (inclusive) indicates a length, not type + switch (type) { + case 0x0800: + return "IPv4"; + case 0x0806: + return "ARP"; + case 0x86dd: + return "IPv6"; + default: + return NULL; + } +} + +void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags) { + mp_printf(print, "[% 8u] ETH%cX len=%u", (unsigned)mp_hal_ticks_ms(), flags & NETUTILS_TRACE_IS_TX ? 'T' : 'R', len); + mp_printf(print, " dst=%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + mp_printf(print, " src=%02x:%02x:%02x:%02x:%02x:%02x", buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]); + + const char *ethertype = ethertype_str(buf[12] << 8 | buf[13]); + if (ethertype) { + mp_printf(print, " type=%s", ethertype); + } else { + mp_printf(print, " type=0x%04x", buf[12] << 8 | buf[13]); + } + if (len > 14) { + len -= 14; + buf += 14; + if (buf[-2] == 0x08 && buf[-1] == 0x00 && buf[0] == 0x45) { + // IPv4 packet + len = get_be16(buf + 2); + mp_printf(print, " srcip=%u.%u.%u.%u dstip=%u.%u.%u.%u", + buf[12], buf[13], buf[14], buf[15], + buf[16], buf[17], buf[18], buf[19]); + uint8_t prot = buf[9]; + buf += 20; + len -= 20; + if (prot == 6) { + // TCP packet + uint16_t srcport = get_be16(buf); + uint16_t dstport = get_be16(buf + 2); + uint32_t seqnum = get_be32(buf + 4); + uint32_t acknum = get_be32(buf + 8); + uint16_t dataoff_flags = get_be16(buf + 12); + uint16_t winsz = get_be16(buf + 14); + mp_printf(print, " TCP srcport=%u dstport=%u seqnum=%u acknum=%u dataoff=%u flags=%x winsz=%u", + srcport, dstport, (unsigned)seqnum, (unsigned)acknum, dataoff_flags >> 12, dataoff_flags & 0x1ff, winsz); + buf += 20; + len -= 20; + if (dataoff_flags >> 12 > 5) { + mp_printf(print, " opts="); + size_t opts_len = ((dataoff_flags >> 12) - 5) * 4; + dump_hex_bytes(print, opts_len, buf); + buf += opts_len; + len -= opts_len; + } + } else if (prot == 17) { + // UDP packet + uint16_t srcport = get_be16(buf); + uint16_t dstport = get_be16(buf + 2); + mp_printf(print, " UDP srcport=%u dstport=%u", srcport, dstport); + len = get_be16(buf + 4); + buf += 8; + if ((srcport == 67 && dstport == 68) || (srcport == 68 && dstport == 67)) { + // DHCP + if (srcport == 67) { + mp_printf(print, " DHCPS"); + } else { + mp_printf(print, " DHCPC"); + } + dump_hex_bytes(print, 12 + 16 + 16 + 64, buf); + size_t n = 12 + 16 + 16 + 64 + 128; + len -= n; + buf += n; + mp_printf(print, " opts:"); + switch (buf[6]) { + case 1: + mp_printf(print, " DISCOVER"); + break; + case 2: + mp_printf(print, " OFFER"); + break; + case 3: + mp_printf(print, " REQUEST"); + break; + case 4: + mp_printf(print, " DECLINE"); + break; + case 5: + mp_printf(print, " ACK"); + break; + case 6: + mp_printf(print, " NACK"); + break; + case 7: + mp_printf(print, " RELEASE"); + break; + case 8: + mp_printf(print, " INFORM"); + break; + } + } + } else { + // Non-UDP packet + mp_printf(print, " prot=%u", prot); + } + } else if (buf[-2] == 0x86 && buf[-1] == 0xdd && (buf[0] >> 4) == 6) { + // IPv6 packet + uint32_t h = get_be32(buf); + uint16_t l = get_be16(buf + 4); + mp_printf(print, " tclass=%u flow=%u len=%u nexthdr=%u hoplimit=%u", (unsigned)((h >> 20) & 0xff), (unsigned)(h & 0xfffff), l, buf[6], buf[7]); + mp_printf(print, " srcip="); + dump_hex_bytes(print, 16, buf + 8); + mp_printf(print, " dstip="); + dump_hex_bytes(print, 16, buf + 24); + buf += 40; + len -= 40; + } + if (flags & NETUTILS_TRACE_PAYLOAD) { + mp_printf(print, " data="); + dump_hex_bytes(print, len, buf); + } + } + if (flags & NETUTILS_TRACE_NEWLINE) { + mp_printf(print, "\n"); + } +} diff --git a/tests/micropython/emg_exc.py.native.exp b/tests/micropython/emg_exc.py.native.exp index 9677c526a9cc8..879fa7ef5b943 100644 --- a/tests/micropython/emg_exc.py.native.exp +++ b/tests/micropython/emg_exc.py.native.exp @@ -1,2 +1 @@ -ValueError: 1 - +ValueError(1,) From 0882034015c259e6eda97fbeee65a378b297aac0 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 10 Apr 2026 16:28:26 -0400 Subject: [PATCH 507/517] fix compile option problems --- py/circuitpy_mpconfig.h | 2 +- shared/runtime/pyexec.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index c02f0b4f87084..b7b5288e8c5f7 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -46,7 +46,7 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_PY_BLUETOOTH (0) #define MICROPY_PY_LWIP_SLIP (0) #define MICROPY_PY_OS_DUPTERM (0) -#define MICROPY_PY_PYEXEC_COMPILE_ONLY (0) +#define MICROPY_PYEXEC_COMPILE_ONLY (0) #define MICROPY_ROM_TEXT_COMPRESSION (0) #define MICROPY_VFS_LFS1 (0) #define MICROPY_VFS_LFS2 (0) diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 430892a00bceb..bc5f89bdc81ad 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -615,7 +615,7 @@ MP_REGISTER_ROOT_POINTER(vstr_t * repl_line); #else // MICROPY_REPL_EVENT_DRIVEN // CIRCUITPY-CHANGE: avoid warnings -#if defined(MICROPY_HAL_HAS_STDIO_MODE_SWITCH) && !MICROPY_HAL_HAS_STDIO_MODE_SWITCH +#if !defined(MICROPY_HAL_HAS_STDIO_MODE_SWITCH) || !MICROPY_HAL_HAS_STDIO_MODE_SWITCH // If the port doesn't need any stdio mode switching calls then provide trivial ones. static inline void mp_hal_stdio_mode_raw(void) { } From e376e2416b84d7b06f3f74843682d1d74d13efd4 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Fri, 10 Apr 2026 15:52:45 -0500 Subject: [PATCH 508/517] refactor to remove common_hal_i2ctarget_i2c_target_is_stop(). Fix separate issue with read_byte returning early. --- .../common-hal/i2ctarget/I2CTarget.c | 38 +++++++++---------- .../common-hal/i2ctarget/I2CTarget.h | 2 - 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c index 655dfd867ca38..a40f6e26608a9 100644 --- a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c +++ b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c @@ -85,7 +85,12 @@ void common_hal_i2ctarget_i2c_target_deinit(i2ctarget_i2c_target_obj_t *self) { } int common_hal_i2ctarget_i2c_target_is_addressed(i2ctarget_i2c_target_obj_t *self, uint8_t *address, bool *is_read, bool *is_restart) { - common_hal_i2ctarget_i2c_target_is_stop(self); + // Interrupt bits must be cleared by software. Clear the STOP and + // RESTART interrupt bits after an I2C transaction finishes. + if (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) { + self->peripheral->hw->clr_stop_det; + self->peripheral->hw->clr_restart_det; + } if (!((self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) || (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS))) { return 0; @@ -100,12 +105,20 @@ int common_hal_i2ctarget_i2c_target_is_addressed(i2ctarget_i2c_target_obj_t *sel } int common_hal_i2ctarget_i2c_target_read_byte(i2ctarget_i2c_target_obj_t *self, uint8_t *data) { - if (self->peripheral->hw->status & I2C_IC_STATUS_RFNE_BITS) { - *data = (uint8_t)self->peripheral->hw->data_cmd; - return 1; - } else { - return 0; + // Wait for data to arrive or a STOP condition indicating the transfer is over. + // Without this wait, the CPU can drain the FIFO faster than bytes arrive on the + // I2C bus, causing transfers to be split into multiple partial reads. + for (int t = 0; t < 100; t++) { + if (self->peripheral->hw->status & I2C_IC_STATUS_RFNE_BITS) { + *data = (uint8_t)self->peripheral->hw->data_cmd; + return 1; + } + if (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) { + break; + } + mp_hal_delay_us(10); } + return 0; } int common_hal_i2ctarget_i2c_target_write_byte(i2ctarget_i2c_target_obj_t *self, uint8_t data) { @@ -125,19 +138,6 @@ int common_hal_i2ctarget_i2c_target_write_byte(i2ctarget_i2c_target_obj_t *self, } } -int common_hal_i2ctarget_i2c_target_is_stop(i2ctarget_i2c_target_obj_t *self) { - // Interrupt bits must be cleared by software. Clear the STOP and - // RESTART interrupt bits after an I2C transaction finishes. - if (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) { - self->peripheral->hw->clr_stop_det; - self->peripheral->hw->clr_restart_det; - return 1; - } else { - return 0; - } - -} - void common_hal_i2ctarget_i2c_target_ack(i2ctarget_i2c_target_obj_t *self, bool ack) { return; } diff --git a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h index 67b09c18c2f12..5d4e0690cbffd 100644 --- a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h +++ b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.h @@ -21,5 +21,3 @@ typedef struct { uint8_t scl_pin; uint8_t sda_pin; } i2ctarget_i2c_target_obj_t; - -int common_hal_i2ctarget_i2c_target_is_stop(i2ctarget_i2c_target_obj_t *self); From 6fc9542d0cc56a52cc87f80ac61b44454990e438 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 10 Apr 2026 18:02:10 -0400 Subject: [PATCH 509/517] mp_int_t and mp_uint_t now defined in mpconfig.h --- py/circuitpy_mpconfig.h | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index b7b5288e8c5f7..b56ffaf076af6 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -46,7 +46,7 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_PY_BLUETOOTH (0) #define MICROPY_PY_LWIP_SLIP (0) #define MICROPY_PY_OS_DUPTERM (0) -#define MICROPY_PYEXEC_COMPILE_ONLY (0) +#define MICROPY_PYEXEC_COMPILE_ONLY (0) #define MICROPY_ROM_TEXT_COMPRESSION (0) #define MICROPY_VFS_LFS1 (0) #define MICROPY_VFS_LFS2 (0) @@ -198,21 +198,6 @@ extern void common_hal_mcu_enable_interrupts(void); // Track stack usage. Expose results via ustack module. #define MICROPY_MAX_STACK_USAGE (0) -#define UINT_FMT "%u" -#define INT_FMT "%d" -#ifdef __LP64__ -typedef long mp_int_t; // must be pointer size -typedef unsigned long mp_uint_t; // must be pointer size -#else -// These are definitions for machines where sizeof(int) == sizeof(void*), -// regardless of actual size. -typedef int mp_int_t; // must be pointer size -typedef unsigned int mp_uint_t; // must be pointer size -#endif -#if __GNUC__ >= 10 // on recent gcc versions we can check that this is so -_Static_assert(sizeof(mp_int_t) == sizeof(void *)); -_Static_assert(sizeof(mp_uint_t) == sizeof(void *)); -#endif typedef long mp_off_t; #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) From 5b2e839d69b0b6b792c1ddaadf796a595032acf9 Mon Sep 17 00:00:00 2001 From: Bernhard Bablok Date: Sat, 11 Apr 2026 09:18:12 +0200 Subject: [PATCH 510/517] added documentation --- ports/raspberrypi/boards/pimoroni_badger2350/pins.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/pins.c b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c index ff9feb9cce464..1a66961e68fcf 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2350/pins.c +++ b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c @@ -122,7 +122,13 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].epaper_display)}, // button-state on reset + // Use `board.RESET_STATE()` to query the state of all buttons at reset. + // To detect individual key-presses, `board.ON_RESET_PRESSED()` + // is simpler. { MP_ROM_QSTR(MP_QSTR_RESET_STATE), (mp_obj_t)&get_reset_state_obj }, + + // Use `board.ON_RESET_PRESSED(board.SW_A)` to check if `SW_A` was pressed + // during reset. { MP_ROM_QSTR(MP_QSTR_ON_RESET_PRESSED), (mp_obj_t)&on_reset_pressed_obj }, }; From e234e622e82179cdd5e357bd935acedc8f299bbe Mon Sep 17 00:00:00 2001 From: foamyguy Date: Sun, 12 Apr 2026 13:14:33 -0500 Subject: [PATCH 511/517] enable hashlib and zlib in zephyr port and add tests for them --- .../autogen_board_info.toml | 4 +- .../autogen_board_info.toml | 4 +- .../native/native_sim/autogen_board_info.toml | 2 +- .../nrf5340bsim/autogen_board_info.toml | 4 +- .../nordic/nrf5340dk/autogen_board_info.toml | 4 +- .../nordic/nrf54h20dk/autogen_board_info.toml | 4 +- .../nordic/nrf54l15dk/autogen_board_info.toml | 4 +- .../nordic/nrf7002dk/autogen_board_info.toml | 2 +- .../nxp/frdm_mcxn947/autogen_board_info.toml | 4 +- .../nxp/frdm_rw612/autogen_board_info.toml | 2 +- .../mimxrt1170_evk/autogen_board_info.toml | 4 +- .../autogen_board_info.toml | 2 +- .../rpi_pico2_zephyr/autogen_board_info.toml | 4 +- .../rpi_pico_w_zephyr/autogen_board_info.toml | 2 +- .../rpi_pico_zephyr/autogen_board_info.toml | 4 +- .../da14695_dk_usb/autogen_board_info.toml | 4 +- .../renesas/ek_ra6m5/autogen_board_info.toml | 4 +- .../renesas/ek_ra8d1/autogen_board_info.toml | 4 +- .../nucleo_n657x0_q/autogen_board_info.toml | 4 +- .../nucleo_u575zi_q/autogen_board_info.toml | 4 +- .../st/stm32h750b_dk/autogen_board_info.toml | 4 +- .../st/stm32h7b3i_dk/autogen_board_info.toml | 4 +- .../stm32wba65i_dk1/autogen_board_info.toml | 4 +- .../zephyr-cp/cptools/build_circuitpython.py | 9 ++ ports/zephyr-cp/prj.conf | 5 + ports/zephyr-cp/tests/test_hashlib.py | 152 ++++++++++++++++++ ports/zephyr-cp/tests/test_zlib.py | 110 +++++++++++++ 27 files changed, 317 insertions(+), 41 deletions(-) create mode 100644 ports/zephyr-cp/tests/test_hashlib.py create mode 100644 ports/zephyr-cp/tests/test_zlib.py diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 17065b902923e..57e08082e9f04 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml index 3d7a2b29a3f50..8e64638dc5a76 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 5b8fcfd0deada..1bb931d29e451 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 9842ea3d88d9b..2ecfd08fb594f 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 065a708c50510..d2e690e3f0ad1 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index b8bad240351fa..9d320db9a99bd 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 22b92d49f49c9..9205f746e6a71 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 5faed268cdb7d..f59f5ea345b06 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -117,4 +117,4 @@ watchdog = false wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 6da4ebc66c5ce..d70cd56fcfd3b 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index 19b3b7cc0e108..045a7970018f5 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -117,4 +117,4 @@ watchdog = false wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index 702f5900eee79..a6d3e3a84623f 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml index 10ca2517b6193..65fd81ed76f74 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml @@ -117,4 +117,4 @@ watchdog = false wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml index 15b36b725af01..e4e52b59b93bc 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml index 1f01990b5f291..ca9200e81804f 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml @@ -117,4 +117,4 @@ watchdog = false wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml index c8624cdde6c53..ca05976aeb6bf 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index ea9ed4c2577a8..4d1b51291d02f 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index c28c03b2c8e30..8251b61be6e4a 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 1b0c652b7d562..ec1576e8d9b64 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index c05bfa1d17eca..e0d0b68c8b894 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index d6a0a27e0cd43..7675df91e0724 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml index 599ec4ec4d2b0..55571241c71af 100644 --- a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index c5505bee932a0..577b1681c69a9 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index 0fe013483482d..9296072a864c2 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -50,7 +50,7 @@ frequencyio = false getpass = false gifio = false gnss = false -hashlib = false +hashlib = true hostnetwork = false i2cdisplaybus = true # Zephyr board has busio i2cioexpander = false @@ -117,4 +117,4 @@ watchdog = false wifi = false zephyr_display = false zephyr_kernel = false -zlib = false +zlib = true diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 32173914a2a7a..2e9b120831e86 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -62,6 +62,8 @@ "math", "msgpack", "aesio", + "hashlib", + "zlib", ] # Flags that don't match with with a *bindings module. Some used by adafruit_requests MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math"] @@ -108,6 +110,13 @@ # No QSTR processing or CIRCUITPY specific flags LIBRARY_SOURCE = { "audiomp3": ["lib/mp3/src/*.c"], + "zlib": [ + "lib/uzlib/tinflate.c", + "lib/uzlib/tinfzlib.c", + "lib/uzlib/tinfgzip.c", + "lib/uzlib/adler32.c", + "lib/uzlib/crc32.c", + ], } SHARED_MODULE_AND_COMMON_HAL = ["_bleio", "os", "rotaryio"] diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index 308333922f3f6..893ff259a97c7 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -49,3 +49,8 @@ CONFIG_I2S=y CONFIG_DYNAMIC_THREAD=y CONFIG_DYNAMIC_THREAD_ALLOC=y CONFIG_DYNAMIC_THREAD_PREFER_ALLOC=y + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_SHA256=y diff --git a/ports/zephyr-cp/tests/test_hashlib.py b/ports/zephyr-cp/tests/test_hashlib.py new file mode 100644 index 0000000000000..94463ba1e991a --- /dev/null +++ b/ports/zephyr-cp/tests/test_hashlib.py @@ -0,0 +1,152 @@ +# SPDX-FileCopyrightText: 2026 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test the hashlib module against CPython hashlib.""" + +import hashlib + +import pytest + + +SHORT_DATA = b"CircuitPython!" +CHUNK_A = b"Hello, " +CHUNK_B = b"CircuitPython world!" +LONG_DATA = b"The quick brown fox jumps over the lazy dog." * 64 + + +SHA256_CODE = """\ +import hashlib + +h = hashlib.new("sha256", b"CircuitPython!") +print(f"digest_size: {h.digest_size}") +print(f"digest_hex: {h.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": SHA256_CODE}) +def test_hashlib_sha256_basic(circuitpython): + """sha256 digest on a small buffer matches CPython hashlib.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + expected = hashlib.sha256(SHORT_DATA).hexdigest() + assert "digest_size: 32" in output + assert f"digest_hex: {expected}" in output + assert "done" in output + + +SHA1_CODE = """\ +import hashlib + +h = hashlib.new("sha1", b"CircuitPython!") +print(f"digest_size: {h.digest_size}") +print(f"digest_hex: {h.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": SHA1_CODE}) +def test_hashlib_sha1_basic(circuitpython): + """sha1 digest on a small buffer matches CPython hashlib.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + expected = hashlib.sha1(SHORT_DATA).hexdigest() + assert "digest_size: 20" in output + assert f"digest_hex: {expected}" in output + assert "done" in output + + +UPDATE_CODE = """\ +import hashlib + +h = hashlib.new("sha256") +h.update(b"Hello, ") +h.update(b"CircuitPython world!") +print(f"chunked_hex: {h.digest().hex()}") + +h2 = hashlib.new("sha256", b"Hello, CircuitPython world!") +print(f"oneshot_hex: {h2.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": UPDATE_CODE}) +def test_hashlib_sha256_update_chunks(circuitpython): + """Multiple update() calls produce the same digest as a single buffer, and match CPython.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + expected = hashlib.sha256(CHUNK_A + CHUNK_B).hexdigest() + assert f"chunked_hex: {expected}" in output + assert f"oneshot_hex: {expected}" in output + assert "done" in output + + +LONG_CODE = """\ +import hashlib + +data = b"The quick brown fox jumps over the lazy dog." * 64 +h = hashlib.new("sha256", data) +print(f"sha256_hex: {h.digest().hex()}") + +h1 = hashlib.new("sha1", data) +print(f"sha1_hex: {h1.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": LONG_CODE}) +def test_hashlib_long_input(circuitpython): + """Digests of a multi-block input match CPython for both sha1 and sha256.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"sha256_hex: {hashlib.sha256(LONG_DATA).hexdigest()}" in output + assert f"sha1_hex: {hashlib.sha1(LONG_DATA).hexdigest()}" in output + assert "done" in output + + +EMPTY_CODE = """\ +import hashlib + +h = hashlib.new("sha256", b"") +print(f"empty256: {h.digest().hex()}") + +h = hashlib.new("sha1", b"") +print(f"empty1: {h.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": EMPTY_CODE}) +def test_hashlib_empty_input(circuitpython): + """Empty-input digests match CPython well-known values.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"empty256: {hashlib.sha256(b'').hexdigest()}" in output + assert f"empty1: {hashlib.sha1(b'').hexdigest()}" in output + assert "done" in output + + +BAD_ALGO_CODE = """\ +import hashlib + +try: + hashlib.new("md5", b"data") +except ValueError: + print("bad_algo: ValueError") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BAD_ALGO_CODE}) +def test_hashlib_unsupported_algorithm(circuitpython): + """Unsupported algorithm names raise ValueError.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "bad_algo: ValueError" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_zlib.py b/ports/zephyr-cp/tests/test_zlib.py new file mode 100644 index 0000000000000..bd89d8e1c257a --- /dev/null +++ b/ports/zephyr-cp/tests/test_zlib.py @@ -0,0 +1,110 @@ +# SPDX-FileCopyrightText: 2026 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test the zlib module against CPython zlib. + +CircuitPython's zlib only implements ``decompress``; these tests compress data +with CPython and verify the zephyr-cp port decodes it back to the same bytes. +""" + +import zlib + +import pytest + + +PLAIN_TEXT = b"CircuitPython running on Zephyr says hello!" +REPEATED_TEXT = b"The quick brown fox jumps over the lazy dog. " * 32 +BINARY_BLOB = bytes(range(256)) * 4 + + +def _make_code(compressed: bytes, wbits: int, expected_len: int) -> str: + return ( + "import zlib\n" + f"compressed = {compressed!r}\n" + f"out = zlib.decompress(compressed, {wbits})\n" + f'print(f"out_len: {{len(out)}}")\n' + f'print(f"expected_len: {expected_len}")\n' + 'print(f"out_hex: {out.hex()}")\n' + 'print("done")\n' + ) + + +ZLIB_COMPRESSED = zlib.compress(PLAIN_TEXT) +ZLIB_CODE = _make_code(ZLIB_COMPRESSED, wbits=15, expected_len=len(PLAIN_TEXT)) + + +@pytest.mark.circuitpy_drive({"code.py": ZLIB_CODE}) +def test_zlib_decompress_zlib_format(circuitpython): + """Data produced by CPython zlib.compress() round-trips through CircuitPython zlib.decompress().""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(PLAIN_TEXT)}" in output + assert f"out_hex: {PLAIN_TEXT.hex()}" in output + assert "done" in output + + +REPEATED_COMPRESSED = zlib.compress(REPEATED_TEXT) +REPEATED_CODE = _make_code(REPEATED_COMPRESSED, wbits=15, expected_len=len(REPEATED_TEXT)) + + +@pytest.mark.circuitpy_drive({"code.py": REPEATED_CODE}) +def test_zlib_decompress_repeated(circuitpython): + """Highly-compressible repeated input decompresses correctly (back-references).""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(REPEATED_TEXT)}" in output + # Repeated text has many back-references; shrinks a lot. + assert len(REPEATED_COMPRESSED) < len(REPEATED_TEXT) // 4 + assert f"out_hex: {REPEATED_TEXT.hex()}" in output + assert "done" in output + + +BINARY_COMPRESSED = zlib.compress(BINARY_BLOB) +BINARY_CODE = _make_code(BINARY_COMPRESSED, wbits=15, expected_len=len(BINARY_BLOB)) + + +@pytest.mark.circuitpy_drive({"code.py": BINARY_CODE}) +def test_zlib_decompress_binary_bytes(circuitpython): + """Decompression preserves every byte value (0x00-0xFF).""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(BINARY_BLOB)}" in output + assert f"out_hex: {BINARY_BLOB.hex()}" in output + assert "done" in output + + +# Raw DEFLATE stream (no zlib header/trailer). +_raw_compressor = zlib.compressobj(wbits=-15) +RAW_COMPRESSED = _raw_compressor.compress(PLAIN_TEXT) + _raw_compressor.flush() +RAW_CODE = _make_code(RAW_COMPRESSED, wbits=-15, expected_len=len(PLAIN_TEXT)) + + +@pytest.mark.circuitpy_drive({"code.py": RAW_CODE}) +def test_zlib_decompress_raw_deflate(circuitpython): + """Raw DEFLATE streams (wbits=-15) decompress to the original bytes.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(PLAIN_TEXT)}" in output + assert f"out_hex: {PLAIN_TEXT.hex()}" in output + assert "done" in output + + +# Gzip wrapper (wbits=31). +_gzip_compressor = zlib.compressobj(wbits=31) +GZIP_COMPRESSED = _gzip_compressor.compress(PLAIN_TEXT) + _gzip_compressor.flush() +GZIP_CODE = _make_code(GZIP_COMPRESSED, wbits=31, expected_len=len(PLAIN_TEXT)) + + +@pytest.mark.circuitpy_drive({"code.py": GZIP_CODE}) +def test_zlib_decompress_gzip_format(circuitpython): + """Gzip-wrapped streams (wbits=31) decompress to the original bytes.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(PLAIN_TEXT)}" in output + assert f"out_hex: {PLAIN_TEXT.hex()}" in output + assert "done" in output From b17cc70bd4ac0c14f9f9e6897b555e62334e482e Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 13 Apr 2026 17:05:20 -0400 Subject: [PATCH 512/517] turn on MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING all the time --- main.c | 2 +- py/circuitpy_mpconfig.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index ca0cef419dc79..ce058cc49c4b5 100644 --- a/main.c +++ b/main.c @@ -1149,7 +1149,7 @@ int __attribute__((used)) main(void) { } else { skip_repl = false; } - } else if (exit_code != 0) { + } else if (exit_code != PYEXEC_NORMAL_EXIT) { break; } diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index ceb4aef802640..4b23784c5416c 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -51,6 +51,9 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_VFS_LFS1 (0) #define MICROPY_VFS_LFS2 (0) +// Always turn on exit code handling +#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (1) + #ifndef MICROPY_GCREGS_SETJMP #define MICROPY_GCREGS_SETJMP (0) #endif From 6caff40439be3ac9d04b6c5c319b4efb92ee2338 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Mon, 13 Apr 2026 18:20:09 -0400 Subject: [PATCH 513/517] re-merge previous cast in py/malloc.c --- py/malloc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/py/malloc.c b/py/malloc.c index fa2878d5905dd..fc9795043a879 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -324,7 +324,8 @@ void m_tracked_free(void *ptr_in) { if (ptr_in == NULL) { return; } - m_tracked_node_t *node = (m_tracked_node_t *)((uint8_t *)ptr_in - sizeof(m_tracked_node_t)); + // CIRCUITPY-CHANGE: cast to avoid compiler warning + m_tracked_node_t *node = (m_tracked_node_t *)(void *)((uint8_t *)ptr_in - sizeof(m_tracked_node_t)); #if MICROPY_DEBUG_VERBOSE size_t data_bytes; #if MICROPY_TRACKED_ALLOC_STORE_SIZE From 0cc24073393acbdbb9f2b9e48f62a9cbb8106748 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 14 Apr 2026 10:54:15 -0500 Subject: [PATCH 514/517] enable adafruit_bus_device and add test --- .../autogen_board_info.toml | 2 +- .../native/native_sim/autogen_board_info.toml | 2 +- .../zephyr-cp/cptools/build_circuitpython.py | 5 +- .../tests/test_adafruit_bus_device.py | 236 ++++++++++++++++++ 4 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 ports/zephyr-cp/tests/test_adafruit_bus_device.py diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml index 3d7a2b29a3f50..501812161220a 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml index 5b8fcfd0deada..d5b253169b618 100644 --- a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 6c71ecd521c23..3e43293d91a18 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -62,6 +62,7 @@ "math", "msgpack", "aesio", + "adafruit_bus_device", ] # Flags that don't match with with a *bindings module. Some used by adafruit_requests MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math"] @@ -549,8 +550,8 @@ async def build_circuitpython(): hal_source.extend(top.glob(f"ports/zephyr-cp/common-hal/{module.name}/*.c")) # Only include shared-module/*.c if no common-hal/*.c files were found if len(hal_source) == len_before or module.name in SHARED_MODULE_AND_COMMON_HAL: - hal_source.extend(top.glob(f"shared-module/{module.name}/*.c")) - hal_source.extend(top.glob(f"shared-bindings/{module.name}/*.c")) + hal_source.extend(top.glob(f"shared-module/{module.name}/**/*.c")) + hal_source.extend(top.glob(f"shared-bindings/{module.name}/**/*.c")) if module.name in LIBRARY_SOURCE: for library_source in LIBRARY_SOURCE[module.name]: library_sources.extend(top.glob(library_source)) diff --git a/ports/zephyr-cp/tests/test_adafruit_bus_device.py b/ports/zephyr-cp/tests/test_adafruit_bus_device.py new file mode 100644 index 0000000000000..3022838506e77 --- /dev/null +++ b/ports/zephyr-cp/tests/test_adafruit_bus_device.py @@ -0,0 +1,236 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test adafruit_bus_device.i2c_device.I2CDevice against the AT24 EEPROM emulator.""" + +import pytest + + +PROBE_OK_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) +print(f"device created: {type(device).__name__}") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": PROBE_OK_CODE}) +def test_i2cdevice_probe_success(circuitpython): + """Constructing I2CDevice with probe=True succeeds when AT24 is present at 0x50.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "device created: I2CDevice" in output + assert "done" in output + + +PROBE_FAIL_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +try: + device = I2CDevice(i2c, 0x51) + print("unexpected_success") +except ValueError as e: + print(f"probe_failed: {e}") +except OSError as e: + print(f"probe_failed: {e}") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": PROBE_FAIL_CODE}) +def test_i2cdevice_probe_failure(circuitpython): + """Constructing I2CDevice with probe=True raises when no device responds.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "probe_failed" in output + assert "unexpected_success" not in output + assert "done" in output + + +PROBE_DISABLED_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +# probe=False should not raise even without a device at this address +device = I2CDevice(i2c, 0x51, probe=False) +print(f"device created: {type(device).__name__}") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": PROBE_DISABLED_CODE}) +def test_i2cdevice_probe_disabled(circuitpython): + """Constructing I2CDevice with probe=False succeeds regardless of device presence.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "device created: I2CDevice" in output + assert "done" in output + + +READ_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) + +# Read first byte by writing the memory address, then reading. +buf = bytearray(1) +with device: + device.write(bytes([0x00])) + device.readinto(buf) +print(f"AT24 byte 0: 0x{buf[0]:02X}") +if buf[0] == 0xFF: + print("eeprom_valid") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": READ_CODE}) +def test_i2cdevice_write_then_readinto_separate(circuitpython): + """write() followed by readinto() inside a single context manager block reads EEPROM data.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "AT24 byte 0: 0xFF" in output + assert "eeprom_valid" in output + assert "done" in output + + +WRITE_THEN_READINTO_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) + +out_buf = bytes([0x00]) +in_buf = bytearray(4) +with device: + device.write_then_readinto(out_buf, in_buf) +print(f"first 4 bytes: {[hex(b) for b in in_buf]}") +if all(b == 0xFF for b in in_buf): + print("eeprom_valid") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_THEN_READINTO_CODE}) +def test_i2cdevice_write_then_readinto_atomic(circuitpython): + """write_then_readinto() performs an atomic write+read against the EEPROM.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "first 4 bytes:" in output + assert "eeprom_valid" in output + assert "done" in output + + +WRITE_READBACK_CODE = """\ +import board +import time +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) + +# Write four bytes starting at EEPROM address 0x10. +payload = bytes([0xDE, 0xAD, 0xBE, 0xEF]) +with device: + device.write(bytes([0x10]) + payload) + +# EEPROM needs a moment to commit the write internally. +time.sleep(0.01) + +readback = bytearray(4) +with device: + device.write_then_readinto(bytes([0x10]), readback) +print(f"readback: {[hex(b) for b in readback]}") +if bytes(readback) == payload: + print("readback_ok") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_READBACK_CODE}) +def test_i2cdevice_write_then_read_roundtrip(circuitpython): + """Writing bytes to the EEPROM and reading them back returns the written values.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "readback_ok" in output + assert "done" in output + + +SLICE_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) + +# Use start/end kwargs to send only a slice of the outgoing buffer. +out = bytearray([0xAA, 0x00, 0xBB]) +dest = bytearray(4) +with device: + # Only send the middle byte (the memory address 0x00). + device.write_then_readinto(out, dest, out_start=1, out_end=2, in_start=0, in_end=2) +print(f"partial read: {[hex(b) for b in dest]}") +# Only the first two entries should have been written by the read. +if dest[0] == 0xFF and dest[1] == 0xFF and dest[2] == 0x00 and dest[3] == 0x00: + print("slice_ok") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": SLICE_CODE}) +def test_i2cdevice_buffer_slices(circuitpython): + """write_then_readinto honors out_start/out_end and in_start/in_end bounds.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "slice_ok" in output + assert "done" in output + + +DISABLED_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +try: + device = I2CDevice(i2c, 0x50) + print("unexpected_success") +except (ValueError, OSError) as e: + print(f"probe_failed: {e}") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": DISABLED_CODE}) +@pytest.mark.disable_i2c_devices("eeprom@50") +def test_i2cdevice_probe_fails_when_device_disabled(circuitpython): + """Probe fails when the AT24 emulator device is disabled on the bus.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "probe_failed" in output + assert "unexpected_success" not in output + assert "done" in output From f8d1c10e083c319ad313ec18504f2c60639805dd Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 14 Apr 2026 21:34:14 +0200 Subject: [PATCH 515/517] Update translation files Updated by "Update PO files to match POT (msgmerge)" hook in Weblate. Translation: CircuitPython/main Translate-URL: https://hosted.weblate.org/projects/circuitpython/main/ --- locale/cs.po | 68 +++++++++++++++++++++++++++----------------- locale/el.po | 68 +++++++++++++++++++++++++++----------------- locale/hi.po | 65 +++++++++++++++++++++++++----------------- locale/ko.po | 68 +++++++++++++++++++++++++++----------------- locale/ru.po | 80 +++++++++++++++++++++++++++++++++++----------------- locale/tr.po | 65 +++++++++++++++++++++++++----------------- 6 files changed, 258 insertions(+), 156 deletions(-) diff --git a/locale/cs.po b/locale/cs.po index 94a03f2e8b822..14eb245b9b813 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -89,6 +89,11 @@ msgid "" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" msgstr "%d adresní pin, %d rgb pin a %d dlaždice indikuje výšku %d, ne %d" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q a %q obsahují duplicitní piny" @@ -273,10 +278,6 @@ msgstr "%q je mimo hranice" msgid "%q out of range" msgstr "%q je mimo rozsah" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "%q krok nemůže být nula" @@ -981,6 +982,14 @@ msgstr "Při zpracování uvedené výjimky nastala další výjimka:" msgid "ECB only operates on 16 bytes at a time" msgstr "ECB operuje najednou pouze 16 bajtů" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c #: ports/espressif/common-hal/qspibus/QSPIBus.c @@ -2656,10 +2665,6 @@ msgstr "pole je příliš velké" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "přetečení v asm" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2873,6 +2878,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2971,14 +2980,14 @@ msgstr "nelze převést complex na dtype" msgid "cannot convert complex type" msgstr "nelze převést typ complex" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "nelze smazat prvky pole" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "nelze změnit rozměry pole" @@ -3133,7 +3142,7 @@ msgstr "dimenze nesouhlasí" msgid "div/mod not implemented for uint" msgstr "div/mod nejsou implementované pro uint" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "dělení nulou" @@ -3509,6 +3518,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3703,6 +3716,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3751,10 +3768,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3776,7 +3789,7 @@ msgstr "záporný faktoriál" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -4131,6 +4144,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4140,6 +4157,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4404,10 +4425,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "neočekávané odsazení" @@ -4429,10 +4446,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4601,6 +4614,9 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "asm overflow" +#~ msgstr "přetečení v asm" + #, c-format #~ msgid "Buffer + offset too small %d %d %d" #~ msgstr "Vyrovnávací paměť + offset je příliš malý %d %d %d" diff --git a/locale/el.po b/locale/el.po index 03a28bf863d77..f298c5c17e32f 100644 --- a/locale/el.po +++ b/locale/el.po @@ -93,6 +93,11 @@ msgid "" msgstr "" "%d pin διεύθυνσης, %d rgb ping και %d πλακίδια αναδεικνύουν ύψος %d, όχι %d" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q και %q περιέχουν διπλότυπα pins" @@ -277,10 +282,6 @@ msgstr "%q εκτός ορίων" msgid "%q out of range" msgstr "%q εκτός εμβέλειας" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "%q μεταονομάστηκε σε %q" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "%q βήμα δεν μπορεί να είναι μηδέν" @@ -989,6 +990,14 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "ECB δουλεύει μόνο σε 16 bytes κάθε φορά" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c #: ports/espressif/common-hal/qspibus/QSPIBus.c @@ -2655,10 +2664,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2872,6 +2877,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2970,14 +2979,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3132,7 +3141,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3508,6 +3517,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3702,6 +3715,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3750,10 +3767,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3775,7 +3788,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -4130,6 +4143,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4139,6 +4156,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4403,10 +4424,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4428,10 +4445,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4600,6 +4613,9 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "%q renamed %q" +#~ msgstr "%q μεταονομάστηκε σε %q" + #, c-format #~ msgid "Buffer + offset too small %d %d %d" #~ msgstr "Buffer + offset είναι πολύ μικρά %d %d %d" diff --git a/locale/hi.po b/locale/hi.po index 18e4eee167c38..f9d7b898198f6 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -80,6 +80,11 @@ msgid "" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" msgstr "" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "" @@ -264,10 +269,6 @@ msgstr "" msgid "%q out of range" msgstr "" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "" @@ -965,6 +966,14 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c #: ports/espressif/common-hal/qspibus/QSPIBus.c @@ -2629,10 +2638,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2846,6 +2851,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2944,14 +2953,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3106,7 +3115,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3482,6 +3491,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3676,6 +3689,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3724,10 +3741,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3749,7 +3762,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -4104,6 +4117,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4113,6 +4130,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4377,10 +4398,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4402,10 +4419,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" diff --git a/locale/ko.po b/locale/ko.po index 9f32555246864..9e570208d398a 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -91,6 +91,11 @@ msgid "" msgstr "" "%d 주소 핀들, %d rgb 핀들과 %d 타일 들은 높이가 %d임을 나타낸다, %d가 아니라" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q 및 %q에 중복된 핀이 포함" @@ -282,10 +287,6 @@ msgstr "%q가 경계를 벗어남" msgid "%q out of range" msgstr "%q가 범위를 벗어남" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "%q가 %q로 이름이 변경되었습니다" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c #, fuzzy msgid "%q step cannot be zero" @@ -1010,6 +1011,14 @@ msgstr "위 예외를 처리하는 동안, 또 다른 예외가 발생하였습 msgid "ECB only operates on 16 bytes at a time" msgstr "ECB는 한 번에 16 바이트에서만 작동합니다" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c #: ports/espressif/common-hal/qspibus/QSPIBus.c @@ -2703,10 +2712,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2920,6 +2925,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -3018,14 +3027,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3180,7 +3189,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3556,6 +3565,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3750,6 +3763,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3798,10 +3815,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3823,7 +3836,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -4178,6 +4191,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4187,6 +4204,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4451,10 +4472,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4476,10 +4493,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4648,6 +4661,9 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "%q renamed %q" +#~ msgstr "%q가 %q로 이름이 변경되었습니다" + #, c-format #~ msgid "Buffer + offset too small %d %d %d" #~ msgstr "Buffer + offset이 너무 작습니다 %d %d %d" diff --git a/locale/ru.po b/locale/ru.po index 8f222fb253449..0f7c6c0944b2f 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -93,6 +93,11 @@ msgstr "" "Адресные контакты %d, контакты rgb %d и плитки %d обозначают высоту %d, а не " "%d" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q и %q содержат пины дупликаты" @@ -277,10 +282,6 @@ msgstr "%q за пределом" msgid "%q out of range" msgstr "%q вне диапазона" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "%q переименован %q" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "Шаг %q не может быть нулём" @@ -992,6 +993,14 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "ECB работает только с 16 байтами за раз" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c #: ports/espressif/common-hal/qspibus/QSPIBus.c @@ -2695,10 +2704,6 @@ msgstr "массив слишком велик" msgid "array/bytes required on right side" msgstr "массив/байты, необходимые справа" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "Переполнение ASM" - #: py/compile.c msgid "async for/with outside async function" msgstr "async для/вместе с внешней async-функцией" @@ -2917,6 +2922,10 @@ msgstr "не может превратиться в полосу неявно" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "не может объявить нелокальный во внешнем коде" @@ -3019,14 +3028,14 @@ msgstr "не может превратить комплекс в dtype" msgid "cannot convert complex type" msgstr "Не удается преобразовать сложный тип" -#: py/objtype.c -msgid "cannot create instance" -msgstr "Не удается создать экземпляр" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "Не удается удалить элементы массива" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "Не удается изменить форму массива" @@ -3187,7 +3196,7 @@ msgstr "Размеры не совпадают" msgid "div/mod not implemented for uint" msgstr "div/mod не реализован для uint" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "Делим на ноль" @@ -3563,6 +3572,10 @@ msgstr "interp определен для 1D-итераций одинаково msgid "interval must be in range %s-%s" msgstr "Интервал должен находиться в диапазоне %s-%s" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "недействительная арка" @@ -3763,6 +3776,10 @@ msgstr "mktime нужен кортеж длины 8 или 9" msgid "mode must be complete, or reduced" msgstr "Режим должен быть завершенным или уменьшенным" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "модуль не найден" @@ -3811,10 +3828,6 @@ msgstr "слишком длинное имя" msgid "native code in .mpy unsupported" msgstr "Нативный код в .mpy не поддерживается" -#: py/asmthumb.c -msgid "native method too big" -msgstr "родной метод слишком большой" - #: py/emitnative.c msgid "native yield" msgstr "родной урожай" @@ -3836,7 +3849,7 @@ msgstr "отрицательный факториал" msgid "negative power with no float support" msgstr "Отрицательная мощность без поплавковой опоры" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "Количество отрицательных сдвигов" @@ -4193,6 +4206,10 @@ msgstr "Маски вытягивания конфликтуют с маскам msgid "real and imaginary parts must be of equal length" msgstr "реальные и воображаемые части должны быть одинаковой длины" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "Относительный импорт" @@ -4202,6 +4219,10 @@ msgstr "Относительный импорт" msgid "requested length %d but object has length %d" msgstr "запрашиваемая длина %d, но объект имеет длину %d" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "Результаты не могут быть приведены к указанному типу" @@ -4468,10 +4489,6 @@ msgstr "тип объекта '%q' не имеет атрибута '%q \"" msgid "type takes 1 or 3 arguments" msgstr "тип занимает 1 или 3 аргумента" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "голова длинная слишком большая" - #: py/parse.c msgid "unexpected indent" msgstr "Неожиданный отступ" @@ -4493,10 +4510,6 @@ msgstr "Экранирование имен в Юникоде" msgid "unindent doesn't match any outer indent level" msgstr "Отступ не совпадает ни с одним уровнем внешнего отступа" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4667,6 +4680,21 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" +#~ msgid "%q renamed %q" +#~ msgstr "%q переименован %q" + +#~ msgid "asm overflow" +#~ msgstr "Переполнение ASM" + +#~ msgid "cannot create instance" +#~ msgstr "Не удается создать экземпляр" + +#~ msgid "native method too big" +#~ msgstr "родной метод слишком большой" + +#~ msgid "ulonglong too large" +#~ msgstr "голова длинная слишком большая" + #~ msgid "font must be 2048 bytes long" #~ msgstr "Длина шрифта должна составлять 2048 байт" diff --git a/locale/tr.po b/locale/tr.po index c35a97623c600..1012dbf8b3937 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -91,6 +91,11 @@ msgstr "" "%d adres pinleri, %d RGB pinleri ve %d döşemeleri %d'nin yüksekliği " "gösterir, %d'nin değil" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q ve %q yinelenen pinler içeriyor" @@ -275,10 +280,6 @@ msgstr "%q sınırların dışında" msgid "%q out of range" msgstr "%q aralık dışında" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "%q sıfır olamaz" @@ -979,6 +980,14 @@ msgstr "Yukarıdaki hatanın işlenmesi sırasında başka bir hata oluştu:" msgid "ECB only operates on 16 bytes at a time" msgstr "ECB aynı anda yalnızca 16 baytla çalışır" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c #: ports/espressif/common-hal/qspibus/QSPIBus.c @@ -2651,10 +2660,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2868,6 +2873,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2966,14 +2975,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3128,7 +3137,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3504,6 +3513,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3698,6 +3711,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3746,10 +3763,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3771,7 +3784,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -4126,6 +4139,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4135,6 +4152,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4399,10 +4420,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4424,10 +4441,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" From 188358a2e8e8b906bd8dd125997ee71cbd1084b1 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 14 Apr 2026 16:47:43 -0500 Subject: [PATCH 516/517] build other zephyr autogen files --- .../feather_nrf52840_sense_zephyr/autogen_board_info.toml | 8 ++++---- .../feather_nrf52840_zephyr/autogen_board_info.toml | 2 +- .../boards/native/nrf5340bsim/autogen_board_info.toml | 2 +- .../boards/nordic/nrf5340dk/autogen_board_info.toml | 2 +- .../boards/nordic/nrf54h20dk/autogen_board_info.toml | 2 +- .../boards/nordic/nrf54l15dk/autogen_board_info.toml | 2 +- .../boards/nordic/nrf7002dk/autogen_board_info.toml | 2 +- .../boards/nxp/frdm_mcxn947/autogen_board_info.toml | 2 +- .../boards/nxp/frdm_rw612/autogen_board_info.toml | 2 +- .../boards/nxp/mimxrt1170_evk/autogen_board_info.toml | 2 +- .../rpi_pico2_w_zephyr/autogen_board_info.toml | 2 +- .../raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml | 2 +- .../raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml | 2 +- .../raspberrypi/rpi_pico_zephyr/autogen_board_info.toml | 2 +- .../boards/renesas/da14695_dk_usb/autogen_board_info.toml | 2 +- .../boards/renesas/ek_ra6m5/autogen_board_info.toml | 2 +- .../boards/renesas/ek_ra8d1/autogen_board_info.toml | 2 +- .../boards/st/nucleo_n657x0_q/autogen_board_info.toml | 2 +- .../boards/st/nucleo_u575zi_q/autogen_board_info.toml | 2 +- .../boards/st/stm32h750b_dk/autogen_board_info.toml | 2 +- .../boards/st/stm32h7b3i_dk/autogen_board_info.toml | 2 +- .../boards/st/stm32wba65i_dk1/autogen_board_info.toml | 2 +- 22 files changed, 25 insertions(+), 25 deletions(-) diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml index cfcae40090624..989332a143103 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml @@ -8,9 +8,9 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -63,7 +63,7 @@ keypad = false keypad_demux = false locale = false lvfontio = true # Zephyr board has busio -math = false +math = true max3421e = false mcp4822 = false mdns = false @@ -71,7 +71,7 @@ memorymap = false memorymonitor = false microcontroller = true mipidsi = false -msgpack = false +msgpack = true neopixel_write = false nvm = true # Zephyr board has nvm onewireio = false diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml index 805cd8c1f33b4..e0bc721297ac7 100644 --- a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml index 9842ea3d88d9b..d5998f7f3bd39 100644 --- a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 065a708c50510..875757375d2b3 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml index b8bad240351fa..eb00e58fe6be2 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 22b92d49f49c9..6debb1ab34078 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index 5faed268cdb7d..c5362b2cb7661 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = false alarm = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml index 6da4ebc66c5ce..a0e0b6cb51aed 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml index 19b3b7cc0e108..0ed7917934a36 100644 --- a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml index 702f5900eee79..efa46fcf0cb37 100644 --- a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml index 10ca2517b6193..a8f5e7682c7d4 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml index 15b36b725af01..5a2a5d831971b 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml index 1f01990b5f291..03e4c710690a6 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml index c8624cdde6c53..4353ce7f8c55b 100644 --- a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml index ea9ed4c2577a8..371a647c87730 100644 --- a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index c28c03b2c8e30..53df62bae8d46 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index 1b0c652b7d562..3bc076dfe09a3 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml index c05bfa1d17eca..553ef877ae7cf 100644 --- a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index d6a0a27e0cd43..e066ce2a9bcac 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml index 599ec4ec4d2b0..e53d7f9f1a035 100644 --- a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index c5505bee932a0..666408c6d7fda 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml index 0fe013483482d..d564f9786a08a 100644 --- a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false aesio = true alarm = false From eb9a3aceaa97d52e61d942d5685954a50a4c1ba1 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Tue, 14 Apr 2026 21:13:38 -0500 Subject: [PATCH 517/517] disable adafruit_bus_device and zlib on nrf7002dk_zephyr --- .../zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml | 4 ++-- ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index ca5c6562a9608..5faed268cdb7d 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -8,7 +8,7 @@ _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = true +adafruit_bus_device = false adafruit_pixelbuf = false aesio = false alarm = false @@ -117,4 +117,4 @@ watchdog = false wifi = true # Zephyr board has wifi zephyr_display = false zephyr_kernel = false -zlib = true +zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml index 7fcb145588bdb..191ed9d2d2c52 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml @@ -2,4 +2,4 @@ CIRCUITPY_BUILD_EXTENSIONS = ["elf"] USB_VID=0x239A USB_PID=0x8168 BLOBS=["nrf_wifi"] -DISABLED_MODULES=["aesio"] +DISABLED_MODULES=["aesio", "adafruit_bus_device", "zlib"]